0

ROLLUP を使用すると、複数の単純な SELECT ステートメントを UNION したかのように、複数レベルのグループ化を集計できます。

しかし、ネストされた SELECT ステートメントまたは相互に依存する CTE のチェーンを使用したかのように、下位レベルのグループ化の結果を集計できるようにしたいと考えています。

たとえば、下位レベルのグループ化からグループの数を数えたり、下位レベルの合計の平均または下位レベルの最大値の最小値を計算したりしたいなど

より具体的な例: 米国での各自動車事故の記録があり、ROLLUP(州、郡、市、郵便番号) の各レベルでの事故の数だけでなく、人の数も取得したい場合 (明らかに各人は複数の事故に巻き込まれ、その結果、複数の記録)。

ROLLUP でそれを達成することは可能ですか? 可能であれば、どのように?

結果を含む SQL の例:

if object_id('accident') is not null drop table accident
create table accident(
     id int identity(1,1)
    ,state varchar(50)
    ,city varchar(50)
    ,zip varchar(50)
    ,person varchar(50)
)

insert accident(state,city,zip,person)values
 ('NY','Manhattan',10001,'John')
,('NY','Manhattan',10001,'John')
,('NY','Manhattan',10001,'Barbara')

select
    state,city,zip,person
    ,accidents=count(1)
    -- the following line causes error: Windowed functions cannot be used in the context of another windowed function or aggregate.
    --,people=sum(case when row_number()over(partition by person order by (select 0))=1 then 1 else 0 end)
from accident
group by rollup(state,city,zip,person)

;with person as (select state,city,zip,person from accident group by state,city,zip,person)
    select
        state,city,zip
        ,people=count(1)
    from person
    group by rollup(state,city,zip)

結果:

state   city    zip person  accidents
NY  Manhattan   10001   Barbara 1
NY  Manhattan   10001   John    2
NY  Manhattan   10001   NULL    3
NY  Manhattan   NULL    NULL    3
NY  NULL    NULL    NULL    3
NULL    NULL    NULL    NULL    3


state   city    zip people
NY  Manhattan   10001   2
NY  Manhattan   NULL    2
NY  NULL    NULL    2
NULL    NULL    NULL    2

最初の結果はレベルごとに 3 つの事故を返し、2 番目の結果は 2 を返します。1 つの ROLLUP クエリで 3 と 2 の両方を取得したい場合。私の問題は、ウィンドウ関数をネストできないことです。

私が今尋ねたことは、次のクエリで実現できます。

;with person as (select state,city,zip,person,accidents=count(1) from accident group by state,city,zip,person)
        select
            state,city,zip
            ,accidents=sum(accidents)
            ,people=count(1)
        from person
        group by rollup(state,city,zip)

state   city    zip accidents   people
NY  Manhattan   10001   3   2
NY  Manhattan   NULL    3   2
NY  NULL    NULL    3   2
NULL    NULL    NULL    3   2

ただし、その方法では、各レベルの CTE を明示的に記述する必要があります。

グループ化レベルの数に関係なく、下位レベルのグループ化の結果にアクセスできる 1 つのクエリを記述できるようにしたいと考えています。

これを試しました:

;with
    lvl as (
            select *
                    ,lvl = -1
                    ,accidents=1
                    ,people=1
                from accident
            union all
            select accident.*
                    ,lvl = grouping_id(accident.state,accident.city,accident.zip,accident.person)
                    ,accidents=sum(accidents)
                    ,people=count(1)
                from accident
                join lvl prev on prev.lvl = (grouping_id(accident.state,accident.city,accident.zip,accident.person)+1)/2-1
                group by rollup(accident.state,accident.city,accident.zip,accident.person)
        )
        select * from lvl

しかし、エラーが発生しました:

Msg 1015, Level 15, State 1, ...
An aggregate cannot appear in an ON clause unless it is in a subquery contained in a HAVING clause or select list, and the column being aggregated is an outer reference.
Msg 467, Level 16, State 1, ...
GROUP BY, HAVING, or aggregate functions are not allowed in the recursive part of a recursive common table expression 'lvl'.

関連する質問:ロールアップ ロジックを使用した再帰的な SQL 関数?

4

0 に答える 0