PySpark两次Left Join结果异常求助:多表关联不符合预期
问题描述
我有三个数据集:
Table 1
| day | spend | id |
|---|---|---|
| 2023-02-02 | 2.4 | 1 |
Table 2
| day | name | id |
|---|---|---|
| 2023-02-03 | Apple | 1 |
| 2023-02-06 | Banana | 2 |
Table 3
| prev_day | prev_name | prev_id |
|---|---|---|
| 2023-02-02 | Apple | 1 |
| 2023-02-05 | Banana | 2 |
执行两次Left Join的PySpark代码:
table1 .join(table2, table1("id") === table2("id") && table1("day") === table2("day"), "left") .join(table3, table1("id") === table3("prev_id") && table1("day") === table3("prev_day"), "left") .select(table1("day"), table1("spend"), table1("id"), table2("name").as("name1"), table3("prev_name").as("name2"))
得到结果:
| day | spend | id | name1 | name2 |
|---|---|---|---|---|
| 2023-02-02 | 2.4 | 1 | null | null |
但只保留与Table3的Left Join时:
table1 .join(table3, table1("id") === table3("prev_id") && table1("day") === table3("prev_day"), "left") .select(table1("day"), table1("spend"), table1("id"), table3("prev_name").as("name2"))
结果符合预期:
| day | spend | id | name2 |
|---|---|---|---|
| 2023-02-02 | 2.4 | 1 | Apple |
请问这种异常的原因是什么?该如何解决?
原因分析
问题核心在于Join后的列名歧义导致条件解析错误:
- 第一次Left Join(table1与table2)时,table1的
id=1、day=2023-02-02在table2中无匹配项,因此table2的所有列(包括id)都会被置为null。 - 第二次Join时,虽然代码写的是
table1("id") === table3("prev_id"),但此时数据集中存在两个id列(分别来自table1和table2),PySpark解析时可能误将table1("id")指向了table2的id(值为null),导致Join条件不成立,最终table3的列全部返回null。
解决方法
方式1:给表设置别名,明确列归属
通过别名消除列名歧义,确保Join条件引用的是正确表的列:
from pyspark.sql.functions import col # 给每个表设置别名 t1 = table1.alias("t1") t2 = table2.alias("t2") t3 = table3.alias("t3") result = t1.join(t2, (col("t1.id") == col("t2.id")) & (col("t1.day") == col("t2.day")), "left") .join(t3, (col("t1.id") == col("t3.prev_id")) & (col("t1.day") == col("t3.prev_day")), "left") .select(col("t1.day"), col("t1.spend"), col("t1.id"), col("t2.name").alias("name1"), col("t3.prev_name").alias("name2"))
方式2:Join后清理重复列
第一次Join后删除table2的重复列(id、day),避免后续解析混淆:
result = table1.join(table2, ["id", "day"], "left") # 用数组形式指定连接键,PySpark会自动保留一份id和day列 .join(table3, (col("id") == col("prev_id")) & (col("day") == col("prev_day")), "left") .select(col("day"), col("spend"), col("id"), col("name").alias("name1"), col("prev_name").alias("name2"))
或者手动删除重复列:
result = table1.join(table2, table1("id") == table2("id") & table1("day") == table2("day"), "left") .drop(table2["id"], table2["day"]) .join(table3, table1("id") == table3("prev_id") & table1("day") == table3("prev_day"), "left") .select(table1("day"), table1("spend"), table1("id"), table2("name").alias("name1"), table3("prev_name").alias("name2"))
内容的提问来源于stack exchange,提问作者Yiiiii
相关产品推荐
相关产品推荐

