Spark SQL:来自另一个SELECT语句的列(二进制搜索树)

时间:2017-12-20 10:45:36

标签: apache-spark pyspark apache-spark-sql spark-dataframe pyspark-sql

我正在尝试使用SparkSQL构建BST,这可以使用主SELECT语句中的另一个SELECT语句轻松完成,但SparkSQL不支持SELECT中的SELECT作为列。

BST的图形表示如下:

Binary Search Tree

输入是BST的行列表示.Goal是使用输入表数据生成输出表。

  • root:没有父节点

  • 内部:拥有父节点以及子节点

  • leaf:只有父节点,没有子节点

Data

这可以通过select中的select轻松实现,SQL下面应该这样做:

SELECT t.node,
    CASE
        WHEN t.parent IS NULL THEN 'root'
        WHEN EXISTS (SELECT t1.parent FROM bst t1 WHERE t1.parent = t.node) THEN 'inner'        
        ELSE 'leaf'
    END
FROM bst t

由于SparkSQL没有上述功能,我不得不解决这个问题。

spark.sql("""SELECT node, 'inner' AS desc
FROM bst t
WHERE EXISTS (SELECT 1
                        FROM bst t1
                        WHERE t1.parent=t.node)
            AND parent IS NOT NULL

UNION ALL

SELECT node,'leaf' AS desc
FROM bst t
WHERE NOT EXISTS (SELECT 1
                        FROM bst t1
                        WHERE t1.parent=t.node)
            AND parent IS NOT NULL          

UNION ALL

SELECT node,'root' AS desc
FROM bst t
WHERE parent IS NULL""").show()

快速创建虚拟数据的命令

data = \
  [(1, 2),
  (3, 2),
  (6, 8),
  (9, 8),
  (2, 5),
  (8, 5),
  (5, None)]
df = spark.createDataFrame(data, ["node", "parent"])
df.createOrReplaceTempView ("bst")

我使用的是Spark 2.1版,还有其他更优化的方法吗?

2 个答案:

答案 0 :(得分:1)

你需要进行自我加入,但你可以在一个语句中做到这一点(对于带有联合的两个语句)。

这是一次尝试:

select distinct 
  case 
    when n.parent is null then 'root' 
    when child.node is null then 'leaf'
    else 'inner' end
  ,n.node 
from bst n 
left outer join bst child
on n.node = child.parent
;

答案 1 :(得分:0)

这是使用 SparkSQL 的一种方法

df.withColumn("TYPE",functions \
.when(functions.col("parent").isNull(),"root") \
.when(functions.col("node")\
.isin(list(df.select(functions.col("parent")).distinct().rdd.map(lambda x:x[0]).collect())),"inner") \
.otherwise("leaf"))\
.drop("parent")\
.show()

+----+-----+
|node| TYPE|
+----+-----+
|   1| leaf|
|   3| leaf|
|   6| leaf|
|   9| leaf|
|   2|inner|
|   8|inner|
|   5| root|
+----+-----+

如果可能的话,你能帮我优化一下吗