【问题标题】:PostgreSQL filter group by individual valuesPostgreSQL 按单个值过滤组
【发布时间】:2019-01-02 07:17:55
【问题描述】:

我有一个查询,它返回如图所示的数据;

name | field | count_1 | count_2 |
-----|-------|---------|---------|
John |  aaa  |    3    |    3    |
John |  bbb  |    3    |    3    |
John |  ccc  |    3    |    3    |
John |  ddd  |    1    |    1    |
Dave |  aaa  |    3    |    3    |
Dave |  bbb  |    3    |    3    |
Dave |  ccc  |    3    |    3    |
Dave |  ddd  |    3    |    3    |
-----|-------|---------|---------|

我需要根据count_1count_2=3 的计数来过滤这些数据。在上述情况下,对于字段 ddd 上的 John,两个计数都不满足条件,因此查询应仅返回 Dave,而不管其他字段上的 John 是否满足其他条件。我怎样才能做到这一点?

只要一个人在给定字段上没有满足一个单一的计数,他就应该被过滤掉。

【问题讨论】:

  • 要正确理解,请在问题中显示您想要获得的输出

标签: sql postgresql filter postgresql-9.1


【解决方案1】:

在 having 子句中使用布尔聚合 bool_and() 来获取满足条件的名称:

select name
from the_data
group by 1
having bool_and(count_1 = 3 and count_2 = 3)

 name 
------
 Dave
(1 row)

您可以将上述内容用作子查询来过滤并返回原始行(如果您需要的话):

select *
from the_data
where name in (
    select name
    from the_data
    group by 1
    having bool_and(count_1 = 3 and count_2 = 3)
    )

 name | field | count_1 | count_2 
------+-------+---------+---------
 Dave | aaa   |       3 |       3
 Dave | bbb   |       3 |       3
 Dave | ccc   |       3 |       3
 Dave | ddd   |       3 |       3
(4 rows)    

【讨论】:

    【解决方案2】:

    如果我做对了,NOT EXISTS 可能会对您有所帮助。

    SELECT *
           FROM (<your query>) x
           WHERE NOT EXISTS (SELECT *
                                    FROM (<your query) y
                                    WHERE y.name = x.name
                                          AND (y.count_1 <> 3
                                               OR y.count_2 <> 3));
    

    &lt;your query&gt; 替换为您的查询,它会为您提供发布的结果(或为此使用 CTE,但请注意,这可能会导致 Postgres 出现性能问题)。

    也许有一个更优雅的解决方案,它已经“捷径”进入您的查询,但要找到这样的解决方案需要有关您的架构和当前查询的更多信息。

    【讨论】:

    • 谢谢。这确实有帮助 - 尽管我修改了 Gordon 的回答 (WITH t AS (&lt;my-query&gt;) SELECT * FROM t WHERE NOT EXISTS...) 中的查询的第一部分,以避免重复庞大的查询
    • @Clint_A 。 . .鉴于您使用的是子查询,最好有一个只引用子查询一次的解决方案。
    【解决方案3】:

    我想你想要:

    with t as (
          <your query here>
         )
    select t.*
    from (select t.*,
                 count(*) filter (where count_1 <> 3) over (partition by name) as cnt_1_3,
                 count(*) filter (where count_2 <> 3) over (partition by name) as cnt_2_3
          from t
         ) t
    where cnt_1_3 = 0 and cnt_2_3 = 0;
    

    如果你不想要原始行,我会去聚合:

    select name
    from t
    group by name
    having min(count_1) = max(count_1) and min(count_1) = 3 and
           min(count_2) = max(count_2) and min(count_2) = 3;
    

    或者你可以这样表述:

    having sum( (count_1 <> 3)::int ) = 0 and
           sum( (count_2 <> 3)::int ) = 0
    

    请注意,以上所有内容都假设计数不是NULL(这对于称为计数的东西似乎是合理的)。如果NULL 值是可能的,您可以使用NULL-safe 比较 (is distinct from)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-26
      • 2022-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多