JOIN 后的 GROUP 或 DISTINCT 返回重复项

Rai*_*eru 5 sql postgresql join group-by distinct

我有两张桌子,productsmeta。它们是 1:N 关系,其中每个产品行通过外键至少有一个元行。

(即 SQLfiddle:http ://sqlfiddle.com/#!15/c8f34/1 )

我需要加入这两个表,但我只需要过滤独特的产品。当我尝试此查询时,一切正常(返回 4 行):

SELECT DISTINCT(product_id)
FROM meta JOIN products ON products.id = meta.product_id
Run Code Online (Sandbox Code Playgroud)

但是当我尝试选择所有列时,DISTINCT 规则不再适用于结果,因为返回的是 8 行而不是 4 行。

SELECT DISTINCT(product_id), *
FROM meta JOIN products ON products.id = meta.product_id
Run Code Online (Sandbox Code Playgroud)

我尝试了很多方法,比如尝试DISTINCTGROUP BY子查询,但总是得到相同的结果。

Erw*_*ter 6

虽然检索全部或大部分行从表中,对于这种类型的查询最快的方法通常是聚合/歧义第一和加入

SELECT *
FROM   products p
JOIN  (
   SELECT DISTINCT ON (product_id) *
   FROM   meta
   ORDER  BY product_id, id DESC
   ) m ON m.product_id = p.id;
Run Code Online (Sandbox Code Playgroud)

meta每行的行数越多products,对性能的影响就越大。

当然,您需要ORDER BY在子查询中添加一个子句来定义从子查询中的每个集合中选择哪一行。@Craig 和 @Clodoaldo 已经告诉过你了。我返回meta最高的行id

SQL小提琴。

详情DISTINCT ON

优化性能

尽管如此,这并不总是最快的解决方案。根据数据分布,有各种其他查询样式。对于这个涉及另一个连接的简单案例,这个在大表测试中的运行速度要快得多:

SELECT p.*, sub.meta_id, m.product_id, m.price, m.flag
FROM  (
   SELECT product_id, max(id) AS meta_id
   FROM   meta
   GROUP  BY 1
   ) sub
JOIN meta     m ON m.id = sub.meta_id
JOIN products p ON p.id = sub.product_id;
Run Code Online (Sandbox Code Playgroud)

如果您不使用非描述性id作为列名,我们就不会遇到命名冲突,并且可以简单地编写SELECT p.*, m.*. (我从来没有使用id的列名。)

如果性能是您的首要要求,请考虑更多选择:

  • aMATERIALIZED VIEW带有来自 的预聚合数据meta,如果您的数据没有变化(很多)。
  • 一个递归 CTE 模拟一个表的松散索引扫描,每个产品有很多行(相对较少)。 这是我所知道的对整个表使用索引进行 DISTINCT 查询的唯一方法。 metaproduct_id


Cra*_*ger 5

我想您可能正在寻找DISTINCT ONPostgreSQL 扩展功能

SELECT 
  DISTINCT ON(product_id)
  * 
FROM meta 
INNER JOIN products ON products.id = meta.product_id;
Run Code Online (Sandbox Code Playgroud)

http://sqlfiddle.com/#!15/c8f34/18

但是,请注意,如果没有 a ORDER BY,则不能保证结果是一致的;数据库可以从匹配的行中选择它想要的任何行。