Как подсчитать отдельное значение с помощью свертки?

У меня есть таблица, содержащая журнал сетевых инцидентов. Каждый инцидент привязан к сайту и отделу.

Я использую запрос агрегации, чтобы суммировать все свои инциденты (общее время простоя, ...), и я использую функцию ROLLUP:

SELECT i.annee, i.mois, i.siteId, count(i.id), sum(i.downtime)
FROM incident i
GROUP BY i.annee, i.mois, i.siteId, i.id
WITH ROLLUP

Я группирую свои инциденты по местам, но я хотел бы добавить столбец, в котором для каждой строки подсчитывается количество различных отделов, связанных с этими инцидентами.

Поэтому я должен добавить поле: COUNT(DISTINCT i.department), но я слышал, что оно несовместимо с ROLLUP function

Знаете ли вы обходной путь к этому?


person Fractaliste    schedule 16.05.2014    source источник
comment
Это действительно использует SQL 2000? Стандартная основная поддержка SQL 2000 закончилась 08.04.2008, а расширенная поддержка закончилась 09.04.2013. Вам действительно следует проверить обновление до более новой версии SQL Server.   -  person BateTech    schedule 16.05.2014
comment
Да, мне нужно использовать SQL 2000. На данный момент миграция не планируется. @BateTech   -  person Fractaliste    schedule 16.05.2014
comment
Вы имели в виду i.id в group by? Похоже, вы этого не сделали, поскольку пытаетесь выполнить агрегат.   -  person BateTech    schedule 16.05.2014
comment
@BateTech . . . Это трюк, который вы можете использовать, когда хотите поместить итоговый столбец данных в таблицу.   -  person Gordon Linoff    schedule 16.05.2014


Ответы (1)


Вот 3 разных варианта. У меня нет SQL 2000 для проверки, но это должно работать. Каждый вариант может работать по-разному, в зависимости от размера таблицы и структуры данных.

ПРИМЕЧАНИЕ. Для всех, кто видит этот ответ, если вы используете SQL Server 2008 R2 или более позднюю версию, COUNT DISTINCT работает даже при использовании ROLLUP. Это может работать с SQL 2005, но у меня нет экземпляра SQL 05 для тестирования.

Использование подзапроса:

SELECT t.annee, t.mois, t.siteId, t.id, t.cnt, t.downtime
    , (
        SELECT count(distinct i2.department) 
        FROM incident i2 
        WHERE (i2.annee = t.annee 
                    or i2.annee is null and t.annee is null and t.isgroup_annee = 0 
                    or t.isgroup_annee = 1)
            and (i2.mois = t.mois 
                    or i2.mois is null and t.mois is null and t.isgroup_mois = 0 
                    or t.isgroup_mois = 1)
            and (i2.siteId = t.siteId 
                    or i2.siteId is null and t.siteId is null and t.isgroup_siteId = 0  
                    or t.isgroup_siteId = 1)
            and (i2.id = t.id 
                    OR i2.id is null and t.id is null and t.isgroup_id = 0 
                    OR t.isgroup_id = 1)
        ) as departmentCnt
FROM (
    SELECT i.annee, i.mois, i.siteId, i.id, count(i.id) as cnt, sum(i.downtime) as downtime 
        , grouping(i.annee) AS isgroup_annee
        , grouping(i.mois) AS isgroup_mois
        , grouping(i.siteId) AS isgroup_siteId
        , grouping(i.id) AS isgroup_Id
    FROM incident i
    GROUP BY i.annee, i.mois, i.siteId, i.id
    WITH ROLLUP
) t
;

Использование соединения:

SELECT t.annee, t.mois, t.siteId, t.cnt, t.downtime
    , count(distinct i2.department) as departmentCnt
FROM (
    SELECT i.annee, i.mois, i.siteId, i.id, count(i.id) as cnt, sum(i.downtime) as downtime 
        , grouping(i.annee) AS isgroup_annee
        , grouping(i.mois) AS isgroup_mois
        , grouping(i.siteId) AS isgroup_siteId
        , grouping(i.id) AS isgroup_Id
    FROM incident i
    GROUP BY i.annee, i.mois, i.siteId, i.id
    WITH ROLLUP
) t
LEFT JOIN incident i2 
    ON (i2.annee = t.annee 
            or i2.annee is null and t.annee is null and t.isgroup_annee = 0 
            or t.isgroup_annee = 1)
    and (i2.mois = t.mois 
            or i2.mois is null and t.mois is null and t.isgroup_mois = 0 
            or t.isgroup_mois = 1)
    and (i2.siteId = t.siteId 
            or i2.siteId is null and t.siteId is null and t.isgroup_siteId = 0  
            or t.isgroup_siteId = 1)
    and (i2.id = t.id 
            OR i2.id is null and t.id is null and t.isgroup_id = 0 
            OR t.isgroup_id = 1)
GROUP BY t.annee, t.mois, t.siteId, t.id, t.cnt, t.downtime, t.isgroup_annee, t.isgroup_mois, t.isgroup_siteId, t.isgroup_Id
;

используя UNIONs для выполнения ROLLUP вручную:

SELECT i.annee, i.mois, i.siteId, i.id, count(i.id) as cnt, sum(i.downtime) as downtime, count(distinct i.department) as departmentCnt
FROM incident i
GROUP BY i.annee, i.mois, i.siteId, i.id
UNION ALL
SELECT i.annee, i.mois, i.siteId, null as id, count(i.id) as cnt, sum(i.downtime) as downtime, count(distinct i.department) as departmentCnt
FROM incident i
GROUP BY i.annee, i.mois, i.siteId
UNION ALL
SELECT i.annee, i.mois, null as siteId, null as id, count(i.id) as cnt, sum(i.downtime) as downtime, count(distinct i.department) as departmentCnt
FROM incident i
GROUP BY i.annee, i.mois
UNION ALL
SELECT i.annee, null as mois, null as siteId, null as id, count(i.id) as cnt, sum(i.downtime) as downtime, count(distinct i.department) as departmentCnt
FROM incident i
GROUP BY i.annee
UNION ALL
SELECT null as annee, null as mois, null as siteId, null as id, count(i.id) as cnt, sum(i.downtime) as downtime, count(distinct i.department) as departmentCnt
FROM incident i
;
person BateTech    schedule 16.05.2014
comment
Кроме того, если ваши столбцы не обнуляемые, вы можете удалить строки, которые выглядят как or i2.annee is null and t.annee is null and t.isgroup_annee = 0 - person BateTech; 16.05.2014
comment
Я протестировал метод соединения, но на самом деле я упростил свой запрос, чтобы опубликовать его в Stackoverflow, и реальный запрос с методом соединения стал большим. Поэтому я сейчас тестирую его с помощью метода UNION. Спасибо за ваши советы - person Fractaliste; 19.05.2014