SQL简单连接查询

时间:2016-03-30 20:27:49

标签: sql scala join apache-spark apache-spark-sql

我首先说我是SQL的新手,所以这个问题可能很简单。 我有两个带时间戳键的表。 对于t_i中的每个事件table 1,我希望q中的所有事件table 2都可以:

q.timeStamp < t_i.timeStamp and q.timeStamp > t_{i-1}.timeStamp

也就是说,如果事件按此顺序按时间戳发生:

    q1
t1  q2
    q3
    q4
t2  q5
    q6
t3  q7

然后生成的查询应为:

t1: q1
t2: q2 q3 q4
t3: q5 q6

我正在使用Scala和SQL Spark以及DataSet和DataFrame类,所以要么是一个纯粹的功能性的&group 39&quot;或者SQL查询会很好。

1 个答案:

答案 0 :(得分:1)

首先,它不是一个非常“简单”的查询...

首先 - 让我们用一些示例数据创建数据框 - 我创建了只有时间和字符串值的小案例类,您可以用更复杂的类替换它们:

case class A(time: Long, aValue: String)
case class B(time: Long, bValue: String)

val tableA = Seq(A(1, "q1"), A(2, "q2"), A(3, "q3"), A(4, "q4"), A(5, "q5"), A(6, "q6"), A(7, "q7"))
val tableB = Seq(B(2, "t1"), B(5, "t2"), B(7, "t3"))

val dfA: DataFrame = sqlContext.createDataFrame(tableA)
val dfB: DataFrame = sqlContext.createDataFrame(tableB)

现在 - 两个替代方案(概念上相同):

  1. 使用SQL

    dfA.registerTempTable("a")
    dfB.registerTempTable("b")
    
    sqlContext.sql(
      """
        |SELECT collect_list(c.time), collect_list(c.aValue), first(b.time), first(b.bValue)
        |FROM (
        |  SELECT FIRST(a.time) as time, FIRST(a.aValue) as aValue, MIN(b.time) AS bTime
        |  FROM a
        |  JOIN b ON b.time > a.time
        |  GROUP BY a.time) AS c
        |JOIN b ON c.bTime = b.time
        |GROUP BY b.time
      """.stripMargin).show()
    

    将为b(时间和b值)的每个值打印一个时间列表和一个值列表。

  2. 使用DataFrames

    import org.apache.spark.sql.functions._
    
    val aWithMinB: DataFrame = dfA
      .join(dfB, dfA("time") < dfB("time"))
      .groupBy(dfA("time"))
      .agg(first(dfA("aValue")), min(dfB("time")))
      .withColumnRenamed("FIRST(aValue)", "aValue")
      .withColumnRenamed("min(time)", "bTime")
    
    aWithMinB
      .join(dfB, dfB("time") === aWithMinB("bTime"))
      .groupBy(dfB("time"))
      .agg(collect_list(aWithMinB("time")), collect_list(aWithMinB("aValue")), first(dfB("time")), first(dfB("bValue")))
      .show()
    
  3. 请注意,两者都只适用于Spark 1.6.0或更高版本,因为早期版本中不存在collect_list

    更新:此流程的一些解释:

    • 第一个查询(SQL版本中的内部查询)旨在为表a中应分组的所有记录创建“公共值”成为结果中的单个记录
    • 这个共同的价值是什么? a中应分组的值是b中两个连续记录之间的值。因此,他们与更多b.time共享最小值,然后是他们的时间。换句话说 - 对于a中的每次X,我们在b中查找大于X的最小时间。这将是相同的值表示a中两个连续b s
    • 之间的所有记录
    • 要实现这一目标,我们将a加入b,条件为b.time > a.time(为b的每条记录获取a的许多记录,然后按a.time分组(将结果缩减回a中每条记录的一条记录),为每条此类记录和最小 b.time >每个a列的第一个值(取第一个值并不重要 - 所有分组记录对所有a列都具有相同的值!)
    • 现在我们为a中的每条记录提供了这个“额外信息”,我们将其加载到b列上的time和该列的分组。具有相同a的所有bTime条记录都会加入相应的b记录,我们就完成了:我们再次对所有first使用b s列(同样,所有分组记录的所有值都相同,因为我们将b的唯一标识符分组),并在collect_list列上使用a将所有值都设为一个清单。