【问题标题】:PostgreSQL: How to select on non-aggregating column?PostgreSQL:如何在非聚合列上进行选择?
【发布时间】:2016-11-19 04:02:18
【问题描述】:

似乎是一个简单的问题,但我无法完成它。我想要做的是返回所有具有重复 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;

但这根本不返回任何东西!什么是“正确的”查询?

我只需要像我的第一个工作建议一样还是有更好的方法?

【问题讨论】:

  • CTE 可能对您很有效。
  • 您是否只是想避免使用多个SELECT 语句?
  • @Nicarus 是的。我想删除多余的 WHERE other_col='x' (实际情况很长)所以删除 1 SELECT 会更清楚。

标签: sql postgresql aggregate having


【解决方案1】:

您声明要避免两个“查询”,这实际上是不可能的。有很多可用的解决方案,但我会使用这样的 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,因此您不必重复逻辑,而且我个人认为它更易于阅读和理解它的作用。

【讨论】:

  • 绝对完美!谢谢 Nicarus,这正是我想要的。
  • 好一个。顺便说一句,我研究过 Temp Tables 和派生表在与 CTE 相比时性能很好,对吗?
  • @MuthaiahPL - 根据我的经验,这取决于您在做什么,但临时表确实允许添加索引等操作 - CTE 不允许。在这种情况下,问题不在于性能,而在于编写查询的简单性,我相信 CTE 提供了这一点。
【解决方案2】:
SELECT name FROM Table
WHERE id IN (SELECT id, COUNT(*) FROM Table GROUP BY id HAVING COUNT(*)>1) Temp

【讨论】:

  • 这与我的建议几乎相同,我想知道是否有更好的方法来完成这个......
  • 我可以知道您在寻找更好的方法吗?
  • 具体来说,我只想知道这是否可以在单个查询中完成,因为我宁愿不必重复两个单独的选择(如果可能的话)。实际 where 条件很长,我想简化一下。
【解决方案3】:

使用 EXIST 运算符

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

【讨论】:

    【解决方案4】:

    使用连接:

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

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


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

    【讨论】:

    • 这不是一个坏建议,但碰巧我的观点很大,所以我不得不去on v1.id = v2.id AND v1.other_col='x' AND v2.other_col='x',而且它需要的时间比接受的答案要长。关于id,我会确保在以后的问题中更恰当地命名我的专栏,谢谢!
    【解决方案5】:

    不要使用 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)。甚至是部分索引,if other_col = 'x' 是一个常量谓词:

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

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

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

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

    允许使用带有部分索引的index-only scan 谓词涉及未存储在索引中的列(Tomas Vondra, 堀口京太郎)

    如果查询提及此类列,现在允许仅索引扫描 仅在与索引谓词匹配的WHERE 子句中

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

    【讨论】:

      猜你喜欢
      • 2016-10-12
      • 1970-01-01
      • 1970-01-01
      • 2021-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-29
      相关资源
      最近更新 更多