JOIN返回重复项后的GROUP或DISTINCT

时间:2014-08-25 13:25:10

标签: 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

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

SELECT DISTINCT(product_id), *
FROM meta JOIN products ON products.id = meta.product_id

我尝试了许多方法,例如在子查询中尝试DISTINCTGROUP BY,但总是有相同的结果。

4 个答案:

答案 0 :(得分:3)

我想你可能在寻找DISTINCT ON, a PostgreSQL extension feature

SELECT 
  DISTINCT ON(product_id)
  * 
FROM meta 
INNER JOIN products ON products.id = meta.product_id;

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

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

答案 1 :(得分:3)

从表中检索所有或大多数行时,此类查询的最快方法通常是首先聚合/消除歧义 并稍后加入

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;

meta中每行products行数越多,对效果的影响就越大。

当然,您要在子查询中添加ORDER BY子句,在子查询中定义哪个行来选择每个集合。 @Craig和@Clodoaldo已经告诉过你了。我将返回metaid DISTINCT ON

SQL Fiddle.

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; 的详细信息:

优化性能

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

id

如果您不使用非描述性SELECT p.*, m.*作为列名,我们就不会遇到命名冲突,只能编写id。 (我从不使用meta作为列名。)

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

    如果您的数据没有变化(很多),则
  • MATERIALIZED VIEW来自meta的预先汇总数据。
  • 一个递归CTE,为每个产品多个行的 product_id表模拟loose index scan(相对较少的不同{{1}} )。
    这是我知道在整个表中使用DISTINCT查询索引的唯一方法。

答案 2 :(得分:1)

按照@Craig's answer的建议使用distinct on,但结合评论中明确的order by子句。 SQL Fiddle

select distinct on(m.product_id) * 
from
    meta m
    inner join
    products p on p.id = m.product_id
order by m.product_id, m.id desc;

答案 3 :(得分:0)

您可以使用子查询来标识每个产品的最大值(ID),然后在超级查询中使用它来收集您要显示的详细信息:

SELECT q.product_id, meta.* from
(SELECT product_id, max(meta.ID)
 FROM meta JOIN products ON products.id=meta.product_id 
 GROUP BY product_id) q 
JOIN meta ON q.max=meta.id;

这不是唯一的解决方案!

使用DISTINCT ON解决方案的快速比较表明它较慢(http://sqlfiddle.com/#!15/c8f34/38)。它避免对ID进行完全排序,并且更喜欢顺序扫描。