【问题标题】:Selecting COUNT from different criteria on a table从表的不同条件中选择 COUNT
【发布时间】:2011-09-15 01:29:53
【问题描述】:

我有一个名为“jobs”的表。对于特定用户,作业可以是活动的、存档的、过期的、待定的或关闭的。现在每个页面请求都会生成 5 个 COUNT 查询,并且为了进行优化,我正试图将其减少为单个查询。这是我到目前为止所拥有的,但它几乎不比 5 个单独的查询快。请注意,我已简化每个子查询的条件以使其更易于理解,但完整查询的行为相同。

有没有办法在不使用低效子查询的情况下在同一个查询中获得这 5 个计数?

SELECT
  (SELECT count(*)
    FROM "jobs"
    WHERE
      jobs.creator_id = 5 AND
      jobs.status_id NOT IN (8,3,11) /* 8,3,11 being 'inactive' related statuses */
  ) AS active_count, 
  (SELECT count(*)
    FROM "jobs"
    WHERE
      jobs.creator_id = 5 AND
      jobs.due_date < '2011-06-14' AND
      jobs.status_id NOT IN(8,11,5,3) /* Grabs the overdue active jobs
                                      ('5' means completed successfully) */
  ) AS overdue_count,
  (SELECT count(*)
    FROM "jobs"
    WHERE
      jobs.creator_id = 5 AND
      jobs.due_date BETWEEN '2011-06-14' AND '2011-06-15 06:00:00.000000'
  ) AS due_today_count

这会再进行 2 个子查询,但我想你明白了。

有没有更简单的方法来收集这些数据,因为它基本上是来自工作表的同一数据子集的 5 个不同的 COUNT?

数据的子集是'creator_id = 5',之后每个计数基本上只是1-2个附加条件。请注意,现在我们正在使用 Postgres,但在不久的将来可能会迁移到 MySQL。所以如果你能提供一个 ANSI 兼容的解决方案,我会很感激 :)

【问题讨论】:

  • 您可以创建一个子查询或加入一个新表,将所有内容分成 3 个组,然后对组执行 COUNT / GROUP BY。

标签: sql query-optimization subquery


【解决方案1】:

这是典型的解决方案。使用 case 语句来分解不同的条件。如果记录满足,则为 1,否则为 0。然后对值​​执行 SUM

  SELECT
    SUM(active_count) active_count,
    SUM(overdue_count) overdue_count
    SUM(due_today_count) due_today_count
  FROM 
  (

  SELECT 
    CASE WHEN jobs.status_id NOT IN (8,3,11) THEN 1 ELSE 0 END active_count,
    CASE WHEN jobs.due_date < '2011-06-14' AND jobs.status_id NOT IN(8,11,5,3)  THEN 1 ELSE 0 END  overdue_count,
    CASE WHEN jobs.due_date BETWEEN '2011-06-14' AND '2011-06-15 06:00:00.000000' THEN 1 ELSE 0 END  due_today_count

    FROM "jobs"
    WHERE
      jobs.creator_id = 5 ) t

更新 如前所述,当返回 0 条记录时,这将导致所有值中的 Null 的单个结果。你有三个选择

1) 添加Having 子句,这样您就不会返回任何记录,而不是所有NULLS 的结果

   HAVING SUM(active_count) is not null

2) 如果您希望返回所有零,则可以将合并添加到所有总和中

例如

 SELECT
      COALESCE(SUM(active_count)) active_count,
       COALESCE(SUM(overdue_count)) overdue_count
      COALESCE(SUM(due_today_count)) due_today_count

3) 利用COUNT(NULL) = 0 sbarro 所展示的事实。您应该注意,非空值可以是任何它不必是 1

例如

 SELECT
      COUNT(CASE WHEN 
            jobs.status_id NOT IN (8,3,11) THEN 'Manticores Rock' ELSE NULL
       END) as [active_count]

【讨论】:

  • 我喜欢这种方法,您可以根据需要添加其他条件。
  • 我喜欢这个解决方案 :) 我会在接受它之前针对我的问题进行调查。
  • Postgres 要求我在每个 CASE 中添加 END 语句。这是标准 SQL 还是 postgres 决定需要的东西? CASE WHEN jobs.status_id = 5 THEN 1 ELSE 0 END active_count 否则它会向我抛出异常。
  • @nzignab 这不是 postgres 独有的。那是我健忘。我更新了答案
  • 只是想指出这将执行时间减少了大约 75%,谢谢 :D
【解决方案2】:

我会使用这种方法,将 COUNT 与 CASE WHEN 结合使用。

SELECT 
    COUNT(CASE WHEN 
        jobs.status_id NOT IN (8,3,11) THEN 1 
    END) as [Count1],
    COUNT(CASE WHEN 
        jobs.due_date < '2011-06-14' 
        AND jobs.status_id NOT IN(8,11,5,3) THEN 1
    END) as [COUNT2],
    COUNT(CASE WHEN
            jobs.due_date BETWEEN '2011-06-14' AND '2011-06-15 06:00:00.000000'
    END) as [COUNT3]
FROM 
    "jobs"
WHERE 
     jobs.creator_id = 5 

【讨论】:

  • 我想性能是相似的,即使这种方法只使用 1 SELECT。
  • +1 但我仍然会使用 sum 例如SUM(CASE WHEN jobs.status_id NOT IN (8,3,11) THEN 1 ELSE 0 END)。使用COUNT 可以利用COUNT 忽略并非所有人都意识到的空值这一事实。
  • @Conrad:好点。我认为这个决定取决于个人喜好。对我来说,当您尝试计算某些东西时,看到 COUNT 会更清楚一些,但任何一种方法都可以正常工作。
  • ) 我知道你的意思 SUM (active_count) active_ count 看起来很有趣
【解决方案3】:

简介

SQL Server 2012 introduced the IIF logical function。使用 SQL Server 2012 或更高版本,您现在可以使用这个新函数而不是 CASE 表达式。 IIF 函数也适用于 Azure SQL 数据库(但目前它不适用于 Azure SQL 数据仓库并行数据仓库 )。它是CASE 表达式的简写。

当只有一种情况时,我发现自己使用IIF 函数而不是CASE 表达式。这减轻了不得不写CASE WHEN condition THEN x ELSE y END 而是写成IIF(condition, x, y) 的痛苦。如果可能满足多个条件(多个WHENs),则应考虑使用常规CASE 表达式而不是嵌套IIF 函数。

根据布尔表达式是否返回两个值之一 在 SQL Server 中计算结果为 true 或 false。

语法

IIF ( boolean_expression, true_value, false_value )

参数

boolean_expression
一个有效的布尔表达式。

如果此参数不是布尔表达式,则语法错误 提出来。

true_value
boolean_expression 计算结果为时返回的值 真的。

false_value
boolean_expression 计算时返回的值 为假。

备注

IIF 是编写CASE 表达式的简写方式。它评估 布尔表达式作为第一个参数传递,然后返回 基于结果的其他两个参数中的任何一个 评估。也就是说,如果布尔值返回 true_value 表达式为真,如果布尔值返回 false_value 表达式为假或未知。 true_valuefalse_value 可以 任何类型的。适用于 CASE 表达式的相同规则 布尔表达式、空处理和返回类型也适用于 IIF。如需更多信息,请参阅CASE (Transact-SQL)

IIF被翻译成CASE这一事实也对 此函数的行为的其他方面。由于CASE 表达式只能嵌套到 10 级,IIF 语句 也可以嵌套最多 10 级。另外,IIF 是 作为语义等效的CASE远程到其他服务器 表达式,具有远程 CASE 表达式的所有行为。


代码

SQL 中IIF 函数的实现类似于以下内容(使用@rsbarrohis answer 中提供的相同逻辑):

SELECT 
    COUNT(
        IIF(jobs.status_id NOT IN (8,3,11), 1, 0)
    ) as active_count,
    COUNT(
        IIF(jobs.due_date < '2011-06-14' AND jobs.status_id NOT IN(8,11,5,3), 1, 0)
    ) as overdue_count,
    COUNT(
        IIF(jobs.due_date BETWEEN '2011-06-14' AND '2011-06-15 06:00:00.000000', 1, 0)
    ) as due_today_count
FROM 
    "jobs"
WHERE 
     jobs.creator_id = 5 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-26
    • 2021-07-06
    • 1970-01-01
    • 2010-10-11
    • 2013-02-02
    • 2015-08-25
    • 1970-01-01
    • 2011-10-17
    相关资源
    最近更新 更多