Как да преброя отделна стойност със сборен пакет?

Имам таблица, която съдържа дневник за мрежови инциденти. Всеки инцидент е прикрепен към сайт и отдел.

Използвам заявка за агрегиране, за да обобщя всички мои инциденти (общо време на престой, ...) и използвам функцията 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 приключи на 8.4.2008 г., а разширената поддръжка приключи на 9.4.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