Hitung total SUM (kolom)


9

Saya memiliki kode ini yang merangkum jumlah untuk item tertentu ( itemid) dan kode tanggal produknya ( proddte).

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

Yang ingin saya lakukan adalah mendapatkan total semua qtyterlepas dari itemid/proddte. Saya telah mencoba:

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

Tapi katanya saya juga harus ada qtydalam group byklausa. Jika saya melakukan itu, hasilnya tidak akan sama dengan hasil yang saya harapkan.

Itu tidak benar-benar perlu direpresentasikan sebagai kolom terpisah, dengan nilai yang sama di setiap baris. Representasi apa pun diterima selama saya dapat menampilkan total keseluruhan.

Jawaban:


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;

Pada GROUP BY GROUPING SETSdasarnya adalah a UNION ALL. Cara ()hanya mengambil SUMterlepas dari pengelompokan, setiap grup lain yang terdaftar akan dikumpulkan secara terpisah. Coba GROUP BY GROUPING SETS ((itemid),(itemid,proddte))lihat perbedaannya.

Untuk lebih jelasnya lihat dokumentasi:

Menggunakan GROUP BY dengan ROLLUP, CUBE, dan SET GROUPING

Seperti yang disebutkan Andriy, permintaan di atas juga dapat ditulis menggunakan:

GROUP BY ROLLUP( (itemid,proddte) )

Perhatikan dua kolom yang ada dalam pasangan kurung tambahan, menjadikannya satu unit. Andriy menulis demo yang diselenggarakan di Stack Exchange Data Explorer.


1
@niq: GROUP BY ROLLUP((itemid,proddte))akan menghasilkan hasil yang sama dan mungkin kurang membingungkan.
Andriy M

@AndriyM yang tidak setara karena akan mencakup sub total untuk itemidIe Ini setara denganGROUP BY GROUPING SETS((),(itemid),(itemid,proddte))
Martin Smith

4
@ MartinSmith: Tidak, kolom-kolom tersebut diapit dengan tanda kurung tambahan, yang menjadikannya satu unit. GROUP BY ROLLUP(itemid,proddte), di sisi lain, memang akan menghasilkan subtotal (tambahan) pada itemid(sama seperti GROUP BY ROLLUP((itemid),(proddte))). Demo di SEDE
Andriy M

3
@ AndriyM Saya berdiri dikoreksi. Meskipun itu merusak titik "kurang membingungkan" karena berhasil membingungkan setidaknya satu orang :-)
Martin Smith

2
@AndriyM Saya pikir tidak GROUP BY ROLLUPterlalu membingungkan, tapi agak subyektif. Saya juga selalu gugup ketika membaca hal-hal seperti The non-ISO compliant WITH ROLLUP, WITH CUBE, and ALL syntax is deprecated- mengapa saya cenderung menyukai GROUPING SETS.
Aaron Bertrand

10

Ini juga sintaks yang valid:

       sum(sum(qty)) over ()

Agak membingungkan ketika orang melihatnya pada awalnya tetapi Anda hanya perlu mengingat bahwa fungsi jendela - misalnya sum() over ()- diterapkan setelah group bysehingga segala sesuatu yang dapat muncul dalam daftar pilih grup berdasarkan kueri dapat ditempatkan di dalam agregat jendela. Jadi ( qtytidak bisa tetapi) sum(qty)dapat ditempatkan di dalam sum() over ():

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

Karena itu, saya lebih suka GROUPING SETSpermintaan yang disediakan oleh Aaron Bertrand. Jumlah total perlu ditampilkan sekali dan tidak di setiap baris.

Perhatikan juga bahwa sementara jumlah jumlah dapat digunakan untuk menghitung jumlah total, jika Anda menginginkan jumlah total, Anda harus menggunakan jumlah jumlah (dan bukan jumlah hitungan!):

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

Dan jika seseorang menginginkan rata-rata dari semua tabel, itu akan menjadi lebih rumit:

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

karena rata-rata rata-rata tidak sama dengan rata-rata di atas semua. (Jika Anda mencoba, avg(avg(qty)) over ()Anda akan melihat bahwa itu dapat menghasilkan hasil yang berbeda dari rata-rata grand di atas.)


3

Salah satu cara yang mungkin dilakukan adalah memasukkan yang pertama GROUP BYke dalam CTE :

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
Tidak perlu CTE seperti yang digambarkan oleh jawaban ypercube
Martin Smith

1
@ MartinSmith, Anda benar. Setiap CTE non-rekursif dapat ditulis ulang sebagai subquery dari beberapa bentuk. Pengoptimal SQL Server tetap memberikan CTE (sebagai lawan Postgres, misalnya), sehingga rencana eksekusi sama dengan CTE atau tanpa CTE. Namun, cukup sering, lebih mudah untuk membaca dan memahami pertanyaan kompleks jika dipecah menjadi bagian-bagian yang lebih sederhana menggunakan CTE. Setidaknya untuk saya.
Vladimir Baranov

3
Saya pikir Anda salah mengerti poin Martin, meskipun poin Anda tentang CTE menambah keterbacaan mungkin masih berlaku. Apa yang ditunjukkan saran ypercube adalah bahwa Anda dapat menghindari subquery dalam bentuk apa pun dalam kasus ini, apakah CTE, tabel turunan, atau kolom terhitung sebagai subquery agregasi skalar.
Andriy M

1
@AndriyM, saya suka varian dari jawaban ypercube dan saya tidak memikirkan sintaksis seperti itu sebelum saya melihatnya di sini. Itu selalu baik untuk mempelajari sesuatu yang baru. Anda benar, poin utama saya adalah keterbacaan. Dalam pengujian saya, pengoptimal menghasilkan rencana eksekusi yang sama, dengan CTE atau tanpa.
Vladimir Baranov
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.