web-dev-qa-db-ja.com

jdbcデータソースでdbtableオプションのサブクエリを使用するにはどうすればよいですか?

Sparkを使用してJDBCソースからのデータを処理したいのですが、まず、JDBCから元のテーブルを読み取る代わりに、JDBC側でいくつかのクエリを実行して列をフィルタリングします。テーブルを結合し、クエリ結果をSpark SQLのテーブルとしてロードします。

生のJDBCテーブルをロードするための次の構文が機能します。

df_table1 = sqlContext.read.format('jdbc').options(
    url="jdbc:mysql://foo.com:3306",
    dbtable="mydb.table1",
    user="me",
    password="******",
    driver="com.mysql.jdbc.Driver" # mysql JDBC driver 5.1.41
).load() 
df_table1.show() # succeeded

Spark ドキュメント (私はPySpark 1.6.3を使用しています)によると:

dbtable:読み取る必要のあるJDBCテーブル。 SQLクエリのFROM句で有効なものはすべて使用できることに注意してください。たとえば、完全なテーブルの代わりに、括弧内にサブクエリを使用することもできます。

実験のために、次のような簡単なことを試しました。

df_table1 = sqlContext.read.format('jdbc').options(
    url="jdbc:mysql://foo.com:3306",
    dbtable="(SELECT * FROM mydb.table1) AS table1",
    user="me",
    password="******",
    driver="com.mysql.jdbc.Driver"
).load() # failed

次の例外が発生しました。

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'table1 WHERE 1=0' at line 1

また、構文の他のいくつかのバリエーション(括弧の追加/削除、 'as'句の削除、caseの切り替えなど)も試してみましたが、うまくいきませんでした。では、正しい構文は何でしょうか?構文の詳細なドキュメントはどこにありますか?その上、エラーメッセージのこの奇妙な「WHERE1 = 0」はどこから来たのですか?ありがとう!

9
Dichen

Spark SQLでSQLクエリを使用してJDBCソースからデータを読み取るには、次のような方法を試すことができます。

val df_table1 = sqlContext.read.format("jdbc").options(Map(
    ("url" -> "jdbc:postgresql://localhost:5432/mydb"),
    ("dbtable" -> "(select * from table1) as table1"),
    ("user" -> "me"),
    ("password" -> "******"),
    ("driver" -> "org.postgresql.Driver"))
).load()

PostgreSQLを使ってみました。 MySQLに従って変更できます。

3
himanshuIIITian

Spark 2.2 on Python MySQL(5.7.19)に接続すると、table="(SELECT * FROM a_table) AS my_table"を使用すると次のように実行できます。 。

from pyspark.sql import SparkSession

my_spark = SparkSession \
    .builder.appName("myApp") \
    .config("jars", "/usr/local/spark-2.2.2-bin-hadoop2.7/jars/mysql-connector-Java-5.1.45-bin.jar") \
    .getOrCreate()

my_df = my_spark.read.jdbc(
    url="jdbc:mysql://my_Host:3306/my_db",
    table="(SELECT * FROM a_table) AS my_table",
    properties={'user': 'my_username', 'password': 'my_password'}
)

my_df.head(20)
3
Will
table = "(SELECT id, person, manager, CAST(tdate AS CHAR) AS tdate, CAST(start AS   CHAR) AS start, CAST(end AS CHAR) as end, CAST(duration AS CHAR) AS duration FROM EmployeeTimes) AS EmployeeTimes",

spark = get_spark_session()
df = spark.read.format("jdbc"). \
    options(url=ip,
            driver='com.mysql.jdbc.Driver',
            dbtable=table,
            user=username,
            password=password).load()
return df

Spark JDBCとMYSQLタイムスタンプとの非互換性に多くの問題がありました。トリックは、JDBCがそれらに触れる前に、すべてのタイムスタンプまたは期間の値を文字列に変換することです。値を文字列としてキャストするだけです。そしてそれは動作します。

注:クエリを機能させるには、ASを使用してクエリにエイリアスを指定する必要もあります。

3
Zack

Spark SQLのバグかもしれません。

this または this line のいずれかでエラーが発生するようです。どちらもScala文字列補間を使用して、tabledbtableに置き換えます。

_s"SELECT * FROM $table WHERE 1=0"
_

ここで、上記のパターンが次のようになるために直面​​したエラーから_table1 WHERE 1=0_を見つけることができます。

_SELECT * FROM (select * from table1) as table1 WHERE 1=0
_

どの見た目が正しくありません。

実際、MySQL固有の方言があります MySQLDialect -getTableExistsQuery独自の でオーバーライドします:

_override def getTableExistsQuery(table: String): String = {
  s"SELECT 1 FROM $table LIMIT 1"
}
_

だから私の賭けは、他のメソッド getSchemaQuery がエラーの原因であるということです。メソッドに@Since("2.1.0")マーカーがあるときに、Spark 1.6.3を使用することを考えると、これは難しいことではありません。

MySQLデータベースのログをチェックして、エラーメッセージにつながるクエリが実行されているかどうかを確認することを強くお勧めします。

2
Jacek Laskowski