【问题标题】:Most efficient way to use aggregate functions in table join?在表连接中使用聚合函数的最有效方法?
【发布时间】:2017-12-30 11:04:57
【问题描述】:

我正在尝试连接两个表,其中表一中的所有记录都有唯一的 id,而表二 可以包含表一中的 id 的多条记录(表一中的 id 可能没有任何表二中的对应记录)。

我只想加入表 2 中的最新记录。

SELECT id, MAX(date) AS DATE FROM table_two

Above 返回特定 id 的最新记录。

SELECT * FROM table_one t1 LEFT JOIN table_two t2 ON t1.id = t2.id

Above 返回连接的表,但也返回重复的表。

如何构造连接语句以仅返回表 2 中的最新记录?

类似这样的:

SELECT * FROM table_one t1 LEFT JOIN table_two t2 ON t1.id = t2.id, MAX(t2.date) AS date GROUP BY date 

以上返回错误:ERROR: aggregate functions are not allowed in functions in FROM

我知道我可以使用子查询来获取最新记录,但不确定最有效的方法。

【问题讨论】:

  • 什么版本的postgresql? (确定是否横向是一个选项,或者是否应该使用 row_Number 方法,或者我们是否只需要子查询。
  • @xQbert 版本为 9.5.7

标签: postgresql left-join aggregate-functions


【解决方案1】:

你必须使用子查询。 这里有两个可能的答案:

使用 row_number 和子查询

select * from(
    SELECT t1.*,row_number() over(partition by t2.id order by t2.date desc) as row_num
    FROM table_one t1
    LEFT JOIN table_two t2 ON t1.id = t2.id
)t
where t.row_num = 1

使用最大和子查询

SELECT * FROM table_one t1 
LEFT JOIN 
(
    SELECT id, MAX(date) AS DATE FROM table_two group by id
)t2
ON t1.id = t2.id

【讨论】:

  • 这里的第二种方法没有从t2获取所有的列信息;只是最大日期。可能需要进行自连接,以便可以返回所有列。
  • @sagar 第一个查询似乎没有从 t1 返回所有信息,第二个查询需要 group by 才能使用聚合函数?
  • 是的,正确的@DonPeat,在第二个查询中需要按 id 分组。对于第一个查询 (t1.*) 应该返回所有列
【解决方案2】:

这两个都未经测试;如果设置了 rextester.com 测试用例,我会让它工作。

PostgreSQL 9.2?而且,我认为,使用 lateral 关键字实现外部应用。这基本上是针对表 1 运行选择,然后针对表 1 中的每条记录运行表 2 中的选择;从表 2 返回相应 ID 的最新日期记录;但不必在循环中这样做,引擎可以以某种方式将连接处理为“SET”,因此不会受到循环/逐行性能影响。

SELECT * 
FROM table_one t1 
LEFT JOIN LATERAL (SELECT A.*
                   FROM table_two A
                   WHERE A.ID = T1.ID
                   ORDER BY A.Date desc
                   LIMIT 1) t2 
  ON TRUE

Grouped LIMIT in PostgreSQL: show the first N rows for each group? 用于其他示例。

--应该在许多以前的版本上工作,因为它不使用分析函数或横向。

SELECT T1.*, t2.*
FROM table_one t1 
LEFT JOIN (SELECT A.* 
           FROM table_two A
           INNER JOIN (SELECT MAX(date) AS date, ID 
                       FROM table_two
                       GROUP BY ID) B
              on B.ID = A.ID
             and B.Date = A.Date) t2
 ON t1.id = t2.id 

上述基本概念...获取表 2 中记录的最大日期和 ID 将此连接回 table2 的基本集,以获取 table2 中具有每个 ID 的最大日期的记录的所有详细信息。然后加入表 1 以获取 t1 和 t2 值。

【讨论】:

  • 两个查询都有效,我之前没用过lateral。使用一个比另一个(与更简化的子查询相比)有显着的性能优势吗?
  • Lateral 是较新的语法,应该更高效,因为它不必生成子查询结果,从而允许引擎生成更优化的执行计划。我建议为两者运行解释计划并分析结果。我认为您会看到横向更有效;但这始终取决于基于成本的优化器 (CBO) 使用的索引、硬件和数据量(表统计信息)。
  • 换一种说法,10 年前你不得不使用第二个查询。这是您可以在 Pure SQL 中做您想做的事情的唯一方法。 5 年前和今天,您可以使用 row_number 逻辑来获取每条记录的最早日期,然后加入.. 今天,当您有 1-N 关系时,使用 LATERAL(其他引擎中的 CROSS/OUTER APPLY)返回这些情况并希望从 N 侧返回 x 行。 DB 制造商认为这种 CROSS Apply 方法有利于从内部处理 x 行的这些 1-N。如果他们建造了它,它必须等于或优于以前所做的。
  • 是的,在此处提出的方法上运行解释具有压倒性优势。感谢您的信息!
  • @DonPeat 注意到 sagar 在横向之前使用 row_number,它最有效的。这是我懒得拼出来的第三种方式,因为他们已经有。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-09
  • 2018-08-06
  • 2017-08-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多