Sự khác biệt trong hai cách tiếp cận đầu tiên
Gói đầu tiên dành khoảng 7 trong 10 giây cho toán tử Window Spool, vì vậy đây là lý do chính khiến nó quá chậm. Nó thực hiện rất nhiều I / O trong tempdb để tạo ra cái này. Số liệu thống kê I / O và thời gian của tôi trông như thế này:
Table 'Worktable'. Scan count 1000001, logical reads 8461526
Table 'Table_1'. Scan count 1, logical reads 2609
Table 'Worktable'. Scan count 0, logical reads 0
SQL Server Execution Times:
CPU time = 8641 ms, elapsed time = 8537 ms.
Các kế hoạch thứ hai là có thể tránh được những ống chỉ, và do đó bàn làm việc hoàn toàn. Nó chỉ đơn giản là lấy 10 hàng trên cùng từ chỉ mục được nhóm, và sau đó một vòng lặp lồng nhau tham gia vào tổng hợp (tổng hợp) từ một lần quét chỉ mục được phân cụm. Phía bên trong vẫn kết thúc việc đọc toàn bộ bảng, nhưng bảng rất dày đặc, vì vậy điều này là hiệu quả hợp lý với một triệu hàng.
Table 'Table_1'. Scan count 11, logical reads 26093
SQL Server Execution Times:
CPU time = 1563 ms, elapsed time = 1671 ms.
Cải thiện hiệu suất
Nhà kho
Nếu bạn thực sự muốn phương pháp "báo cáo trực tuyến", cột cửa hàng có thể là lựa chọn tốt nhất của bạn.
ALTER TABLE [dbo].[Table_1] DROP CONSTRAINT [PK_Table_1];
CREATE CLUSTERED COLUMNSTORE INDEX [PK_Table_1] ON dbo.Table_1;
Sau đó, truy vấn này nhanh đến mức nực cười:
SELECT TOP 10
seq,
value,
SUM(value) OVER (ORDER BY seq ROWS UNBOUNDED PRECEDING)
FROM dbo.Table_1
ORDER BY seq DESC;
Dưới đây là số liệu thống kê từ máy của tôi:
Table 'Table_1'. Scan count 4, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 3319
Table 'Table_1'. Segment reads 1, segment skipped 0.
Table 'Worktable'. Scan count 0, logical reads 0
SQL Server Execution Times:
CPU time = 375 ms, elapsed time = 205 ms.
Bạn có thể sẽ không đánh bại điều đó (trừ khi bạn thực sự thông minh - người tốt bụng, Joe). Cột lưu trữ rất tốt trong việc quét và tổng hợp số lượng lớn dữ liệu.
Sử dụng ROW
thay vì RANGE
tùy chọn chức năng cửa sổ
Bạn có thể nhận được hiệu suất rất giống với truy vấn thứ hai của mình với cách tiếp cận này, được đề cập trong một câu trả lời khác và tôi đã sử dụng trong ví dụ về cột lưu trữ ở trên ( kế hoạch thực hiện ):
SELECT TOP 10
seq,
value,
SUM(value) OVER (ORDER BY seq ROWS UNBOUNDED PRECEDING)
FROM dbo.Table_1
ORDER BY seq DESC;
Nó dẫn đến số lần đọc ít hơn so với cách tiếp cận thứ hai của bạn và không có hoạt động tempdb so với cách tiếp cận đầu tiên của bạn vì bộ đệm cửa sổ xảy ra trong bộ nhớ :
... RANGE sử dụng bộ đệm trên đĩa, trong khi ROWS sử dụng bộ đệm trong bộ nhớ
Thật không may, thời gian chạy gần giống như cách tiếp cận thứ hai của bạn.
Table 'Worktable'. Scan count 0, logical reads 0
Table 'Table_1'. Scan count 1, logical reads 2609
Table 'Worktable'. Scan count 0, logical reads 0
SQL Server Execution Times:
CPU time = 1984 ms, elapsed time = 1474 ms.
Giải pháp dựa trên lược đồ: async chạy tổng số
Vì bạn cởi mở với các ý tưởng khác, bạn có thể xem xét cập nhật "tổng số chạy" không đồng bộ. Bạn có thể định kỳ lấy kết quả của một trong những truy vấn này và tải nó vào bảng "tổng". Vì vậy, bạn sẽ làm một cái gì đó như thế này:
CREATE TABLE [dbo].[Table_1_Totals]
(
[seq] [int] NOT NULL,
[running_total] [bigint] NOT NULL,
CONSTRAINT [PK_Table_1_Totals] PRIMARY KEY CLUSTERED ([seq])
);
Tải nó mỗi ngày / giờ / bất cứ điều gì (điều này mất khoảng 2 giây trên máy của tôi với các hàng 1mm và có thể được tối ưu hóa):
INSERT INTO dbo.Table_1_Totals
SELECT
seq,
SUM(value) OVER (ORDER BY seq ROWS UNBOUNDED PRECEDING) as total
FROM dbo.Table_1 t
WHERE NOT EXISTS (
SELECT NULL
FROM dbo.Table_1_Totals t2
WHERE t.seq = t2.seq)
ORDER BY seq DESC;
Sau đó, truy vấn báo cáo của bạn rất hiệu quả:
SELECT TOP 10
t.seq,
t.value,
t2.running_total
FROM dbo.Table_1 t
INNER JOIN dbo.Table_1_Totals t2
ON t.seq = t2.seq
ORDER BY seq DESC;
Dưới đây là số liệu thống kê đọc:
Table 'Table_1'. Scan count 0, logical reads 35
Table 'Table_1_Totals'. Scan count 1, logical reads 3
Giải pháp dựa trên lược đồ: tổng số liên tiếp với các ràng buộc
Một giải pháp thực sự thú vị cho vấn đề này được đề cập chi tiết trong câu trả lời này cho câu hỏi: Viết một lược đồ ngân hàng đơn giản: Làm thế nào tôi nên giữ số dư của mình đồng bộ với lịch sử giao dịch của họ?
Cách tiếp cận cơ bản sẽ là theo dõi tổng số hàng đang chạy hiện tại cùng với tổng số và số thứ tự chạy trước đó. Sau đó, bạn có thể sử dụng các ràng buộc để xác thực các tổng số đang chạy luôn chính xác và cập nhật.
Tín dụng cho Paul White để cung cấp một triển khai mẫu cho lược đồ trong Hỏi & Đáp này:
CREATE TABLE dbo.Table_1
(
seq integer IDENTITY(1,1) NOT NULL,
val bigint NOT NULL,
total bigint NOT NULL,
prev_seq integer NULL,
prev_total bigint NULL,
CONSTRAINT [PK_Table_1]
PRIMARY KEY CLUSTERED (seq ASC),
CONSTRAINT [UQ dbo.Table_1 seq, total]
UNIQUE (seq, total),
CONSTRAINT [UQ dbo.Table_1 prev_seq]
UNIQUE (prev_seq),
CONSTRAINT [FK dbo.Table_1 previous seq and total]
FOREIGN KEY (prev_seq, prev_total)
REFERENCES dbo.Table_1 (seq, total),
CONSTRAINT [CK dbo.Table_1 total = prev_total + val]
CHECK (total = ISNULL(prev_total, 0) + val),
CONSTRAINT [CK dbo.Table_1 denormalized columns all null or all not null]
CHECK
(
(prev_seq IS NOT NULL AND prev_total IS NOT NULL)
OR
(prev_seq IS NULL AND prev_total IS NULL)
)
);