Có phải chúng tôi đang làm gì đó sai hoặc là lỗi SQL Server?
Đây là một lỗi kết quả sai, mà bạn nên báo cáo qua kênh hỗ trợ thông thường của mình. Nếu bạn không có thỏa thuận hỗ trợ, có thể giúp biết rằng các sự cố đã thanh toán thường được hoàn lại nếu Microsoft xác nhận hành vi là một lỗi.
Các lỗi đòi hỏi ba thành phần:
- Các vòng lặp lồng nhau với một tham chiếu bên ngoài (áp dụng)
- Một bộ chỉ mục lười biếng bên trong tìm kiếm trên tham chiếu bên ngoài
- Một toán tử nối bên trong
Ví dụ: truy vấn trong câu hỏi tạo ra một kế hoạch như sau:
Có nhiều cách để loại bỏ một trong các yếu tố này, vì vậy lỗi không còn sinh sản nữa.
Ví dụ: người ta có thể tạo các chỉ mục hoặc số liệu thống kê xảy ra có nghĩa là trình tối ưu hóa chọn không sử dụng Bộ chỉ số lười biếng. Hoặc, người ta có thể sử dụng các gợi ý để buộc băm hoặc hợp nhất liên kết thay vì sử dụng Ghép. Người ta cũng có thể viết lại truy vấn để diễn đạt cùng một ngữ nghĩa, nhưng điều đó dẫn đến một hình dạng kế hoạch khác trong đó thiếu một hoặc nhiều yếu tố cần thiết.
Thêm chi tiết
Một chỉ số lười biếng Bộ đệm lười biếng lưu trữ các hàng kết quả bên trong, trong một bảng làm việc được lập chỉ mục bởi các giá trị tham chiếu bên ngoài (tham số tương quan). Nếu một chỉ số Lazy Index Spool được yêu cầu tham chiếu bên ngoài mà nó đã thấy trước đó, nó sẽ tìm nạp hàng kết quả được lưu trong bộ nhớ cache từ bảng làm việc của nó ("tua lại"). Nếu bộ đệm được yêu cầu một giá trị tham chiếu bên ngoài mà nó chưa từng thấy trước đó, nó sẽ chạy cây con của nó với giá trị tham chiếu bên ngoài hiện tại và lưu trữ kết quả (một "rebind"). Vị từ tìm kiếm trên Lazy Index Spool chỉ ra (các) khóa cho bảng làm việc của nó.
Vấn đề xảy ra trong hình dạng kế hoạch cụ thể này khi ống chỉ kiểm tra xem liệu một tham chiếu bên ngoài mới có giống với tham chiếu bên ngoài không. Các vòng lặp lồng nhau tham gia cập nhật chính xác các tham chiếu bên ngoài của nó và thông báo cho các toán tử về đầu vào bên trong của nó thông qua các PrepRecompute
phương thức giao diện của chúng . Khi bắt đầu kiểm tra này, các nhà khai thác bên trong đọc thuộc CParamBounds:FNeedToReload
tính để xem liệu tham chiếu bên ngoài đã thay đổi so với lần trước. Một dấu vết ngăn xếp ví dụ được hiển thị dưới đây:
Khi cây con được hiển thị ở trên tồn tại, cụ thể là nơi sử dụng Phép nối, có vấn đề xảy ra (có lẽ là sự cố ByVal / ByRef / Sao chép) với các ràng buộc CParamBounds:FNeedToReload
luôn trả về sai, bất kể tham chiếu bên ngoài có thực sự thay đổi hay không.
Khi cùng một cây con tồn tại, nhưng Merge Union hoặc Hash Union được sử dụng, thuộc tính thiết yếu này được đặt chính xác trên mỗi lần lặp và Bộ đệm chỉ số lười biếng tua lại hoặc tua lại mỗi lần khi thích hợp. Nhân tiện, sắp xếp phân biệt và luồng tổng hợp là đáng trách. Sự nghi ngờ của tôi là Hợp nhất và Hash Union tạo một bản sao của giá trị trước đó, trong khi Kết hợp sử dụng một tham chiếu. Thật không may là không thể xác minh điều này mà không có quyền truy cập vào mã nguồn SQL Server.
Kết quả cuối cùng là Lazy Index Spool trong hình dạng kế hoạch có vấn đề luôn nghĩ rằng nó đã thấy tham chiếu bên ngoài hiện tại, tua lại bằng cách tìm kiếm trong bảng làm việc của nó, thường không tìm thấy gì, vì vậy không có hàng nào được trả về cho tham chiếu bên ngoài đó. Bước qua thực thi trong trình gỡ lỗi, bộ đệm chỉ thực hiện RewindHelper
phương thức của nó và không bao giờ ReloadHelper
phương thức của nó (reload = rebind trong ngữ cảnh này). Điều này thể hiện rõ trong kế hoạch thực hiện bởi vì tất cả các toán tử trong bộ đệm đều có 'Số lần thực hiện = 1'.
Tất nhiên, ngoại lệ là dành cho tham chiếu bên ngoài đầu tiên, Lazy Index Spool được đưa ra. Điều này luôn luôn thực thi cây con và lưu trữ một hàng kết quả trong bảng làm việc. Tất cả các lần lặp lại tiếp theo dẫn đến tua lại, sẽ chỉ tạo ra một hàng (hàng được lưu trong bộ nhớ cache) khi lần lặp hiện tại có cùng giá trị cho tham chiếu bên ngoài như lần đầu tiên.
Vì vậy, đối với bất kỳ đầu vào đã cho nào được đặt ở phía bên ngoài của Tham gia các vòng lặp lồng nhau, truy vấn sẽ trả về nhiều hàng như có các bản sao của hàng đầu tiên được xử lý (dĩ nhiên cộng với một hàng cho hàng đầu tiên).
Bản giới thiệu
Bảng và dữ liệu mẫu:
CREATE TABLE #T1
(
pk integer IDENTITY NOT NULL,
c1 integer NOT NULL,
CONSTRAINT PK_T1
PRIMARY KEY CLUSTERED (pk)
);
GO
INSERT #T1 (c1)
VALUES
(1), (2), (3), (4), (5), (6),
(1), (2), (3), (4), (5), (6),
(1), (2), (3), (4), (5), (6);
Truy vấn (tầm thường) sau đây tạo ra số đếm chính xác là hai cho mỗi hàng (tổng cộng 18) bằng cách sử dụng Liên minh Hợp nhất:
SELECT T1.c1, C.c1
FROM #T1 AS T1
CROSS APPLY
(
SELECT COUNT_BIG(*) AS c1
FROM
(
SELECT T1.c1
UNION
SELECT NULL
) AS U
) AS C;
Nếu bây giờ chúng ta thêm một gợi ý truy vấn để buộc Kết nối:
SELECT T1.c1, C.c1
FROM #T1 AS T1
CROSS APPLY
(
SELECT COUNT_BIG(*) AS c1
FROM
(
SELECT T1.c1
UNION
SELECT NULL
) AS U
) AS C
OPTION (CONCAT UNION);
Kế hoạch thực hiện có hình dạng có vấn đề:
Và kết quả bây giờ không chính xác, chỉ ba hàng:
Mặc dù hành vi này không được đảm bảo, hàng đầu tiên từ Quét chỉ mục cụm có c1
giá trị là 1. Có hai hàng khác có giá trị này, do đó tổng cộng ba hàng được tạo ra.
Bây giờ cắt bớt bảng dữ liệu và tải nó với nhiều bản sao của hàng 'đầu tiên':
TRUNCATE TABLE #T1;
INSERT #T1 (c1)
VALUES
(1), (2), (3), (4), (5), (6),
(1), (2), (3), (4), (5), (6),
(1), (1), (1), (1), (1), (1);
Bây giờ kế hoạch kết nối là:
Và, như đã chỉ ra, 8 hàng được sản xuất, tất c1 = 1
nhiên đều có:
Tôi nhận thấy bạn đã mở một mục Kết nối cho lỗi này nhưng thực sự đó không phải là nơi để báo cáo các vấn đề đang có tác động sản xuất. Nếu đó là trường hợp, bạn thực sự nên liên hệ với bộ phận Hỗ trợ của Microsoft.
Lỗi kết quả sai này đã được sửa ở một số giai đoạn. Nó không còn sao chép cho tôi trên bất kỳ phiên bản SQL Server nào từ năm 2012 trở đi. Nó không repro trên SQL Server 2008 R2 SP3-GDR bản dựng 10.50,6560.0 (X64).