2016-07-15 60 views
2

看起来像一个简单的问题,但我很难完成它。我想要做的是返回所有具有重复ID的名称。视图看起来这样:PostgreSQL:如何选择非聚合列?

id | name | other_col 
---+--------+---------- 
1 | James | x 
2 | John | x 
2 | David | x 
3 | Emily | x 
4 | Cameron| x 
4 | Thomas | x 

所以在这种情况下,我只是想要的结果:

name 
------- 
John 
David 
Cameron 
Thomas 

下面的查询工作,但它似乎是一个矫枉过正有两个不同的选择:

select name 
from view where id = ANY(select id from view 
         WHERE other_col='x' 
         group by id 
         having count(id) > 1) 
     and other_col='x'; 

我相信这应该是可以做到的线下的东西:

select name from view WHERE other_col='x' group by id, name having count(id) > 1; 

但是这完全没有任何回报!什么是“正确的”查询?

我只是喜欢我的第一个工作建议,还是有更好的方法?

+2

CTE可能会很好地为你工作。 –

+0

你只是试图避免使用多个'SELECT'语句? – Nicarus

+0

@Ncarus是的。我想删除冗余 WHERE other_col ='x'(真实条件相当长),所以删除1'SELECT'会使其更清晰。 – nico

回答

2

你说你想避免两个“查询”,这是不可能的。有很多可用的解决方案,但我会用一个CTE像这样:

WITH cte AS 
(
SELECT 
    id, 
    name, 
    other_col, 
    COUNT(name) OVER(PARTITION BY id) AS id_count 
FROM 
    table 
) 

SELECT name FROM cte WHERE id_count > 1; 

您可以重用CTE,所以您不必重复逻辑和我个人觉得它更容易阅读和理解它是什么是在做。

+0

绝对完美!感谢Nicarus,这正是我正在寻找的。 – nico

+0

好的。顺便说一句,我已经学习了临时表和派生表在性能上与CTE相比有很好的表现,对吗? –

+2

@MuthaiahPL - 根据我的经验,这取决于你在做什么,但临时表确实允许添加索引等事情 - CTE不这样做。在这种情况下,问题不是关于性能,而是写入查询的简单性,我相信CTE提供了这个问题。 – Nicarus

2
SELECT name FROM Table 
WHERE id IN (SELECT id, COUNT(*) FROM Table GROUP BY id HAVING COUNT(*)>1) Temp 
+0

这与我的建议非常相似,我想知道是否有更好的方法来完成此操作... – nico

+0

我可以知道您正在寻找更好的方法吗? –

+0

具体而言,我只是想知道这是否可以在单个查询中完成,因为我宁愿不必重复两个单独的选择(如果可能)。实际情况很长,我想简化它。 – nico

0

方面的用途存在操作

SELECT * FROM table t1 
WHERE EXISTS(
    SELECT null FROM table t2 
    WHERE t1.id = t2.id 
    AND t1.name <> t2.name 
) 
0

使用JOIN:

select distinct name 
from view v1 
join view v2 on v1.id = v2.id 
    and v1.name != v2.name 

使用的distinct有没有的情况下,有超过2行共享相同的id。如果这是不可能的,你可以省略distinct


的说明:命名列id时,它不是唯一的可能会引起混乱,因为它是唯一标识符列的行业标准。如果根本没有独特的列,将导致编码困难。

+0

这不是一个坏建议,但它发生在我看来很大,所以我不得不去'在v1.id = v2.id AND v1.other_col ='x'AND v2.other_col ='x''和它比接受的答案需要更长的时间。关于'id',我会确保在以后的问题中更好地列出我的专栏,谢谢! – nico

0

不是使用CTE。这通常比较昂贵,因为Postgres必须实现中间结果。

一个EXISTS半连接通常是最快的。只要确保重复谓词(或匹配值):

SELECT name 
FROM view v 
WHERE other_col = 'x' 
AND EXISTS (
    SELECT 1 FROM view 
    WHERE other_col = 'x' -- or: other_col = v.other_col 
    AND id <> v.id  -- exclude join to self 
    ); 

这是一个查询,即使你看到关键字SELECT这里两次。一个EXISTS表达式不会生成派生表,它将被解析为简单索引查找。

说到其中:(other_col, id)上的多列索引应该有所帮助。根据数据分布和访问模式,追加有效负载列name以启用仅索引扫描可能有助于:(other_col, id, name)。甚至部分指标,如果other_col = 'x'是一个常数谓词:

CREATE INDEX ON view (id) WHERE other_col = 'x'; 

即将到来的Pos​​tgres 9.6甚至会允许索引仅在部分索引扫描:

CREATE INDEX ON view (id, name) WHERE other_col = 'x'; 

您将这种改进(quoting the /devel manual):

允许使用index-only scan具有部分索引当索引的 谓词涉及没有被存储在索引(托马斯冯德拉, 京太郎堀口)柱(S)

仅索引扫描现在允许,如果查询提到这样的列 只在WHERE匹配索引断言

VERI该条款fy表现与EXPLAIN (ANALYZE, TIMING OFF) SELECT ...
运行几次以排除缓存效果。