SUM(列)の合計を計算します


9

特定のアイテムの数量(itemid)と製品の日付コード(proddte)を合計するこのコードがあります。

select sum(qty), itemid, proddte 
from testtable where .... 
group by itemid, proddte

私がやりたいのは、にqty関係なくすべての合計を取得することですitemid/proddte。私が試してみました:

select sum(qty), itemid, proddte, sum(qty) over() as grandtotal 
from testtable 
where .... 
group by itemid, proddte

しかし、それは私も持っていなければならないと言うqtygroup by句。そうした場合、結果は期待した結果とは異なります。

すべての行が同じ値で、個別の列として表される必要はありません。全体の合計を表示できる限り、どのような表現でも受け入れられます。

回答:


9
CREATE TABLE #foo
(
 itemid int, 
 proddte date,
 qty int
);

INSERT #foo(itemid,proddte,qty) VALUES
(1,'20140101',5),(1,'20140102',7),(2,'20150101',10);


-- if it really needs to be a column with the same value
-- in every row, just calculate once and assign it to a variable

DECLARE @sum int = (SELECT SUM(qty) FROM #foo);

SELECT itemid, proddte, GroupedSum = SUM(qty), GrandTotal = @sum
  FROM #foo
  GROUP BY itemid, proddte;

-- if the grand total can be expressed on its own row, 
-- you can use GROUP BY GROUPING SETS:
SELECT itemid, proddte, SUM(qty)
  FROM #foo GROUP BY GROUPING SETS((),(itemid,proddte));

-- if that syntax is confusing, you can use a less
-- efficient UNION ALL:
SELECT itemid, proddte, SUM(qty)
  FROM #foo GROUP BY itemid,proddte
UNION ALL
SELECT NULL, NULL, SUM(qty) 
  FROM #foo;

GO
DROP TABLE #foo;

GROUP BY GROUPING SETSある基本的にUNION ALL()手段は、単に取るSUMに関係なく、列挙された任意の他のグループが別々に集約されますグループ化します。GROUP BY GROUPING SETS ((itemid),(itemid,proddte))違いを確認してみてください。

詳細については、ドキュメントを参照してください。

ROLLUP、CUBE、およびGROUPING SETSでのGROUP BYの使用

Andriyが述べたように、上記のクエリは次のように記述することもできます。

GROUP BY ROLLUP( (itemid,proddte) )

そこにある2つの列は、追加の括弧のペアで囲まれているため、1つの単位になっています。Andriy Stack Exchange Data Explorerでホストされているデモを書きました


1
@niq:GROUP BY ROLLUP((itemid,proddte))同じ結果が生成され、混乱が少なくなる可能性があります。
Andriy M

Ieの小計が含まれるため、同等ではない@AndriyM itemidこれは同等ですGROUP BY GROUPING SETS((),(itemid),(itemid,proddte))
Martin Smith

4
@MartinSmith:いいえ、列は追加の括弧のペアで囲まれているため、1つの単位になります。GROUP BY ROLLUP(itemid,proddte)一方、は、実際に(追加の)小計を生成しますitemid(と同じGROUP BY ROLLUP((itemid),(proddte)))。SEDEでのデモ
Andriy M

3
@AndriyM私は修正された立場です。それは、少なくとも1人:-)混同するために管理しているため、それは「あまり混乱」のポイントを損なうんが
マーティン・スミスの

2
@AndriyM私はGROUP BY ROLLUPそれほど混乱しないと思いますが、それはむしろ主観的です。また、次のようなものを読むといつも緊張The non-ISO compliant WITH ROLLUP, WITH CUBE, and ALL syntax is deprecatedGROUPING SETSます。
アーロンバートランド

10

これも有効な構文です:

       sum(sum(qty)) over ()

最初にそれを見ると少し混乱しますが、ウィンドウ関数-例えばsum() over ()-がの後に適用されることを覚えておかなければならないgroup byので、クエリによってグループの選択リストに表示される可能性のあるすべてのものは、ウィンドウ集合体内に配置できます。だから(qtyできませんが)sum(qty)内部に置くことができますsum() over ()

select sum(qty), itemid, proddte, 
       sum(sum(qty)) over () as grandtotal  
from testtable 
where .... 
group by itemid, proddte ;

そうは言っても、私はGROUPING SETSAaron Bertrandによって提供されたクエリを好みます。合計をすべての行に表示するのではなく、一度表示する必要があります。

また、合計の合計は合計の計算に使用できますが、合計数が必要な場合は、カウントの合計ではなく合計数を使用する必要があります。

sum(count(*)) over ()  as grand_count

また、テーブル全体の平均が必要な場合は、さらに複雑になります。

sum(sum(qty)) over ()
/ sum(count(qty)) over ()  as grand_average

平均の平均がすべての平均と同じではないためです。(試してみるとavg(avg(qty)) over ()、上記の総平均とは異なる結果になる場合があります。)


3

回避策の1つは、最初のものGROUP BYCTEにラップすることです。

WITH
CTE
AS
(
    select
        itemid
        ,proddte
        ,sum(qty) AS SumQty
    from testtable 
    where .... 
    group by itemid, proddte
)
SELECT
    itemid
    ,proddte
    ,SumQty
    ,SUM(SumQty) OVER () AS grandtotal
FROM CTE
;

3
ypercubeの回答が示すように、CTEは必要ありません
Martin Smith

1
@MartinSmith、あなたは正しいです。非再帰的なCTEは、何らかの形式のサブクエリとして書き直すことができます。SQL Serverのオプティマイザは、とにかく(たとえば、Postgresとは対照的に)CTEをインライン化するため、実行計画はCTEの有無にかかわらず同じです。ただし、CTEを使用して複雑なクエリをより単純な部分に分割すると、複雑なクエリを読んで理解することがかなり容易になります。少なくとも私にとっては。
Vladimir Baranov 2016年

3
マーティンの見解を誤解していると思いますが、CTEが読みやすさを追加するというあなたの見解は依然として有効です。ypercubeの提案が示しているのは、CTE、派生テーブル、または計算列がスカラー集計サブクエリであって、この場合は任意の形式のサブクエリを回避できるということです。
Andriy M

1
@AndriyM、私はypercubeの回答のバリアントが好きで、ここで見る前にそのような構文については考えていませんでした。何か新しいことを学ぶことは常に良いことです。正解です。私の要点は、読みやすさです。私のテストでは、オプティマイザはCTEの有無にかかわらず同じ実行プランを生成しました。
Vladimir Baranov 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.