【问题标题】:PostgreSQL Where count conditionPostgreSQL Where 计数条件
【发布时间】:2011-12-28 12:03:22
【问题描述】:

我在 PostgreSQL 中有以下查询:

SELECT 
    COUNT(a.log_id) AS overall_count
FROM 
    "Log" as a, 
    "License" as b 
WHERE 
    a.license_id=7 
AND 
    a.license_id=b.license_id 
AND
    b.limit_call > overall_count
GROUP BY 
    a.license_id;

为什么会出现这个错误:

错误:列“overall_count”不存在

我的表结构:

License(license_id, license_name, limit_call, create_date, expire_date)
Log(log_id, license_id, log, call_date)

我想检查许可证是否已达到特定月份的通话限制。

【问题讨论】:

    标签: sql postgresql group-by having


    【解决方案1】:
    SELECT a.license_id, a.limit_call
         , count(b.license_id) AS overall_count
    FROM   "License"  a
    LEFT   JOIN "Log" b USING (license_id)
    WHERE  a.license_id = 7 
    GROUP  BY a.license_id  -- , a.limit_call  -- add in old versions
    HAVING a.limit_call > count(b.license_id)
    

    从 Postgres 9.1 开始,主键覆盖了 GROUP BY 子句中表的所有列。在旧版本中,您必须将 a.limit_call 添加到 GROUP BY 列表中。 release notes for 9.1

    当primary时允许查询目标列表中的非GROUP BY列 密钥在GROUP BY 子句中指定

    进一步阅读:

    WHERE 子句中的条件必须移至 HAVING 子句,因为它引用聚合函数的结果( @987654333 @ 已应用)。并且您不能在 HAVING 子句中引用 输出列(列别名),您只能引用输入列。所以你必须重复表达。 The manual:

    输出列的名称可用于引用该列的值 ORDER BYGROUP BY 子句,但不在 WHEREHAVING 中 条款;在那里你必须写出表达式。

    我颠倒了FROM 子句中的表顺序,并稍微清理了语法以减少混乱。 USING 在这里只是一个符号方便。

    我使用 LEFT JOIN 而不是 JOIN,因此您根本不会排除没有任何日志的许可证。

    count() 仅计算非空值。由于您想计算表"Log" 中的相关条目,因此使用count(b.license_id) 更安全且成本更低。该列用于连接,因此我们不必担心该列是否可以为空。
    count(*) 甚至更短且速度稍快。如果您不介意在左表中为0 行计算1,请使用它。

    旁白:如果可能,我建议不要在 Postgres 中使用 mixed case identifiers。非常容易出错。

    【讨论】:

    • 嗨欧文,谢谢。它对我有用,但我不想在结果中返回 b.limit_call 。顺便说一句,与 9.1 版本有什么不同?
    • @Hadi:您可以在此查询中从 SELECT 列表中删除任何列,没问题。请参阅我修改后的答案以了解 9.1 中的新增功能
    • @ErwinBrandstetter,如果/因为“you”通常将主键与许多“b”记录相连接,“9.1 中的新功能”如何工作?
    • @epox:一旦 PK(隐式 UNIQUE NOT NULL)在 GROUP BY 列表中,将同一表的任何其他列添加到同一 GROUP BY 列表不会改变结果。 (那些是“功能依赖的”。)在加入许多行之后也是如此。
    • 我明白了,所以a 组是唯一(1 行)组,并且任何as 列都可以在 HAVING 中使用,因为 9.1。但是如果“你”在 HAVING 中需要 b 列,那么如果没有 COUNT(b.x) 或包含在 GROUP BY 中,您将无法使用它。谢谢
    【解决方案2】:

    where 查询无法识别您的列别名,此外,您正试图在聚合之后过滤掉行。试试:

    SELECT 
    COUNT(a.log_id) AS overall_count
    FROM 
    "Log" as a, 
    "License" as b 
    WHERE 
    a.license_id=7 
    AND 
    a.license_id=b.license_id 
    GROUP BY 
    a.license_id
    having b.limit_call > count(a.log_id);
    

    having 子句类似于where 子句,不同之处在于它处理聚合之后的列,而where 子句处理聚合之前的列。

    另外,你的表名用双引号括起来有什么原因吗?

    【讨论】:

    • 我收到错误“列“b.limit_call”必须出现在 GROUP BY 子句中或在聚合函数中使用”当使用有时
    • 那么您还需要按该列进行分组。由于我不知道您的数据结构,您必须为group by 子句找出适当的列列表。
    【解决方案3】:

    纯条件计数(*):

      SELECT COUNT(*) FILTER(where a.myfield > 0) AS my_count
        FROM "Log" as a 
    
    GROUP BY a.license_id
    

    所以你:

    • 获取0 用于条件从未满足的组
    • 可以根据需要添加任意数量的 count(*) 列

    过滤出条件不匹配的组:

    注意:您不能使用HAVING b.limit_call > ...,除非您按limit_call 分组。但是您可以使用 agregate 函数将组中的许多“limit_calls”映射为单个值。例如,在您的情况下,您可以使用MAX

      SELECT COUNT(a.log_id) AS overall_count
        FROM "Log" as a 
        JOIN "License" b ON(a.license_id=b.license_id)
    
    GROUP BY a.license_id
      HAVING MAX(b.limit_call) > COUNT(a.log_id)
    

    并且不要关心在第一行和最后一行中重复COUNT(a.log_id) 表达式。 Postgres 会对其进行优化。

    【讨论】:

      猜你喜欢
      • 2018-02-06
      • 2020-03-21
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 2017-06-28
      • 1970-01-01
      相关资源
      最近更新 更多