У меня есть таблица, в которой есть столбец processed_timestamp - если запись была обработана, то это поле содержит дату и время, когда она была обработана, в противном случае она равна нулю.
Я хочу написать запрос, который возвращает две строки:
NULL xx -- count of records with null timestamps
NOT NULL yy -- count of records with non-null timestamps
Это возможно?
Обновлять: Таблица довольно большая, поэтому важна эффективность. Я мог бы просто запустить два запроса, чтобы вычислить каждую сумму отдельно, но я хочу избежать двойного попадания в таблицу, если я могу этого избежать.


В MySQL вы могли бы сделать что-то вроде
SELECT
IF(ISNULL(processed_timestamp), 'NULL', 'NOT NULL') as myfield,
COUNT(*)
FROM mytable
GROUP BY myfield
Лучший ответ для MySQL.
Если это оракул, вы можете:
select decode(field,NULL,'NULL','NOT NULL'), count(*)
from table
group by decode(field,NULL,'NULL','NOT NULL');
Я уверен, что другие БД допускают подобный трюк.
Попробуйте следующее, оно не зависит от производителя:
select
'null ' as type,
count(*) as quant
from tbl
where tmstmp is null
union all
select
'not null' as type,
count(*) as quant
from tbl
where tmstmp is not null
После того, как наш местный гуру DB2 посмотрел на это, он соглашается: ни одно из представленных на сегодняшний день решений (включая это) не может избежать полного сканирования таблицы (таблицы, если метка времени не проиндексирована, или индекса в противном случае). Все они проверяют каждую запись в таблице ровно один раз.
Все решения CASE / IF / NVL2 () выполняют преобразование нуля в строку для каждой строки, создавая ненужную нагрузку на СУБД. У этого решения нет такой проблемы.
Это довольно большая таблица - ударить ее дважды, как это неэффективно, не так ли?
Нет, на самом деле (по крайней мере, в DB2, которую я использую), это решение будет таким же быстрым, как и все решения типа decode / nvl2 - все они должны выполнить полное сканирование таблицы (мое решение обработает такое же количество записей в целом, но в двух группах) - в обоих случаях требуется индекс по полю метки времени.
Когда я завтра приступлю к работе, мне будет интересно попробовать это решение бок о бок с решением конкретного производителя.
Я пришел сюда, чтобы опубликовать это решение, но Pax Diablo меня опередил. Все остальные решения полагаются на преобразование столбца в строку, которую вы затем подсчитываете. В этом случае есть вероятность, что вам даже не придется касаться каких-либо строк, потому что вся важная информация находится в индексе.
@Pax: Я пришел сюда случайно и заметил, что за этот ответ я проголосовал против (я понятия не имею, почему я должен был это сделать). Любопытно, что мне удалось вернуть его к +1, хотя оно должно было быть слишком старым. Странный.
Нет проблем, @Tomalak, я уже заметил это раньше. Вы не можете отменить голосование по прошествии определенного времени, если вопрос / ответ не будет отредактирован. Но вы можете изменить свой голос (почему?, Только сильные мира сего могут ответить на этот вопрос). Обычно, если кто-то решает проблему, за которую я проголосовал против (но недостаточно, чтобы заслужить положительный голос), я просто немного редактирую вопрос / ответ, чтобы отменить голосование.
@Pax: Значит, голосование "против" должно быть подано до версии №4. Я до сих пор не понимаю, зачем я вообще его бросил. В любом случае - это вовсе не предназначалось для того, чтобы попросить вас проголосовать за. Но все равно спасибо. ;-)
Оракул:
группа по nvl2 (поле, 'NOT NULL', 'NULL')
Сладкое - это отличная настраиваемая функция. (Подробнее здесь: java2s.com/Code/Oracle/Char-Functions/…)
Я не знаю почему, но похоже, что он работает и с моей версией MariaDB (10.4.13).
В T-SQL (MS SQL Server) это работает:
SELECT
CASE WHEN Field IS NULL THEN 'NULL' ELSE 'NOT NULL' END FieldContent,
COUNT(*) FieldCount
FROM
TheTable
GROUP BY
CASE WHEN Field IS NULL THEN 'NULL' ELSE 'NOT NULL' END
Другой метод MySQL - использовать CASE оператор, который можно обобщить на большее количество альтернатив, чем IF():
SELECT CASE WHEN processed_timestamp IS NULL THEN 'NULL'
ELSE 'NOT NULL' END AS a,
COUNT(*) AS n
FROM logs
GROUP BY a
Функция IF () также работает - if (Processing_timestamp имеет значение null, 'null', 'not null')
Мне лично нравится решение Pax, но если вам абсолютно необходимо вернуть только одну строку (как я недавно сделал), в MS SQL Server 2005/2008 вы можете «сложить» два запроса, используя CTE.
with NullRows (countOf)
AS
(
SELECT count(*)
FORM table
WHERE [processed_timestamp] IS NOT NULL
)
SELECT count(*) AS nulls, countOf
FROM table, NullRows
WHERE [processed_timestamp] IS NULL
GROUP BY countOf
Надеюсь это поможет
Но тогда вы дважды попадаете в базу данных - неэффективно. (Наверное, поэтому Пакс удалил свое решение.)
Довольно большой подход к такой простой проблеме, не так ли?
Вы только дважды попадаете в базу данных, если ваша база данных не оптимизирует ее. Вероятно, безопасное предположение, но тем не менее предположение.
Пакс удалил свое решение, потому что оно начало получать отрицательные голоса, несмотря на то, что это единственное решение, не связанное с поставщиком :-). Вероятно, лучше иметь полный список всех оптимизированных решений для конкретных поставщиков, и читатели могут выбрать, какое из них им нужно.
Собственно, я положу его обратно и возьму хиты - интересно посмотреть, сколько голосов против ...
Что ж, тогда это решение должно получить дополнительные голоса против, потому что оно неэффективно И зависит от поставщика. Есть еще одно решение T-SQL, которое дважды не попадает в базу данных.
@ Стюарт Джонсон. CTE являются ANSI SQL: 1999! Кроме того, все агрегатные функции (из которых count (*) равно одному) дают скалярный результат для заданного им набора, в данном случае строки со значениями этого столбца, а затем строки без них.
[T-SQL]:
select [case], count(*) tally
from (
select
case when [processed_timestamp] is null then 'null'
else 'not null'
end [case]
from myTable
) a
И вы можете добавить в оператор case любые другие значения, которые хотите сформировать раздел, например сегодня, вчера, с полудня до 14:00, после 18:00 в четверг.
Стюарт,
Может быть, рассмотрите это решение. Это (также!) Не зависит от производителя.
SELECT count([processed_timestamp]) AS notnullrows,
count(*) - count([processed_timestamp]) AS nullrows
FROM table
Что касается эффективности, это позволяет избежать двукратного поиска индекса / сканирования таблицы / чего угодно, включая результаты в одну строку. Если вам абсолютно необходимы 2 строки в результате, два прохода по набору могут быть неизбежны из-за объединения агрегатов.
Надеюсь это поможет
Select Sum(Case When processed_timestamp IS NULL
Then 1
Else 0
End) not_processed_count,
Sum(Case When processed_timestamp Is Not NULL
Then 1
Else 0
End) processed_count,
Count(1) total
From table
Обновлено: не читал внимательно, этот возвращает одну строку.
В Oracle
SELECT COUNT(*), COUNT(TIME_STAMP_COLUMN)
FROM TABLE;
count (*) возвращает количество всех строк
count (column_name) возвращает количество строк, которые не равны NULL, поэтому
SELECT COUNT(*) - COUNT(TIME_STAMP_COLUMN) NUL_COUNT,
COUNT(TIME_STAMP_COLUMN) NON_NUL_COUNT
FROM TABLE
должен делать свою работу.
Если столбец проиндексирован, вы можете выполнить какое-то сканирование диапазона и избежать фактического чтения таблицы.
Если в вашей базе данных есть эффективная функция COUNT (*) для таблицы, вы можете COUNT, в зависимости от того, какое из них меньше, и вычесть.
SQL Server (начиная с 2012 г.):
SELECT IIF(ISDATE(processed_timestamp) = 0, 'NULL', 'NON NULL'), COUNT(*)
FROM MyTable
GROUP BY ISDATE(processed_timestamp);
Другой способ в T-sql (sql-server)
select count(case when t.timestamps is null
then 1
else null end) NULLROWS,
count(case when t.timestamps is not null
then 1
else null end) NOTNULLROWS
from myTable t
Это потрясающе, мне нужно еще немного поиграть с условным условием IF