Tính tổng SUM (cột)


9

Tôi có mã này tổng hợp số lượng cho một mặt hàng nhất định ( itemid) và theo mã ngày sản phẩm của nó ( proddte).

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

Những gì tôi muốn làm là để có được tổng số tất cả qtybất kể itemid/proddte. Tôi đã thử:

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

Nhưng nó nói tôi cũng nên có qtytrong group bymệnh đề. Nếu tôi làm điều đó, kết quả sẽ không bằng kết quả mong đợi của tôi.

Nó không hoàn toàn cần phải được biểu diễn dưới dạng một cột riêng biệt, với cùng một giá trị trong mỗi hàng. Bất kỳ đại diện nào được chấp nhận miễn là tôi có thể hiển thị tổng thể.

Câu trả lời:


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;

Các GROUP BY GROUPING SETSlà về cơ bản là một UNION ALL. Các ()phương tiện chỉ cần thực hiện SUMbất kể nhóm nào, bất kỳ nhóm nào khác được liệt kê sẽ được tổng hợp riêng. Hãy thử GROUP BY GROUPING SETS ((itemid),(itemid,proddte))để thấy sự khác biệt.

Để biết thêm chi tiết xem tài liệu:

Sử dụng NHÓM THEO với ROLLUP, CUBE và TẬP ĐOÀN NHÓM

Như Andriy đã đề cập, truy vấn trên cũng có thể được viết bằng cách sử dụng:

GROUP BY ROLLUP( (itemid,proddte) )

Lưu ý hai cột được đặt trong một cặp dấu ngoặc đơn bổ sung, biến chúng thành một đơn vị. Andriy đã viết một bản demo được lưu trữ trên Stack Exchange Data Explorer.


1
@niq: GROUP BY ROLLUP((itemid,proddte))sẽ tạo ra kết quả tương tự và có thể ít gây nhầm lẫn hơn.
Andriy M

@AndriyM không tương đương vì nó sẽ bao gồm tổng số phụ cho itemidIe Nó tương đương vớiGROUP BY GROUPING SETS((),(itemid),(itemid,proddte))
Martin Smith

4
@MartinSmith: Không, các cột được đặt trong một cặp dấu ngoặc bổ sung, làm cho chúng trở thành một đơn vị. GROUP BY ROLLUP(itemid,proddte)mặt khác, thực sự sẽ tạo ra (bổ sung) tổng phụ trên itemid(giống như GROUP BY ROLLUP((itemid),(proddte))). Bản demo tại SEDE
Andriy M

3
@AndriyM Tôi đứng sửa. Mặc dù điều đó làm suy yếu điểm "ít gây nhầm lẫn" bởi vì nó đã gây nhầm lẫn cho ít nhất một người :-)
Martin Smith

2
@AndriyM Tôi không thấy GROUP BY ROLLUPkhó hiểu, nhưng nó khá chủ quan. Tôi cũng luôn cảm thấy lo lắng khi đọc những thứ như The non-ISO compliant WITH ROLLUP, WITH CUBE, and ALL syntax is deprecated- tại sao tôi có xu hướng ủng hộ GROUPING SETS.
Aaron Bertrand

10

Đây cũng là cú pháp hợp lệ:

       sum(sum(qty)) over ()

Hơi khó hiểu khi người ta nhìn thấy nó lúc đầu nhưng bạn chỉ cần nhớ rằng các chức năng của cửa sổ - ví dụ sum() over ()- được áp dụng sau khi group bymọi thứ có thể xuất hiện trong danh sách chọn của một nhóm theo truy vấn có thể được đặt trong tổng hợp cửa sổ. Vì vậy ( qtykhông thể nhưng) sum(qty)có thể được đặt bên trong sum() over ():

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

Phải nói rằng, tôi thích GROUPING SETStruy vấn được cung cấp bởi Aaron Bertrand. Tổng số cần phải được hiển thị một lần và không phải trong mỗi hàng.

Cũng lưu ý rằng mặc dù tổng số tiền có thể được sử dụng để tính tổng, nhưng nếu bạn muốn tổng số đếm, bạn phải sử dụng tổng số đếm (chứ không phải tổng số đếm!):

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

Và nếu một người muốn trung bình trên tất cả các bảng, nó sẽ còn phức tạp hơn nữa:

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

bởi vì trung bình của trung bình không giống với trung bình trên tất cả. (Nếu bạn thử, avg(avg(qty)) over ()bạn sẽ thấy rằng nó có thể mang lại kết quả khác với mức trung bình lớn ở trên.)


3

Một cách có thể là bọc đầu tiên GROUP BYvào 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
Không cần CTE như câu trả lời của ypercube minh họa
Martin Smith

1
@MartinSmith, bạn nói đúng. Bất kỳ CTE không đệ quy có thể được viết lại dưới dạng một truy vấn con của một số hình thức. Trình tối ưu hóa của SQL Server dù sao cũng sẽ xử lý các CTE (trái ngược với Postgres), vì vậy kế hoạch thực hiện là giống với CTE hoặc không. Tuy nhiên, khá thường xuyên, việc đọc và hiểu các truy vấn phức tạp sẽ dễ dàng hơn nếu chúng được chia thành các phần đơn giản hơn bằng CTE. Ít nhất là đối với tôi.
Vladimir Baranov

3
Tôi nghĩ rằng bạn đã hiểu sai quan điểm của Martin, mặc dù quan điểm của bạn về CTE thêm khả năng đọc vẫn có thể đứng vững. Điều gợi ý của ypercube cho thấy là bạn có thể tránh truy vấn con dưới mọi hình thức trong trường hợp này, cho dù là CTE, bảng dẫn xuất hoặc cột được tính như là truy vấn tổng hợp vô hướng.
Andriy M

1
@AndriyM, tôi thích biến thể từ câu trả lời của ypercube và tôi đã không nghĩ về cú pháp như vậy trước khi tôi thấy nó ở đây. Nó luôn luôn tốt để học một cái gì đó mới. Bạn nói đúng, điểm chính của tôi sôi sùng sục đến dễ đọc. Trong các thử nghiệm của tôi, trình tối ưu hóa đã tạo ra cùng một kế hoạch thực hiện, với CTE hoặc không.
Vladimir Baranov
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.