当数据集很大时,有啥技巧可以避免或降低一对多连接和非等连接的成本?

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 连接。如果AB 这两个表的键足够重复,则两者之间的连接输出可以接近|A| 的大小。 * |B|。这在大公司的分析中必须经常出现,所以我想知道有什么方法可以减少这些连接的计算时间。

但是,很多时候AB 是不同的表,在这些情况下我认为不能使用LAG()

非 equi 的一对多连接示例

作为可能需要非 equi 和一对多连接的情况的简化示例,我有表 AB,每个表都有一个数字 id 列,一个日期字段date_created 和一些字段 group。对于表A 中的每一行,我想要Aid 列以及表B 中相应行的所有数据,其中B.date_created 是最大可能值,使得A.date_created > B.date_createdA.group = B.group。换句话说,我想要B 表中关于date_createdgroupA 中每一行的字段的最新行。

使用窗口函数时的代码

在出现这些非等连接的大多数用例中,AB 是同一个表,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.groupB.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() 用于您想要的列。

以上是关于当数据集很大时,有啥技巧可以避免或降低一对多连接和非等连接的成本?的主要内容,如果未能解决你的问题,请参考以下文章

oracle sql优化技巧

ACM题目如何避免超时?有啥技巧吗?

具有连接或多个结果集

使用 switch case 连接两个表以避免一对多连接

tcp和udp有啥区别

oracle 11g 和 12c 有啥区别?