Выбор COUNT из разных критериев на столе - PullRequest
16 голосов
/ 15 июня 2011

У меня есть таблица с именем '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 разных СЧЕТОВ от одногоподмножество данных из таблицы заданий?

подмножество данных - 'creator_id = 5', после этого каждый отсчет в основном состоит из 1-2 дополнительных условий.Обратите внимание, что сейчас мы используем Postgres, но, возможно, перейдем на MySQL в ближайшем будущем.Поэтому, если вы можете предоставить ANSI-совместимое решение, я был бы благодарен:)

Ответы [ 3 ]

24 голосов
/ 15 июня 2011

Это типичное решение. Используйте инструкцию 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

UPDATE Как отмечено, когда 0 записей возвращаются как t, это приводит к единственному результату Null во всех значениях. У вас есть три варианта

1) Добавьте предложение «Имея», чтобы у вас не было возвращено ни одной записи, а не результата всех 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, как продемонстрировал Сбарро. Следует отметить, что ненулевым значением может быть все, что не обязательно должно быть 1

например

 SELECT
      COUNT(CASE WHEN 
            jobs.status_id NOT IN (8,3,11) THEN 'Manticores Rock' ELSE NULL
       END) as [active_count]
12 голосов
/ 15 июня 2011

Я бы использовал этот подход, используйте 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 
0 голосов
/ 18 декабря 2017

Brief

SQL Server 2012 представил логическую функцию IIF . Используя SQL Server 2012 или новее, вы можете теперь использовать эту новую функцию вместо выражения CASE. Функция IIF также работает с Базой данных SQL Azure (но в настоящее время она не работает с Хранилищем данных SQL Azure или Хранилище параллельных данных ). Это сокращение для выражения CASE.

Я обнаружил, что использую функцию IIF вместо выражения CASE, когда есть только один случай. Это облегчает боль от необходимости писать CASE WHEN condition THEN x ELSE y END и вместо этого писать IIF(condition, x, y). Если могут быть выполнены несколько условий (несколько WHEN с), вместо этого вам следует рассмотреть возможность использования регулярного выражения 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_value и false_value могут быть любого типа. Те же правила, которые применяются к выражению CASE для Булевы выражения, нулевая обработка и возвращаемые типы также применяются к IIF. Для получения дополнительной информации см. CASE (Transact-SQL) .

Тот факт, что IIF переведен в CASE, также влияет на другие аспекты поведения этой функции. С CASE выражения могут быть вложены только до уровня 10, IIF операторов также может быть вложен только до максимального уровня 10. Также, IIF удаленный на другие серверы как семантически эквивалентный CASE выражение, со всеми поведениями удаленного CASE выражения.


Код

Реализация функции IIF в SQL будет выглядеть следующим образом (с использованием той же логики, представленной @ rsbarro в его ответе ):

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 
...