Vì nó là một SQL Server sản xuất lâu đời nên tôi không thể dễ dàng đề xuất nâng cấp các phiên bản
Lỗi ước tính cardinality chống bán tham gia có thể được sao chép trên tất cả các phiên bản của SQL Server từ năm 2005 đến 2012 . Tất cả đều yêu cầu cờ theo dõi 4199 để cho phép sửa lỗi, vì vậy việc nâng cấp sẽ không giải quyết được vấn đề của bạn mà không kích hoạt 4199 (mặc dù có nhiều lý do tốt khác để nâng cấp từ năm 2005).
... Như vậy tôi không thể buộc gợi ý dấu vết 4199 trên truy vấn cụ thể này.
Nếu đó chỉ là một truy vấn cụ thể bị ảnh hưởng, bạn có thể sử dụng OPTION (QUERYTRACEON 4199)
để bật cờ theo dõi cho truy vấn đó. Gợi ý truy vấn này được ghi lại và hỗ trợ để sử dụng với 4199 và áp dụng từ SQL Server 2005 Service Pack 2 trở đi.
Gợi ý này có hiệu quả chạy DBCC TRACEON (4199)
và DBCC TRACEOFF (4199)
xung quanh truy vấn và kết quả là cần có sự cho phép của sysadmin . Nếu đó là một vấn đề, thêm gợi ý bằng cách sử dụng một hướng dẫn kế hoạch .
Bạn cũng nên xem xét việc kiểm tra toàn bộ hệ thống của mình với 4199 phiên bản được kích hoạt . Hồi quy kế hoạch là có thể, nhưng nhìn chung bạn có thể thấy rằng các bản sửa lỗi tối ưu hóa khác nhau được kích hoạt bởi cờ này rất đáng giá. Tất cả các bản sửa lỗi bộ xử lý truy vấn ảnh hưởng đến kế hoạch trong tương lai đều yêu cầu cờ này để kích hoạt.
Tất cả đã nói ...
Như đã đề cập trong câu trả lời của ypercube , lỗi này yêu cầu hai hoặc nhiều cột tham gia để hiển thị (trong số nhiều chi tiết). Sự dư thừa trong NOT IN
mệnh đề của bạn làm cho trình tối ưu hóa nhìn thấy hai so sánh cột (mặc dù về mặt logic chỉ có một), do đó phơi bày lỗi.
Loại bỏ sự dư thừa này sẽ 'giải quyết' vấn đề cho truy vấn cụ thể này, mặc dù các truy vấn khác thực sự có nhiều hơn một biến vị ngữ tham gia vẫn sẽ dễ bị tổn thương .
Thí dụ
Để minh họa, đây là một ví dụ dựa trên bài đăng blog CSS được liên kết trong câu hỏi (nhưng với một tập lệnh hoàn chỉnh!):
CREATE TABLE dbo.tst_TAB1
(
c1 integer NOT NULL,
c2 integer NOT NULL,
c3 integer NOT NULL
);
CREATE TABLE dbo.tst_TAB2
(
c1 integer NOT NULL,
c2 integer NOT NULL,
c3 integer NOT NULL
);
CREATE INDEX i ON dbo.tst_TAB1 (c1, c2);
CREATE INDEX i ON dbo.tst_TAB2 (c1, c2);
Dữ liệu mẫu:
INSERT dbo.tst_TAB1
(c1, c2, c3)
SELECT
number, number, number
FROM master.dbo.spt_values
WHERE
[type] = N'P'
AND number BETWEEN 1 AND 2047;
INSERT dbo.tst_TAB2 (c1, c2, c3)
VALUES (1, 1, 1);
Kiểm tra truy vấn bằng cách sử dụng NOT IN
vị ngữ dự phòng:
SELECT
T1.c1
FROM tst_TAB1 AS t1
WHERE
t1.c1 NOT IN
(
SELECT
t2.c1
FROM tst_TAB2 AS t2
-- This is redundant!
WHERE
t2.c1 = t1.c1
);
Kế hoạch thực hiện ước tính cho thấy ước tính 1 hàng sau khi chống bán tham gia:
Lưu ý bên lề: Trên thực tế đây là một ví dụ về một lỗi (hiếm) khác. Viết WHERE
mệnh đề t1.c1 = t2.c1
thay vì t2.c1 = t1.c1
cho phép trình tối ưu hóa thấy rằng hai biến vị ngữ nối trên thực tế là giống nhau và lỗi không biểu hiện.
Truy vấn tương tự với OPTION (QUERYTRACEON 4199)
:
SELECT
T1.c1
FROM tst_TAB1 AS t1
WHERE
t1.c1 NOT IN
(
SELECT
t2.c1
FROM tst_TAB2 AS t2
WHERE
t2.c1 = t1.c1
)
OPTION (QUERYTRACEON 4199);
Kế hoạch thực hiện ước tính hiện hiển thị ước tính 2046 hàng , điều này hoàn toàn chính xác:
Chúng ta cũng có thể loại bỏ vị từ thừa:
SELECT
T1.c1
FROM tst_TAB1 AS t1
WHERE
t1.c1 NOT IN
(
SELECT
t2.c1
FROM tst_TAB2 AS t2
);
Kế hoạch thực hiện tình cờ sử dụng một tối ưu hóa không liên quan bổ sung (Tổng hợp luồng), nhưng điểm quan trọng là ước tính sau khi tham gia là chính xác mà không phải bật 4199:
Nhiều cột chống bán tham gia
Có thể biểu thị một liên kết chống bán trên nhiều cột bằng NOT IN
cú pháp. Những trường hợp này sẽ yêu cầu 4199. Ví dụ: truy vấn tiếp theo tham gia vào c1
và c2
:
SELECT
T1.c1
FROM tst_TAB1 AS t1
WHERE
t1.c1 NOT IN
(
SELECT
t2.c1
FROM tst_TAB2 AS t2
WHERE
t2.c2 = t1.c2
);
Kế hoạch thực hiện cho thấy ước tính 1 hàng sai lầm:
Với 4199, vấn đề được giải quyết:
SELECT
T1.c1
FROM tst_TAB1 AS t1
WHERE
t1.c1 NOT IN
(
SELECT
t2.c1
FROM tst_TAB2 AS t2
WHERE
t2.c2 = t1.c2
)
OPTION (QUERYTRACEON 4199);
Các cú pháp khác
Sử dụng NOT IN
theo cách này là tốt nhất nên tránh, nhất là vì những lý do được đề cập trong Sách trực tuyến:
Vấn đề đó với NOT IN
và NULLs
đã được viết về nhiều lần. Có nhiều cú pháp thay thế có sẵn, trong đó NOT EXISTS
là sở thích cá nhân của tôi. Lưu ý rằng việc thay đổi cú pháp sẽ không tránh được lỗi ước tính cardinality:
SELECT
T1.c1
FROM dbo.tst_TAB1 AS t1
WHERE
NOT EXISTS
(
SELECT 1
FROM dbo.tst_TAB2 AS t2
WHERE
t2.c1 = t1.c1
AND t2.c2 = t1.c2
);
Hai cột chống bán tham gia đó tạo ra ước tính 1 hàng và yêu cầu 4199 để sửa nó. Các kế hoạch thực hiện giống hệt như đã thấy trước đây, vì vậy tôi sẽ không lặp lại chúng. Các NOT EXISTS
cú pháp không tránh được những NULLs
vấn đề với NOT IN
.
Những quan sát khác
Tôi đồng ý với những quan sát khác của ypercube.
Rắc NOLOCK
gợi ý trên mỗi bảng trong một truy vấn là một mùi mã xấu. Nếu truy vấn có thể thực sự chấp nhận READ UNCOMMITTED
ngữ nghĩa giao dịch, hãy đặt mức cô lập rõ ràng.
TOP
không có ORDER BY
là một dấu hiệu khác của mã kém. TOP
đòi hỏi một ORDER BY
mệnh đề để định TOP
nghĩa nghĩa là gì Không bao giờ dựa vào hành vi được quan sát, sử dụng mức cao nhất rõ ràng ORDER BY
để có được sự đảm bảo.
INNER LOOP JOIN
và tham gia gợi ý nói chung, ngụ ý một FORCE ORDER
gợi ý truy vấn. Điều này hạn chế nghiêm trọng sự tự do của trình tối ưu hóa, và thường bị hiểu lầm và áp dụng sai. Không bao giờ sử dụng gợi ý mà bạn không hiểu đầy đủ.