Bối cảnh:Máy chủ SQL - tập hợp có điều kiện với tương quan
original case rất đơn giản. Tính chạy tổng cho mỗi người dùng từ doanh thu cao nhất đến thấp nhất:
CREATE TABLE t(Customer INTEGER NOT NULL PRIMARY KEY
,"User" VARCHAR(5) NOT NULL
,Revenue INTEGER NOT NULL);
INSERT INTO t(Customer,"User",Revenue) VALUES
(001,'James',500),(002,'James',750),(003,'James',450),
(004,'Sarah',100),(005,'Sarah',500),(006,'Sarah',150),
(007,'Sarah',600),(008,'James',150),(009,'James',100);
Query:
SELECT *,
1.0 * Revenue/SUM(Revenue) OVER(PARTITION BY "User") AS percentage,
1.0 * SUM(Revenue) OVER(PARTITION BY "User" ORDER BY Revenue DESC)
/SUM(Revenue) OVER(PARTITION BY "User") AS running_percentage
FROM t;
Output:
╔════╦═══════╦═════════╦════════════╦════════════════════╗
║ ID ║ User ║ Revenue ║ percentage ║ running_percentage ║
╠════╬═══════╬═════════╬════════════╬════════════════════╣
║ 2 ║ James ║ 750 ║ 0.38 ║ 0.38 ║
║ 1 ║ James ║ 500 ║ 0.26 ║ 0.64 ║
║ 3 ║ James ║ 450 ║ 0.23 ║ 0.87 ║
║ 8 ║ James ║ 150 ║ 0.08 ║ 0.95 ║
║ 9 ║ James ║ 100 ║ 0.05 ║ 1 ║
║ 7 ║ Sarah ║ 600 ║ 0.44 ║ 0.44 ║
║ 5 ║ Sarah ║ 500 ║ 0.37 ║ 0.81 ║
║ 6 ║ Sarah ║ 150 ║ 0.11 ║ 0.93 ║
║ 4 ║ Sarah ║ 100 ║ 0.07 ║ 1 ║
╚════╩═══════╩═════════╩════════════╩════════════════════╝
Nó có thể được tính toán khác nhau bằng cách sử dụng các chức năng cửa sổ cụ thể.
Bây giờ chúng ta hãy giả sử rằng chúng ta không thể sử dụng cửa sổ SUM
và viết lại nó:
SELECT c.Customer, c."User", c."Revenue"
,1.0 * Revenue/NULLIF(c3.s,0) AS percentage
,1.0 * c2.s /NULLIF(c3.s,0) AS running_percentage
FROM t c
CROSS APPLY
(SELECT SUM(Revenue) AS s
FROM t c2
WHERE c."User" = c2."User"
AND c2.Revenue >= c.Revenue) AS c2
CROSS APPLY
(SELECT SUM(Revenue) AS s
FROM t c2
WHERE c."User" = c2."User") AS c3
ORDER BY "User", Revenue DESC;
Tôi đã sử dụng CROSS APPLY
bởi vì tôi không muốn truy vấn con tương quan trong SELECT
colums danh sách và c3
được sử dụng hai lần.
Mọi thứ hoạt động như mong muốn. Nhưng khi chúng ta nhìn gần hơn c2
và c3
thì rất giống nhau. Vì vậy, tại sao không kết hợp chúng và sử dụng tổng hợp có điều kiện đơn giản:
SELECT c.Customer, c."User", c."Revenue"
,1.0 * Revenue /NULLIF(c2.sum_total,0) AS percentage
,1.0 * c2.sum_running/NULLIF(c2.sum_total,0) AS running_percentage
FROM t c
CROSS APPLY
(SELECT SUM(Revenue) AS sum_total,
SUM(CASE WHEN c2.Revenue >= c.Revenue THEN Revenue ELSE 0 END)
AS sum_running
FROM t c2
WHERE c."User" = c2."User") AS c2
ORDER BY "User", Revenue DESC;
Thật không may là không thể.
Nhiều cột được chỉ định trong biểu thức tổng hợp chứa tham chiếu ngoài. Nếu một biểu thức được tổng hợp chứa tham chiếu ngoài, thì tham chiếu ngoài đó phải là cột duy nhất được tham chiếu trong biểu thức.
Dĩ nhiên tôi có thể phá vỡ nó gói với subquery khác, nhưng nó trở nên một chút "xấu xí":
SELECT c.Customer, c."User", c."Revenue"
,1.0 * Revenue /NULLIF(c2.sum_total,0) AS percentage
,1.0 * c2.sum_running/NULLIF(c2.sum_total,0) AS running_percentage
FROM t c
CROSS APPLY
( SELECT SUM(Revenue) AS sum_total,
SUM(running_revenue) AS sum_running
FROM (SELECT Revenue,
CASE WHEN c2.Revenue >= c.Revenue THEN Revenue ELSE 0 END
AS running_revenue
FROM t c2
WHERE c."User" = c2."User") AS sub
) AS c2
ORDER BY "User", Revenue DESC
Postgresql
phiên bản. Sự khác biệt duy nhất là LATERAL
thay vì CROSS APPLY
.
SELECT c.Customer, c."User", c.Revenue
,1.0 * Revenue /NULLIF(c2.sum_total,0) AS percentage
,1.0 * c2.running_sum/NULLIF(c2.sum_total,0) AS running_percentage
FROM t c
,LATERAL (SELECT SUM(Revenue) AS sum_total,
SUM(CASE WHEN c2.Revenue >= c.Revenue THEN c2.Revenue ELSE 0 END)
AS running_sum
FROM t c2
WHERE c."User" = c2."User") c2
ORDER BY "User", Revenue DESC;
Nó hoạt động rất tốt đẹp.
SQLite
/MySQL
phiên bản (có nghĩa là lý do tại sao tôi thích LATERAL/CROSS APPLY
):
SELECT c.Customer, c."User", c.Revenue,
1.0 * Revenue/(SELECT SUM(Revenue)
FROM t c2
WHERE c."User" = c2."User") AS percentage,
1.0 * (SELECT SUM(CASE WHEN c2.Revenue >= c.Revenue THEN c2.Revenue ELSE 0 END)
FROM t c2
WHERE c."User" = c2."User")/
(SELECT SUM(c2.Revenue)
FROM t c2
WHERE c."User" = c2."User") AS running_percentage
FROM t c
ORDER BY "User", Revenue DESC;
SQLFiddleDemo-SQLite
SQLFiddleDemo-MySQL
Tôi đã đọc Aggregates with an Outer Reference:
Các nguồn cho những hạn chế là trong tiêu chuẩn
SQL-92
vàSQL Server
thừa hưởng nó từSybase
codebase. Vấn đề là SQL Server cần phải tìm ra truy vấn nào sẽ tính tổng hợp.
Tôi không tìm kiếm câu trả lời rằng chỉ hiển thị cách phá vỡ nó.
Các câu hỏi là:
- Phần nào của disallow tiêu chuẩn hoặc gây trở ngại với nó?
- Tại sao các RDBMS khác không có vấn đề với loại phụ thuộc bên ngoài này?
- Họ có mở rộng
SQL Standard
vàSQL Server
hoạt động bình thường hoặcSQL Server
không triển khai đầy đủ (đúng không?) ?.
tôi sẽ rất biết ơn về tài liệu tham khảo để:
ISO standard
(92 hoặc mới hơn)- SQL Server Standards Support
- documenation chính thức từ bất kỳ RDBMS giải thích nó (
SQL Server/Postgresql/Oracle/...
).
EDIT:
Tôi biết rằng SQL-92
không có khái niệm về LATERAL
. Nhưng phiên bản với các truy vấn phụ (như trong SQLite/MySQL
) cũng không hoạt động.
EDIT 2:
Để đơn giản hóa nó một chút, chúng ta hãy kiểm tra chỉ tương quan chỉ subquery:
SELECT c.Customer, c."User", c.Revenue,
1.0*(SELECT SUM(CASE WHEN c2.Revenue >= c.Revenue THEN c2.Revenue ELSE 0 END)
FROM t c2
WHERE c."User" = c2."User")
/(SELECT SUM(c2.Revenue)
FROM t c2
WHERE c."User" = c2."User") AS running_percentage
FROM t c
ORDER BY "User", Revenue DESC;
Phiên bản trên hoạt động tốt trong MySQL/SQLite/Postgresql
.
Trong SQL Server
chúng tôi gặp lỗi. Sau wraping nó với subquery để "san bằng" nó đến một mức độ hoạt động:
SELECT c.Customer, c."User", c.Revenue,
1.0 * (
SELECT SUM(CASE WHEN r1 >= r2 THEN r1 ELSE 0 END)
FROM (SELECT c2.Revenue AS r1, c.Revenue r2
FROM t c2
WHERE c."User" = c2."User") AS S)/
(SELECT SUM(c2.Revenue)
FROM t c2
WHERE c."User" = c2."User") AS running_percentage
FROM t c
ORDER BY "User", Revenue DESC;
Mấu chốt của câu hỏi này là như thế nào SQL standard
điều chỉnh nó.
Yup hoặc sử dụng 'CROSS APPLY' ** [Demo] (http://rextester.com/LOBM67950) **. Nó "ẩn" truy vấn phụ. – lad2025
'(GIÁ TRỊ (c.REVENUE)) x (REVENUE)' làm một công việc tốt hơn, theo ý kiến của tôi. –
Đường cú pháp nó phụ thuộc vào hương vị :) Lưu ý đầu tiên là đúng, 'LATERAL' (cũng là loại đường cú pháp) là khái niệm mới hơn. – lad2025