Hive 低效的嵌套连接
Hive inefficient nested joins
我有两个大的 Hive tables(十亿条记录),它们可以通过一个公共键(即事务 ID)连接起来。 table_a
包含所有交易 ID,table_b
有一些时间的交易。
理论上,table 中不应有重复的交易 ID。实际上,有一些重复项,虽然它们的数量相对较少,但 many:many 连接有可能导致问题。我正在基于此数据构建模型,作为第一步,忽略所有具有重复交易 ID 的记录可能更容易。
我写了一个丑陋的 Hive 查询,虽然它在逻辑上做了它需要做的事情,但效率低得可怕:
SELECT
table_a.someCol,
table_b.anotherCol,
[etc...]
FROM
(SELECT
table_a.*
FROM table_a
INNER JOIN
(SELECT
transaction_id
FROM table_a
GROUP BY transaction_id
HAVING COUNT(*) = 1) unique_transaction_ids
ON table_a.transaction_id = unique_transaction_ids.transaction_id) table_a_unique_transaction_ids_only
LEFT OUTER JOIN
(SELECT
table_b.*
FROM table_b
INNER JOIN
(SELECT
transaction_id
FROM table_b
GROUP BY transaction_id
HAVING COUNT(*) = 1) unique_transaction_ids
ON table_b.transaction_id = unique_transaction_ids.transaction_id) table_b_unique_transaction_ids_only
ON table_a_unique_transaction_ids_only.transaction_id = table_b_unique_transaction_ids_only.transaction_id;
工作原理:
首先,为两个 table 创建一个仅出现一次的交易 ID 列表:
SELECT
transaction_id
FROM table_?
GROUP BY transaction_id
HAVING COUNT(*) = 1
然后通过将唯一事务子查询内部连接到原始 tables 来过滤 tables。
最后,对过滤后的子查询进行左外连接。
有没有更有效的写法(例如使用ROW_NUMBER() OVER ...
等分析函数)?
如果你想 table_a
和 table_b
在一起,LEFT OUTER JOIN
似乎是不可避免的。可以避免这两个自连接。由于您希望 transaction_id
只出现一次,因此您可以在其余列上使用 MAX()
或 MIN()
而不会丢失信息(即使它们不是数字列)。像
select transaction_id
, max(col1) col1
, max(col2) col2
.
.
.
, max(coln) coln
from table_a
group by transaction_id
having count(transaction_id) = 1
这是一种 "pull columns through" 到下一个 "level" 而不必 group by
他们的方法。如果你有很多列,写起来可能会很乏味,但通常值得避免两次自连接。
我有两个大的 Hive tables(十亿条记录),它们可以通过一个公共键(即事务 ID)连接起来。 table_a
包含所有交易 ID,table_b
有一些时间的交易。
理论上,table 中不应有重复的交易 ID。实际上,有一些重复项,虽然它们的数量相对较少,但 many:many 连接有可能导致问题。我正在基于此数据构建模型,作为第一步,忽略所有具有重复交易 ID 的记录可能更容易。
我写了一个丑陋的 Hive 查询,虽然它在逻辑上做了它需要做的事情,但效率低得可怕:
SELECT
table_a.someCol,
table_b.anotherCol,
[etc...]
FROM
(SELECT
table_a.*
FROM table_a
INNER JOIN
(SELECT
transaction_id
FROM table_a
GROUP BY transaction_id
HAVING COUNT(*) = 1) unique_transaction_ids
ON table_a.transaction_id = unique_transaction_ids.transaction_id) table_a_unique_transaction_ids_only
LEFT OUTER JOIN
(SELECT
table_b.*
FROM table_b
INNER JOIN
(SELECT
transaction_id
FROM table_b
GROUP BY transaction_id
HAVING COUNT(*) = 1) unique_transaction_ids
ON table_b.transaction_id = unique_transaction_ids.transaction_id) table_b_unique_transaction_ids_only
ON table_a_unique_transaction_ids_only.transaction_id = table_b_unique_transaction_ids_only.transaction_id;
工作原理:
首先,为两个 table 创建一个仅出现一次的交易 ID 列表:
SELECT
transaction_id
FROM table_?
GROUP BY transaction_id
HAVING COUNT(*) = 1
然后通过将唯一事务子查询内部连接到原始 tables 来过滤 tables。
最后,对过滤后的子查询进行左外连接。
有没有更有效的写法(例如使用ROW_NUMBER() OVER ...
等分析函数)?
如果你想 table_a
和 table_b
在一起,LEFT OUTER JOIN
似乎是不可避免的。可以避免这两个自连接。由于您希望 transaction_id
只出现一次,因此您可以在其余列上使用 MAX()
或 MIN()
而不会丢失信息(即使它们不是数字列)。像
select transaction_id
, max(col1) col1
, max(col2) col2
.
.
.
, max(coln) coln
from table_a
group by transaction_id
having count(transaction_id) = 1
这是一种 "pull columns through" 到下一个 "level" 而不必 group by
他们的方法。如果你有很多列,写起来可能会很乏味,但通常值得避免两次自连接。