在 SQL 中,为啥这个 JOIN 会两次返回键列?
Posted
技术标签:
【中文标题】在 SQL 中,为啥这个 JOIN 会两次返回键列?【英文标题】:in SQL, why is this JOIN returning the key column twice?在 SQL 中,为什么这个 JOIN 会两次返回键列? 【发布时间】:2017-12-31 21:12:41 【问题描述】:如果这是一个愚蠢的问题,我很抱歉,但我似乎无法理解它。我对 SQL 很陌生,这种行为在 R 或 Pandas 或我习惯使用的其他东西中会很奇怪。
基本上,我在两个不同的数据库中有两个表,有一个公共键user_id
。我想加入所有列
SELECT * FROM db1.first_table t1
JOIN db2.second_table t2
ON t1.user_id = t2.user_id
很好,它有效。除了有两个(相同的)列称为user_id
。这并不重要,除非我在 pyspark 中执行此操作,并且当我尝试将连接的表导出到平面文件时,我收到一个错误,即其中两列具有相同的名称。有解决方法,但我只是想知道是否有人可以解释为什么连接返回 both user_id
列。看起来它是一个内部连接,所以根据定义,列是相同的。为什么会同时返回?
作为一个附带问题,有没有一种简单的方法可以避免这种行为?
提前致谢!
【问题讨论】:
因为“select *”正在返回两个表中的所有列。如果你需要限制,那么明确定义你想要的table.columns 因为您使用的是SELECT *
,这意味着它会返回连接中每个表的每一列
解决方法?使用select *
实际上不是很好的做法,您应该始终定义要检索的列。这不是一种解决方法——你应该这样做!
我唯一的问题是,这两个表实际上都有超过 1000 列。我可以把它们都列出来,但它会有点乏味,更不用说丑陋了。你能做 SELECT * EXCEPT user_id 吗?
【参考方案1】:
SELECT *
返回查询的所有表中的所有列。这包括两个 user_id
列 - 一个来自表 A,一个来自表 B。
最好的做法是列出您要专门返回的列名,但缩短列表的另一种选择是:
SELECT TableA.*,
TableB.col1,
TableB.col2,
...rest of B columns except user_id
【讨论】:
这不可能是最好的方法吗?在数据科学中,像提出这个问题的人一样,我经常有 1000 多列。祝你好运手动列出所有这些... @Thomas 在 SQL 中,这是唯一的方法。有很多方法可以生成列选择,而无需手动输入,但问题是,“在 SQL 中”是否有解决此行为的方法,但没有。 this answer 怎么样?USING
而不是 ON
似乎可以解决问题? (诚实的问题,我对 SQL 的了解不够深入,无法知道任何潜在的陷阱)
另见this excellent and highly upvoted answer
@Thomas 这些都是好点,只是USING
并非在所有 SQL 风格中都可用。鉴于pyspark
标签,我认为您的答案是正确的。【参考方案2】:
所有这些答案(除了 OP 自己写的答案)似乎都假设我们在非常小的表上进行操作,我们可以手动输入我们需要的每一列。
PySpark 中最简单的解决方案是使用 DataFrame 连接语法:
df = left_df.join(right_df, ["name"])
这不会复制列并且表现得像熊猫合并。如果没有特殊原因必须将其编写为 sql 命令,我会推荐这个。对比一下
df = left_df.join(right_df, left.name == right.name)
其行为类似于 SQL 连接并保留两列!
这也适用于 Scala 和 R,see here。
另一种解决方案是将第二个目标列重命名为“target_dataframe2”之类的名称,然后加入 sql,然后再次简单地删除“target_dataframe2”。
【讨论】:
谢谢,这很有帮助。 spark 命令是返回两个 user_id cols 还是更像 pandas 合并(即只保留一份连接键的副本)? 这取决于你如何使用它:如果你如上所述使用它(只是列名),它的行为就像熊猫。如果您像这样提及这两个列:left_df.name == right_df.name
,它会给您两个列。【参考方案3】:
您可以减少引用所需字段的字段数量。
现在你有
SELECT *
等于
SELECT t1.*, t2.*
也许你想要类似的东西
SELECT t1.*, t2.field1, t2.field2 ...
【讨论】:
【参考方案4】:这是因为您使用的是Select *
。当在SELECT
之后仅定义*
时,它将返回两个表中的所有列。您必须定义列名。始终定义要显示的列。你可以这样做:
SELECT t1.userid, t1.ColumnName1, t2.ColumnName2
FROM db1.first_table t1
INNER JOIN db2.second_table t2 ON t1.user_id = t2.user_id
*
可以通过以下方式使用:
以下查询将返回两个表中的所有列:
SELECT *
FROM db1.first_table t1
INNER JOIN db2.second_table t2 ON t1.user_id = t2.user_id
以下查询将返回 first_table 表中的所有列:
SELECT t1.*
FROM db1.first_table t1
INNER JOIN db2.second_table t2 ON t1.user_id = t2.user_id
以下查询将返回 Second_table 表中的所有列:
SELECT t2.*
FROM db1.first_table t1
INNER JOIN db2.second_table t2 ON t1.user_id = t2.user_id
此外,您可以通过这种方式从一个表中获取所有列,并从另一个表中获取某些列:
SELECT t1.*, t2.ColumnName
FROM db1.first_table t1
INNER JOIN db2.second_table t2 ON t1.user_id = t2.user_id
【讨论】:
【参考方案5】:好的,我想出了一种无需输入所有列名的方法(正如我在 cmets 中提到的,总共有大约 5k 列)。
这是特定于 pyspark 的,但我只是将列名导出到 csv 并加载它们并执行以下操作:
with open("t1_cols.csv") as data_file:
t1_cols = data_file.read().split('\n')
with open("t2_cols.csv") as data_file:
t2_cols = data_file.read().split('\n')
sql = 'SELECT t1.user_id, t1.' + ', t1.'.join(t1_cols) + \
', t2.' + ', t2.'.join(t2_cols) + ' ' + \
'FROM db1.first_table t1 JOIN db2.second_table t2 ON t1.user_id = t2.user_id'
df = sqlContext.sql(sql)
有点讨厌,但它确实有效。
另外,我接受了第一个答案,因为上述所有答案在技术上都是正确的,而且那是第一个。感谢您的帮助!
【讨论】:
【参考方案6】:如果您只想打印一列 user_id,那么您应该使用带有 USING
关键字的内连接。
当您将USING
关键字与列名一起使用时,它会从两个表中过滤掉该公共列并仅显示一个。但是,当您将ON
与条件t1.user_id = t2.user_id
一起使用时,这只是条件中使用了同名列的巧合。
ON
也用于比较两个表的不同列,因此它不会根据条件过滤掉列。所以,如果你想在加入后只显示一次公共列,那么你应该使用USING
关键字。
【讨论】:
以上是关于在 SQL 中,为啥这个 JOIN 会两次返回键列?的主要内容,如果未能解决你的问题,请参考以下文章