Tại sao kế hoạch thực hiện là khác nhau
Truy vấn đầu tiên
select sum(PrinBal)
from fpc
inner join gl on gl.SnapshotKey = fpc.SnapshotKey
where fpc.SnapshotKey = 201703
Trình tối ưu hóa biết:
gl.SnapshotKey = fpc.SnapshotKey
; và
fpc.SnapshotKey = 201703
để nó có thể suy ra:
Giống như bạn đã viết:
select sum(PrinBal)
from fpc
inner join gl on gl.SnapshotKey = fpc.SnapshotKey
where fpc.SnapshotKey = 201703
and gl.SnapshotKey = 201703
Giá trị bằng chữ 201703 cũng có thể được sử dụng bởi trình tối ưu hóa để xác định id phân vùng. Với cả hai biến SnapshotKey
vị ngữ (một đã cho, một suy ra) điều này có nghĩa là trình tối ưu hóa biết id phân vùng cho cả hai bảng.
Đi xa hơn, với giá trị bằng chữ (201703) SnapshotKey
hiện có sẵn trên cả hai bảng, vị từ tham gia:
gl.SnapshotKey = fpc.SnapshotKey
đơn giản hóa để:
201703 = 201703
; hoặc đơn giản
true
Có nghĩa là không có vị ngữ tham gia nào cả. Kết quả là một tham gia chéo hợp lý. Thể hiện kế hoạch thực hiện cuối cùng bằng cú pháp T-SQL gần nhất có sẵn, như thể bạn đã viết:
SELECT
CASE
WHEN SUM(Q1.c) = 0 THEN NULL
ELSE SUM(Q1.s)
END
FROM
(
SELECT c = COUNT_BIG(*), s = SUM(GL.PrinBal)
FROM dbo.gl AS GL
WHERE GL.SnapshotKey = 201703
AND $PARTITION.PF(GL.SnapshotKey) = $PARTITION.PF(201703)
) AS Q1
CROSS JOIN
(
SELECT Dummy = 1
FROM dbo.fpc AS FPC
WHERE FPC.SnapshotKey = 201703
AND $PARTITION.PF(FPC.SnapshotKey) = $PARTITION.PF(201703)
) AS Q2;
Truy vấn thứ hai
select sum(PrinBal)
from fpc
inner join gl on $Partition.PF(gl.SnapshotKey) = $Partition.PF(fpc.SnapshotKey)
where fpc.SnapshotKey = 201703
Trình tối ưu hóa không còn có thể suy ra bất cứ điều gì về gl.SnapshotKey
, vì vậy việc đơn giản hóa và biến đổi được thực hiện cho truy vấn đầu tiên không còn có thể.
Thật vậy, trừ khi đúng là mỗi phân vùng chỉ giữ một lần duy nhất SnapshotKey
, việc viết lại không được đảm bảo để tạo ra kết quả tương tự.
Một lần nữa, biểu thị kế hoạch thực hiện được tạo bằng cú pháp T-SQL gần nhất có sẵn:
SELECT
CASE
WHEN SUM(Q2.c) = 0 THEN NULL
ELSE SUM(Q2.s)
END
FROM
(
SELECT
Q1.PtnID,
c = COUNT_BIG(*),
s = SUM(Q1.PrinBal)
FROM
(
SELECT GL.PrinBal, PtnID = $PARTITION.PF(GL.SnapshotKey)
FROM dbo.gl AS GL
) AS Q1
GROUP BY
Q1.PtnID
) AS Q2
CROSS APPLY
(
SELECT Dummy = 1
FROM dbo.fpc AS FPC
WHERE
$PARTITION.PF(FPC.SnapshotKey) = Q2.PtnID
AND FPC.SnapshotKey = 201703
) AS Q3;
Lần này không có sự tham gia chéo hợp lý. Thay vào đó, có một phép nối tương quan (áp dụng) trên id phân vùng.
tại sao truy vấn thứ hai nhanh hơn
Điều này là khó để đánh giá từ các thông tin được đưa ra. Sử dụng dữ liệu giả và bảng dựa trên các truy vấn và hình ảnh kế hoạch được cung cấp, tôi thấy truy vấn đầu tiên vượt trội hơn lần thứ hai trong mọi trường hợp.
Cùng một truy vấn được thể hiện bằng cách sử dụng cú pháp khác nhau thường có thể tạo ra một kế hoạch thực hiện khác nhau, đơn giản là vì trình tối ưu hóa bắt đầu từ một điểm khác nhau và khám phá các tùy chọn theo một thứ tự khác trước khi tìm thấy một kế hoạch thực hiện phù hợp. Tìm kiếm kế hoạch không đầy đủ, và không phải mọi chuyển đổi logic có thể có sẵn, do đó kết quả cuối cùng có thể khác nhau. Như đã lưu ý ở trên, hai truy vấn không nhất thiết phải thể hiện cùng một yêu cầu (ít nhất là được cung cấp thông tin có sẵn cho trình tối ưu hóa).
Trên một lưu ý riêng, hãy lưu ý rằng việc triển khai cột lưu trữ ban đầu trong SQL Server 2012 (và ở mức độ thấp hơn một chút, 2014) có nhiều hạn chế, nhất là về mặt tối ưu hóa mọi thứ. Bạn có thể sẽ nhận được kết quả tốt hơn và nhất quán hơn bằng cách nâng cấp lên bản phát hành gần đây hơn (lý tưởng nhất là mới nhất). Điều này đặc biệt đúng nếu bạn đang sử dụng phân vùng.
Tôi chắc chắn sẽ không khuyên bạn nên tập thói quen viết lại các phép nối bằng cách sử dụng $PARTITION
, ngoại trừ như là phương sách cuối cùng và với sự hiểu biết rất sâu sắc về những gì bạn đang làm.
Đó là tất cả những gì tôi có thể nói mà không thể xem chi tiết lược đồ hoặc kế hoạch.