#sql
#sql
Вопрос:
У меня есть таблица с 6-значными номерами событий; я пытаюсь подсчитать номер одного события (скажем, 600189), произошедшего за определенный период времени, и разделить его на количество второго номера события (скажем, 600122) за тот же период времени.
приведенный ниже запрос возвращает мои два значения, но я хочу изменить запрос для выполнения вычисления (count of 600189 / count of 600122) * 100
:
select count (messageno)
from event
where timestamp > '2019-03-14' and timestamp < '2019-03-15' and
messageno in ('600122','600189')
group by messageno
Ответ №1:
Я бы просто использовал условную агрегацию:
select sum(case when messageno = '600122' then 1 else 0 end) as cnt_1,
sum(case when messageno = '600189' then 1 end) as cnt_2,
( sum(case when messageno = '600122' then 1 else 0 end) /
sum(case when messageno = '600189' then 1 end)
) as ratio
from event
where timestamp > '2019-03-14' and
timestamp < '2019-03-15' and
messageno in ('600122', '600189');
Обратите внимание, что else
в знаменателе удалено. Это сделано намеренно. Если нет сообщений с этим номером, то это возвращает NULL
ошибку деления на ноль.
Ответ №2:
Есть пара шаблонов запросов.
Одним из подходов является условное агрегирование. Мы можем вернуть значение 1, если условие истинно, в противном случае вернуть 0, и сложить единицы и 0 с суммой aggregate, чтобы получить количество. Рассмотрим:
Для MySQL мы можем сделать что-то вроде этого:
SELECT SUM( IF(e.messageno='600122',1,0) ) AS cnt_600122
, SUM( IF(e.messageno='600189',1,0) ) AS cnt_600189
, ( 100.0
* SUM( IF(e.messageno='600122',1,0) )
/ SUM( IF(e.messageno='600189',1,0) )
) AS pct
FROM event e
WHERE e.timestamp > '2019-03-14'
AND e.timestamp < '2019-03-15'
AND e.messageno IN ('600122','600189')
Более переносимый эквивалент, совместимый со стандартами ANSI, будет работать для Microsoft SQL Server и др.:
SELECT SUM( CASE e.messageno WHEN '600122' THEN 1 ELSE 0 END ) AS cnt_600122
, SUM( CASE e.messageno WHEN '600189' THEN 1 ELSE 0 END ) AS cnt_600189
, ( 100.0
* SUM( CASE e.messageno WHEN '600122' THEN 1 ELSE 0 END )
/ SUM( CASE e.messageno WHEN '600122' THEN 1 ELSE 0 END )
) AS pct
FROM event e
WHERE e.timestamp > '2019-03-14'
AND e.timestamp < '2019-03-15'
AND e.messageno IN ('600122','600189')
Другие подходы заключались бы в выполнении агрегации во встроенном представлении (представлениях) или использовании подзапросов в списке ВЫБОРА:
SELECT 100.0
* ( SELECT SUM(1)
FROM event e
WHERE e.timestamp > '2019-03-14'
AND e.timestamp < '2019-03-15'
AND e.messageno IN ('600122')
)
/ ( SELECT SUM(1)
FROM event e
WHERE e.timestamp > '2019-03-14'
AND e.timestamp < '2019-03-15'
AND e.messageno IN ('600189')
)
AS pct
Комментарии:
1. я попробовал первый пример, и он выдал ошибку — Сообщение 156, уровень 15, состояние 1, в строке 1 неправильный синтаксис возле ключевого слова ‘IF’. Сообщение 102, уровень 15, состояние 1, в строке 1 неправильный синтаксис рядом с ‘,’. Сообщение 102, уровень 15, состояние 1, строка 2 Неправильный синтаксис рядом с ‘,’. Сообщение 102, уровень 15, состояние 1, строка 4 Неправильный синтаксис рядом с ‘,’. Сообщение 102, уровень 15, состояние 1, строка 5 Неправильный синтаксис рядом с ‘,’.
2. Синтаксис, приведенный в этом ответе, был предназначен для MySQL, а не для Microsoft SQL Server. Ответ отредактирован, чтобы включить более переносимую версию первого примера, более совместимую со стандартами ANSI.
3. оба варианта сработали! спасибо! я буду запускать оба независимо, чтобы увидеть, какой из них работает быстрее. Очень признателен!