Perbedaan dalam dua pendekatan pertama
Paket pertama menghabiskan sekitar 7 dari 10 detik di operator Window Spool, jadi ini adalah alasan utama sangat lambat. Ini melakukan banyak I / O di tempdb untuk membuat ini. Statistik I / O dan waktu saya terlihat seperti ini:
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.
The Rencana kedua adalah mampu menghindari spool, dan dengan demikian meja kerja seluruhnya. Ini hanya mengambil 10 baris teratas dari indeks berkerumun, dan kemudian apakah loop bersarang bergabung dengan agregasi (jumlah) yang keluar dari pemindaian indeks berkerumun terpisah. Sisi dalam masih berakhir dengan membaca seluruh tabel, tetapi meja ini sangat padat, jadi ini cukup efisien dengan jutaan baris.
Table 'Table_1'. Scan count 11, logical reads 26093
SQL Server Execution Times:
CPU time = 1563 ms, elapsed time = 1671 ms.
Meningkatkan kinerja
Kolom toko
Jika Anda benar-benar menginginkan pendekatan "pelaporan online", toko kolom kemungkinan adalah pilihan terbaik Anda.
ALTER TABLE [dbo].[Table_1] DROP CONSTRAINT [PK_Table_1];
CREATE CLUSTERED COLUMNSTORE INDEX [PK_Table_1] ON dbo.Table_1;
Maka pertanyaan ini sangat cepat:
SELECT TOP 10
seq,
value,
SUM(value) OVER (ORDER BY seq ROWS UNBOUNDED PRECEDING)
FROM dbo.Table_1
ORDER BY seq DESC;
Berikut ini statistik dari mesin saya:
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.
Anda mungkin tidak akan mengalahkan itu (kecuali jika Anda benar - benar pintar - bagus, Joe). Columnstore sangat pandai memindai dan mengagregasi sejumlah besar data.
Menggunakan opsi fungsi jendela ROW
daripadaRANGE
Anda bisa mendapatkan kinerja yang sangat mirip dengan permintaan kedua Anda dengan pendekatan ini, yang disebutkan dalam jawaban lain, dan yang saya gunakan dalam contoh kolom di atas ( rencana eksekusi ):
SELECT TOP 10
seq,
value,
SUM(value) OVER (ORDER BY seq ROWS UNBOUNDED PRECEDING)
FROM dbo.Table_1
ORDER BY seq DESC;
Ini menghasilkan lebih sedikit bacaan daripada pendekatan kedua Anda, dan tidak ada aktivitas tempdb vs pendekatan pertama Anda karena spool jendela terjadi di memori :
... RANGE menggunakan spool di disk, sementara ROWS menggunakan spool di memori
Sayangnya, runtime hampir sama dengan pendekatan kedua Anda.
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.
Solusi berbasis skema: total async running
Karena Anda terbuka untuk ide-ide lain, Anda dapat mempertimbangkan memperbarui "running total" secara tidak sinkron. Anda bisa secara berkala mengambil hasil dari salah satu pertanyaan ini, dan memuatnya ke tabel "total". Jadi, Anda akan melakukan sesuatu seperti ini:
CREATE TABLE [dbo].[Table_1_Totals]
(
[seq] [int] NOT NULL,
[running_total] [bigint] NOT NULL,
CONSTRAINT [PK_Table_1_Totals] PRIMARY KEY CLUSTERED ([seq])
);
Muat setiap hari / jam / apa pun (butuh sekitar 2 detik pada mesin saya dengan baris 1mm, dan bisa dioptimalkan):
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;
Maka permintaan pelaporan Anda sangat efisien:
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;
Berikut adalah statistik baca:
Table 'Table_1'. Scan count 0, logical reads 35
Table 'Table_1_Totals'. Scan count 1, logical reads 3
Solusi berbasis skema: total baris dengan batasan
Solusi yang sangat menarik untuk hal ini dibahas secara terperinci dalam jawaban untuk pertanyaan ini: Menulis skema bank sederhana: Bagaimana saya harus menjaga saldo saya sinkron dengan riwayat transaksi mereka?
Pendekatan dasar adalah untuk melacak total berjalan saat ini di-baris bersama dengan jumlah total dan urutan berjalan sebelumnya. Kemudian Anda bisa menggunakan batasan untuk memvalidasi total yang berjalan selalu benar dan terbaru.
Penghargaan untuk Paul White karena memberikan contoh implementasi untuk skema dalam T&J ini:
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)
)
);