当数据集很大时,有啥技巧可以避免或降低一对多连接和非等连接的成本?
Posted
技术标签:
【中文标题】当数据集很大时,有啥技巧可以避免或降低一对多连接和非等连接的成本?【英文标题】:Any tips and tricks to avoid or reduce cost of one-to-many joins and non-equi joins when dataset is large?当数据集很大时,有什么技巧可以避免或降低一对多连接和非等连接的成本? 【发布时间】:2019-01-09 00:04:10 【问题描述】:我想知道当人们拥有大量数据时,他们如何处理大型一对多连接,尤其是非 equi 连接。如果A
和B
这两个表的键足够重复,则两者之间的连接输出可以接近|A
| 的大小。 * |B
|。这在大公司的分析中必须经常出现,所以我想知道有什么方法可以减少这些连接的计算时间。
但是,很多时候A
和B
是不同的表,在这些情况下我认为不能使用LAG()
。
非 equi 的一对多连接示例
作为可能需要非 equi 和一对多连接的情况的简化示例,我有表 A
和 B
,每个表都有一个数字 id
列,一个日期字段date_created
和一些字段 group
。对于表A
中的每一行,我想要A
的id
列以及表B
中相应行的所有数据,其中B.date_created
是最大可能值,使得A.date_created
> B.date_created
和A.group = B.group
。换句话说,我想要B
表中关于date_created
和group
列A
中每一行的字段的最新行。
使用窗口函数时的代码
在出现这些非等连接的大多数用例中,A
和 B
是同一个表,date_created
字段实际上对应于同一列。在这种情况下,我可以使用LAG()
窗口函数:
WITH id_tuples AS
(
SELECT A.id,
LAG(A.id, 1) OVER (PARTITION BY A.group ORDER BY A.date_created) AS lagged_id
FROM A
)
SELECT id_t.id,
A.*
FROM id_tuples id_t
INNER JOIN A ON A.id = id_t.lagged_id
我认为这比自加入更有效。但是,当被比较的列不同或属于不同的表时,这种方法是不可能的。
窗口函数不可行时的代码
我使用以下代码为表A
中的每一行计算表B
的最新行。
SELECT *
FROM
(
SELECT A.id,
B.*,
DENSE_RANK() OVER (PARTITION BY A.id ORDER BY B.date_created) AS date_rank
FROM A
INNER JOIN B ON B.group = A.group
AND B.date_created < A.date_created
)
WHERE date_rank = 1
这里的问题是分组变量A.group
和B.group
只能有几个不同的值。然后连接就变成了一个笛卡尔连接,并且子查询中输出结果的数量可能比 A 和 B 的行的总和大很多数量级。这是浪费的,因为外部查询继续抛出大部分通过过滤 date_rank = 1
得到结果。
有没有更好的方法来构建查询以降低这些连接的成本,或者在这些情况下完全避免它们?我在抽象地问,但我发现我的关系数据库和我的 Spark 集群(一旦我将数据移动到那里)都没有足够的内存来处理这样的连接。即使在较小的数据集上,此操作也需要大量时间来运行。而且我不相信我的数据集相对于其他人正在做的事情来说特别大。
【问题讨论】:
同时标记postgresql(和database)和apache-spark 没有任何意义。这些是完全不同的工具,具有不同的功能、执行模型和性能考虑。 (任何)优化的第一步:摆脱 CTE。 【参考方案1】:您的第一个查询可以简单地写成:
SELECT A.id,
LAG(A.id, 1) OVER (PARTITION BY A.group ORDER BY A.date_created) AS lagged_id
FROM A;
不需要JOIN
。
对于第二个查询,一种方法是横向连接:
SELECT A.id, B.*,
FROM A LEFT JOIN LATERAL
(SELECT B.*
FROM B
WHERE B.group = A.group AND
B.date_created < A.date_created
ORDER BY B.date_created DESC
FETCH FIRST 1 ROW ONLY
) B;
这应该使用B(GROUP, date_created)
上的索引。
【讨论】:
如果我想要滞后 A 的相应列,我认为第一个查询需要连接 第二条评论看起来很棒;我会在一天左右的时间内给它答案,但我想看看其他人是否可以回答问题的其他更一般的部分 @EricHe 。 . .不需要。只需将lag()
用于您想要的列。以上是关于当数据集很大时,有啥技巧可以避免或降低一对多连接和非等连接的成本?的主要内容,如果未能解决你的问题,请参考以下文章