如何在jdbc数据源中使用子查询来获取dbtable选项?

时间:2017-04-02 23:59:16

标签: mysql apache-spark jdbc apache-spark-sql pyspark-sql

我想使用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 documentation(我使用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'子句,切换案例等),没有任何运气。那么正确的语法是什么?我在哪里可以找到更详细的语法文档?此外,这在哪里奇怪" WHERE 1 = 0"在错误消息来自?谢谢!

4 个答案:

答案 0 :(得分:3)

要在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修改它。

答案 1 :(得分:2)

我认为它可能是Spark SQL中的一个错误。

似乎thisthis line会给您错误。两者都使用Scala字符串插值将table替换为dbtable

s"SELECT * FROM $table WHERE 1=0"

您可以从上述模式中遇到的错误中找到table1 WHERE 1=0的位置:

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

看起来不正确。

确实有一种特定于MySQL的方言 - MySQLDialect - 用its own覆盖getTableExistsQuery

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

所以我敢打赌,另一种方法getSchemaQuery是错误的来源。鉴于您使用Spark 1.6.3而该方法具有@Since("2.1.0")标记,这种情况极不可能发生。

我强烈建议您查看MySQL数据库的日志,看看执行了哪些查询会导致错误消息。

答案 2 :(得分:2)

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 :(得分:1)

通过Python上的Spark 2.2连接到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)