Làm cách nào để gợi ý nhiều người tham gia vào SQL Server?


9

Tôi có 3 bảng "lớn" tham gia trên một cặp cột (cả hai int).

  • Bảng 1 có ~ 200 triệu hàng
  • Bảng 2 có ~ 1,5 triệu hàng
  • Bảng 3 có ~ 6 triệu hàng

Mỗi bảng có một nhóm chỉ số trên Key1, Key2và sau đó thêm một cột. Key1có cardinality thấp và rất sai lệch. Nó luôn được tham chiếu trong WHEREmệnh đề. Key2không bao giờ được đề cập trong WHEREmệnh đề. Mỗi tham gia là nhiều-nhiều.

Vấn đề là với ước tính cardinality. Ước tính đầu ra của mỗi liên kết trở nên nhỏ hơn thay vì lớn hơn . Điều này dẫn đến ước tính cuối cùng là hàng trăm thấp khi kết quả thực tế là hàng triệu.

Có cách nào để tôi đầu mối CE để ước tính tốt hơn không?

SELECT 1
FROM Table1 t1
     JOIN Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
     JOIN Table3 t3
       ON t1.Key1 = t3.Key1
          AND t1.Key2 = t3.Key2
WHERE t1.Key1 = 1;

Các giải pháp tôi đã thử:

  • Tạo số liệu thống kê nhiều cột trên Key1,Key2
  • Tạo hàng tấn số liệu thống kê được lọc trên Key1(Điều này giúp khá nhiều, nhưng tôi kết thúc với hàng ngàn số liệu thống kê do người dùng tạo trong cơ sở dữ liệu.)

Kế hoạch thực hiện đeo mặt nạ (xin lỗi vì mặt nạ xấu)

Trong trường hợp tôi đang xem xét, kết quả có 9 triệu hàng. CE mới ước tính 180 hàng; di sản CE ước tính 6100 hàng.

Đây là một ví dụ có thể tái tạo:

DROP TABLE IF EXISTS #Table1, #Table2, #Table3;
CREATE TABLE #Table1 (Key1 INT NOT NULL, Key2 INT NOT NULL, T1Key3 INT NOT NULL, CONSTRAINT pk_t1 PRIMARY KEY CLUSTERED (Key1, Key2, T1Key3));
CREATE TABLE #Table2 (Key1 INT NOT NULL, Key2 INT NOT NULL, T2Key3 INT NOT NULL, CONSTRAINT pk_t2 PRIMARY KEY CLUSTERED (Key1, Key2, T2Key3));
CREATE TABLE #Table3 (Key1 INT NOT NULL, Key2 INT NOT NULL, T3Key3 INT NOT NULL, CONSTRAINT pk_t3 PRIMARY KEY CLUSTERED (Key1, Key2, T3Key3));

-- Table1 
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2),
     DataSize (Key1, NumberOfRows)
     AS (SELECT 1, 2000 UNION
         SELECT 2, 10000 UNION
         SELECT 3, 25000 UNION
         SELECT 4, 50000 UNION
         SELECT 5, 200000)
INSERT INTO #Table1
SELECT Key1
     , Key2 = ROW_NUMBER() OVER (PARTITION BY Key1, T1Key3 ORDER BY Number)
     , T1Key3
FROM DataSize
     CROSS APPLY (SELECT TOP(NumberOfRows) 
                         Number
                       , T1Key3 = Number%(Key1*Key1) + 1 
                  FROM Numbers
                  ORDER BY Number) size;

-- Table2 (same Key1, Key2 values; smaller number of distinct third Key)
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2)
INSERT INTO #Table2
SELECT DISTINCT 
       Key1
     , Key2
     , T2Key3
FROM #Table1
     CROSS APPLY (SELECT TOP (Key1*10) 
                         T2Key3 = Number
                  FROM Numbers
                  ORDER BY Number) size;

-- Table2 (same Key1, Key2 values; smallest number of distinct third Key)
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2)
INSERT INTO #Table3
SELECT DISTINCT 
       Key1
     , Key2
     , T3Key3
FROM #Table1
     CROSS APPLY (SELECT TOP (Key1) 
                         T3Key3 = Number
                  FROM Numbers
                  ORDER BY Number) size;


DROP TABLE IF EXISTS #a;
SELECT col = 1 
INTO #a
FROM #Table1 t1
     JOIN #Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
WHERE t1.Key1 = 1;

DROP TABLE IF EXISTS #b;
SELECT col = 1 
INTO #b
FROM #Table1 t1
     JOIN #Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
     JOIN #Table3 t3
       ON t1.Key1 = t3.Key1
          AND t1.Key2 = t3.Key2
WHERE t1.Key1 = 1;

Câu trả lời:


5

Rõ ràng, trình tối ưu hóa đã biết rằng đó là một sự tham gia nhiều-nhiều. Nếu bạn buộc hợp nhất các phép nối và xem xét một kế hoạch ước tính, bạn có thể thấy một thuộc tính cho toán tử nối sẽ cho bạn biết nếu phép nối có thể là nhiều-nhiều. Vấn đề mà bạn cần giải quyết ở đây là làm tăng các ước tính về số lượng thẻ, có lẽ để bạn có được một kế hoạch truy vấn hiệu quả hơn cho phần truy vấn mà bạn đã bỏ qua.

Điều đầu tiên mà tôi sẽ thử là đưa kết quả của việc tham gia từ Object3Object5vào một bảng tạm thời. Đối với gói mà bạn đã đăng, nó chỉ là một cột duy nhất trên 51393 hàng, do đó, nó hầu như không chiếm bất kỳ khoảng trống nào trong tempdb. Bạn có thể thu thập số liệu thống kê đầy đủ trên bảng tạm thời và chỉ riêng điều đó có thể đủ để có được ước tính chính xác về số lượng chính xác cuối cùng. Thu thập số liệu thống kê đầy đủ trên Object1cũng có thể giúp đỡ. Ước tính cardinality thường trở nên tồi tệ hơn khi bạn đi qua một kế hoạch từ phải sang trái.

Nếu điều đó không hiệu quả, bạn có thể thử ENABLE_QUERY_OPTIMIZER_HOTFIXESgợi ý truy vấn nếu bạn chưa kích hoạt nó ở cấp cơ sở dữ liệu hoặc máy chủ. Microsoft khóa các bản sửa lỗi hiệu năng ảnh hưởng đến kế hoạch cho SQL Server 2016 đằng sau cài đặt đó. Một số trong số chúng liên quan đến ước tính cardinality, vì vậy có lẽ bạn sẽ gặp may mắn và một trong những cách khắc phục sẽ giúp ích cho truy vấn của bạn. Bạn cũng có thể thử sử dụng công cụ ước tính cardinality kế thừa với một FORCE_LEGACY_CARDINALITY_ESTIMATIONgợi ý truy vấn. Một số bộ dữ liệu có thể có được ước tính tốt hơn với CE kế thừa.

Như một phương sách cuối cùng, bạn có thể tự tăng ước tính số lượng thẻ theo bất kỳ yếu tố nào bạn muốn bằng cách sử dụng MANY()chức năng của Adam Machanic . Tôi nói về nó trong một câu trả lời khác nhưng có vẻ như liên kết đã chết. Nếu bạn quan tâm tôi có thể cố gắng đào một cái gì đó lên.


make_parallelChức năng của Adam được sử dụng để giúp giảm thiểu vấn đề. Tôi sẽ xem xét many. Có vẻ như một hỗ trợ ban nhạc khá thô.
Steven Hibble

2

Số liệu thống kê SQL Server chỉ chứa biểu đồ cho cột hàng đầu của đối tượng thống kê. Do đó, bạn có thể tạo các số liệu thống kê được lọc cung cấp biểu đồ giá trị cho Key2, nhưng chỉ giữa các hàng với Key1 = 1. Tạo các thống kê được lọc này trên mỗi bảng sẽ sửa các ước tính và dẫn đến hành vi bạn mong đợi cho truy vấn kiểm tra: mỗi lần tham gia mới không ảnh hưởng đến ước tính số lượng thẻ cuối cùng (được xác nhận trong cả SQL 2016 SP1 và SQL 2017).

-- Note: Add "WITH FULLSCAN" to each if you want a perfect 20,000 row estimate
CREATE STATISTICS st_#Table1 ON #Table1 (Key2) WHERE Key1 = 1
CREATE STATISTICS st_#Table2 ON #Table2 (Key2) WHERE Key1 = 1
CREATE STATISTICS st_#Table3 ON #Table3 (Key2) WHERE Key1 = 1

Nếu không có các thống kê được lọc này, SQL Server sẽ có cách tiếp cận dựa trên kinh nghiệm hơn để ước tính mức độ chính xác của việc tham gia của bạn. Bảng trắng sau đây chứa các mô tả cấp cao tốt về một số phương pháp phỏng đoán mà SQL Server sử dụng: Tối ưu hóa các kế hoạch truy vấn của bạn với Công cụ ước tính Cardinality của SQL Server 2014 .

Ví dụ: việc thêm USE HINT('ASSUME_JOIN_PREDICATE_DEPENDS_ON_FILTERS')gợi ý vào truy vấn của bạn sẽ thay đổi heuristic ngăn chặn tham gia để giả định một số mối tương quan (chứ không phải độc lập) giữa Key1vị từ và Key2vị từ tham gia, có thể có lợi cho truy vấn của bạn. Đối với truy vấn thử nghiệm cuối cùng, gợi ý này làm tăng ước tính cardinality từ 1,175đến 7,551, nhưng vẫn còn hơi ngại với 20,000ước tính hàng chính xác được tạo ra với các thống kê được lọc.

Một cách tiếp cận khác mà chúng tôi đã sử dụng trong các tình huống tương tự là trích xuất tập hợp con dữ liệu có liên quan vào các bảng #temp. Đặc biệt là bây giờ các phiên bản SQL Server mới hơn không còn háo hức ghi các bảng #temp vào đĩa , chúng tôi đã có kết quả tốt với phương pháp này. Mô tả của bạn về việc tham gia nhiều-nhiều của bạn ngụ ý rằng mỗi bảng #temp riêng lẻ trong trường hợp của bạn sẽ tương đối nhỏ (hoặc ít nhất là nhỏ hơn tập kết quả cuối cùng), vì vậy cách tiếp cận này có thể đáng để thử.

DROP TABLE IF EXISTS #Table1_extract, #Table2_extract, #Table3_extract, #c
-- Extract only the subset of rows that match the filter predicate
-- (Or better yet, extract only the subset of columns you need!)
SELECT * INTO #Table1_extract FROM #Table1 WHERE Key1 = 1
SELECT * INTO #Table2_extract FROM #Table2 WHERE Key1 = 1
SELECT * INTO #Table3_extract FROM #Table3 WHERE Key1 = 1
-- Now perform the join on those extracts, removing the filter predicate
SELECT col = 1
INTO #c 
FROM #Table1_extract t1
JOIN #Table2_extract t2
    ON t1.Key2 = t2.Key2
JOIN #Table3_extract t3
    ON t1.Key2 = t3.Key2

Chúng tôi sử dụng rộng rãi các số liệu thống kê được lọc, nhưng chúng tôi làm cho chúng một Key1giá trị trên mỗi bảng. Chúng tôi hiện có hàng ngàn người trong số họ.
Steven Hibble

2
@StevenHibble Điểm hay là hàng ngàn chỉ số được lọc có thể gây khó khăn cho việc quản lý. (Chúng tôi cũng đã thấy rằng nó ảnh hưởng tiêu cực đến thời gian biên soạn kế hoạch.) Nó có thể không phù hợp với trường hợp sử dụng của bạn, nhưng tôi cũng đã thêm một cách tiếp cận bảng #temp khác mà chúng tôi đã sử dụng thành công nhiều lần.
Geoff Patterson

-1

Một tầm nhìn. Không có cơ sở thực tế nào ngoài thử.

SELECT 1
FROM Table1 t1
     JOIN Table2 t2
       ON t1.Key2 = t2.Key2
      AND t1.Key1 = 1
      AND t2.Key1 = 1
     JOIN Table3 t3
       ON t2.Key2 = t3.Key2
      AND t3.Key1 = 1;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.