PostgreSQL:如何在非聚合列上 select?

PostgreSQL: How to select on non-aggregating column?

这似乎是一个简单的问题,但我无法完成它。我想要做的是 return 所有具有重复 ID 的名称。视图如下所示:

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;

但这return根本算不了什么! 'proper' 查询是什么?

我是必须像我的第一个工作建议那样做还是有更好的方法?

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

使用 EXIST 运算符

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

使用联接:

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

使用 distinct 是为了防止超过 2 行共享相同的 id。如果那不可能,您可以省略 distinct.


注意:将不唯一的列命名为 id 可能会引起混淆,因为这是唯一标识符列的行业标准。如果根本没有唯一列,就会造成编码困难。

你说你想避免两个 "queries",这实际上是不可能的。有很多可用的解决方案,但我会像这样使用 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,因此您不必重复逻辑,我个人觉得它更容易阅读和理解它在做什么。

不要使用 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 表达式不会产生派生的 table,它将被解析为简单的索引查找。

说到:(other_col, id) 上的多列索引应该有所帮助。根据数据分布和访问模式,附加负载列 name 以启用仅索引扫描可能会有所帮助:(other_col, id, name)。甚至部分索引,if other_col = 'x' 是常量谓词:

CREATE INDEX ON view (id) WHERE other_col = 'x';
  • PostgreSQL does not use a partial index

即将推出的 Postgres 9.6 甚至允许对部分索引进行仅索引扫描:

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

你会喜欢这项改进(quoting the /devel manual):

Allow using an index-only scan with a partial index when the index's predicate involves column(s) not stored in the index (Tomas Vondra, Kyotaro Horiguchi)

An index-only scan is now allowed if the query mentions such columns only in WHERE clauses that match the index predicate

使用 EXPLAIN (ANALYZE, TIMING OFF) SELECT ...
验证性能 运行 几次以排除缓存影响。