Tôi sẽ giới hạn bài đăng này để thảo luận về thống kê cột đơn vì nó sẽ khá dài và bạn quan tâm đến cách SQL Server chuyển dữ liệu vào các bước của biểu đồ. Đối với thống kê nhiều cột, biểu đồ chỉ được tạo trên cột hàng đầu.
Khi SQL Server xác định rằng cần có cập nhật thống kê, nó sẽ khởi động một truy vấn ẩn đọc tất cả dữ liệu của bảng hoặc mẫu dữ liệu của bảng. Bạn có thể xem các truy vấn này với các sự kiện mở rộng. Có một chức năng được gọi StatMan
trong SQL Server có liên quan đến việc tạo biểu đồ. Đối với các đối tượng thống kê đơn giản, có ít nhất hai loại StatMan
truy vấn khác nhau (có các truy vấn khác nhau để cập nhật chỉ số nhanh và tôi nghi ngờ rằng tính năng thống kê gia tăng trên các bảng được phân đoạn cũng sử dụng một truy vấn khác nhau).
Cái đầu tiên chỉ lấy tất cả dữ liệu từ bảng mà không cần lọc. Bạn có thể thấy điều này khi bảng rất nhỏ hoặc bạn thu thập số liệu thống kê với FULLSCAN
tùy chọn:
CREATE TABLE X_SHOW_ME_STATMAN (N INT);
CREATE STATISTICS X_STAT_X_SHOW_ME_STATMAN ON X_SHOW_ME_STATMAN (N);
-- after gathering stats with 1 row in table
SELECT StatMan([SC0]) FROM
(
SELECT TOP 100 PERCENT [N] AS [SC0]
FROM [dbo].[X_SHOW_ME_STATMAN] WITH (READUNCOMMITTED)
ORDER BY [SC0]
) AS _MS_UPDSTATS_TBL
OPTION (MAXDOP 16);
SQL Server chọn kích thước mẫu tự động dựa trên kích thước của bảng (Tôi nghĩ rằng đó là cả số lượng hàng và trang trong bảng). Nếu một bảng quá lớn thì kích thước mẫu tự động giảm xuống dưới 100%. Đây là những gì tôi nhận được cho cùng một bảng với các hàng 1M:
-- after gathering stats with 1 M rows in table
SELECT StatMan([SC0], [SB0000]) FROM
(
SELECT TOP 100 PERCENT [SC0], step_direction([SC0]) over (order by NULL) AS [SB0000]
FROM
(
SELECT [N] AS [SC0]
FROM [dbo].[X_SHOW_ME_STATMAN] TABLESAMPLE SYSTEM (6.666667e+001 PERCENT) WITH (READUNCOMMITTED)
) AS _MS_UPDSTATS_TBL_HELPER
ORDER BY [SC0], [SB0000]
) AS _MS_UPDSTATS_TBL
OPTION (MAXDOP 1);
TABLESAMPLE
được ghi lại nhưng StatMan và step_direction thì không. ở đây SQL Server lấy mẫu khoảng 66,6% dữ liệu từ bảng để tạo biểu đồ. Điều này có nghĩa là bạn có thể nhận được một số bước khác nhau khi cập nhật số liệu thống kê (không có FULLSCAN
) trên cùng một dữ liệu. Tôi chưa bao giờ quan sát điều này trong thực tế nhưng tôi không hiểu tại sao điều đó là không thể.
Hãy chạy một vài thử nghiệm trên dữ liệu đơn giản để xem cách thống kê thay đổi theo thời gian. Dưới đây là một số mã kiểm tra mà tôi đã viết để chèn số nguyên tuần tự vào một bảng, thu thập số liệu thống kê sau mỗi lần chèn và lưu thông tin về số liệu thống kê vào bảng kết quả. Hãy bắt đầu chỉ với việc chèn 1 hàng tại một thời điểm lên tới 10000. Giường thử:
DECLARE
@stats_id INT,
@table_object_id INT,
@rows_per_loop INT = 1,
@num_of_loops INT = 10000,
@loop_num INT;
BEGIN
SET NOCOUNT ON;
TRUNCATE TABLE X_STATS_RESULTS;
SET @table_object_id = OBJECT_ID ('X_SEQ_NUM');
SELECT @stats_id = stats_id FROM sys.stats
WHERE OBJECT_ID = @table_object_id
AND name = 'X_STATS_SEQ_INT_FULL';
SET @loop_num = 0;
WHILE @loop_num < @num_of_loops
BEGIN
SET @loop_num = @loop_num + 1;
INSERT INTO X_SEQ_NUM WITH (TABLOCK)
SELECT @rows_per_loop * (@loop_num - 1) + N FROM dbo.GetNums(@rows_per_loop);
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN; -- can comment out FULLSCAN as needed
INSERT INTO X_STATS_RESULTS WITH (TABLOCK)
SELECT 'X_STATS_SEQ_INT_FULL', @rows_per_loop * @loop_num, rows_sampled, steps
FROM sys.dm_db_stats_properties(@table_object_id, @stats_id);
END;
END;
Đối với dữ liệu này, số bước của biểu đồ nhanh chóng tăng lên 200 (lần đầu tiên đạt số bước tối đa với 397 hàng), giữ ở mức 199 hoặc 200 cho đến khi có 1485 hàng trong bảng, sau đó giảm dần cho đến khi biểu đồ chỉ có 3 hoặc 4 các bước. Dưới đây là biểu đồ của tất cả các dữ liệu:
Đây là biểu đồ cho các hàng 10k:
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1 0 1 0 1
9999 9997 1 9997 1
10000 0 1 0 1
Có một vấn đề là biểu đồ chỉ có 3 bước? Có vẻ như thông tin được bảo tồn từ quan điểm của chúng tôi. Lưu ý rằng vì kiểu dữ liệu là INTEGER, chúng ta có thể tìm ra có bao nhiêu hàng trong bảng cho mỗi số nguyên từ 1 - 10000. Thông thường, SQL Server cũng có thể tìm ra điều này, mặc dù có một số trường hợp điều này không hoàn toàn diễn ra . Xem bài SE này cho một ví dụ về điều này.
Bạn nghĩ điều gì sẽ xảy ra nếu chúng ta xóa một hàng khỏi bảng và cập nhật số liệu thống kê? Lý tưởng nhất là chúng ta sẽ có một bước biểu đồ khác để chỉ ra rằng số nguyên bị thiếu không còn trong bảng.
DELETE FROM X_SEQ_NUM
WHERE X_NUM = 1000;
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;
DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 3 steps
DELETE FROM X_SEQ_NUM
WHERE X_NUM IN (2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000);
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;
DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 3 steps
Đó là một chút thất vọng. Nếu chúng tôi đang xây dựng một biểu đồ bằng tay, chúng tôi sẽ thêm một bước cho mỗi giá trị còn thiếu. SQL Server đang sử dụng thuật toán cho mục đích chung, vì vậy đối với một số bộ dữ liệu, chúng tôi có thể đưa ra biểu đồ phù hợp hơn so với mã mà nó sử dụng. Tất nhiên, sự khác biệt thực tế giữa việc nhận 0 hoặc 1 hàng từ một bảng là rất nhỏ. Tôi nhận được kết quả tương tự khi kiểm tra với 20000 hàng mà mỗi số nguyên có 2 hàng trong bảng. Biểu đồ không đạt được các bước khi tôi xóa dữ liệu.
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1 0 2 0 1
9999 19994 2 9997 2
10000 0 2 0 1
Nếu tôi kiểm tra với 1 triệu hàng với mỗi số nguyên có 100 hàng trong bảng tôi sẽ nhận được kết quả tốt hơn một chút, nhưng tôi vẫn có thể xây dựng biểu đồ tốt hơn bằng tay.
truncate table X_SEQ_NUM;
BEGIN TRANSACTION;
INSERT INTO X_SEQ_NUM WITH (TABLOCK)
SELECT N FROM dbo.GetNums(10000);
GO 100
COMMIT TRANSACTION;
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;
DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- 4 steps
DELETE FROM X_SEQ_NUM
WHERE X_NUM = 1000;
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;
DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- now 5 steps with a RANGE_HI_KEY of 998 (?)
DELETE FROM X_SEQ_NUM
WHERE X_NUM IN (2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000);
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;
DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 5 steps
Biểu đồ cuối cùng:
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1 0 100 0 1
998 99600 100 996 100
3983 298100 100 2981 100
9999 600900 100 6009 100
10000 0 100 0 1
Hãy thử nghiệm thêm với các số nguyên tuần tự nhưng có nhiều hàng hơn trong bảng. Lưu ý rằng đối với các bảng quá nhỏ chỉ định thủ công một cỡ mẫu sẽ không có hiệu lực, vì vậy tôi sẽ thêm 100 hàng vào mỗi lần chèn và thu thập số liệu thống kê mỗi lần lên tới 1 triệu hàng. Tôi thấy một mẫu tương tự như trước đây, ngoại trừ khi tôi nhận được tới 637300 hàng trong bảng, tôi không còn lấy mẫu 100% các hàng trong bảng với tỷ lệ mẫu mặc định. Khi tôi đạt được hàng, số bước của biểu đồ tăng lên. Có lẽ điều này là do SQL Server kết thúc với nhiều khoảng trống trong dữ liệu khi số lượng hàng không được ghép trong bảng tăng lên. Tôi không đạt 200 bước ngay cả ở các hàng 1 M, nhưng nếu tôi tiếp tục thêm các hàng, tôi hy vọng tôi sẽ đến đó và cuối cùng bắt đầu quay trở lại.
Trục X là số lượng hàng trong bảng. Khi số lượng hàng tăng lên, các hàng được lấy mẫu thay đổi một chút và không vượt quá 650k.
Bây giờ hãy thực hiện một số thử nghiệm đơn giản với dữ liệu VARCHAR.
CREATE TABLE X_SEQ_STR (X_STR VARCHAR(5));
CREATE STATISTICS X_SEQ_STR ON X_SEQ_STR(X_STR);
Ở đây tôi đang chèn 200 số (dưới dạng chuỗi) cùng với NULL.
INSERT INTO X_SEQ_STR
SELECT N FROM dbo.GetNums(200)
UNION ALL
SELECT NULL;
UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;
DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 111 steps, RANGE_ROWS is 0 or 1 for all steps
Lưu ý rằng NULL luôn có bước biểu đồ riêng khi được tìm thấy trong bảng. SQL Server có thể đã cung cấp cho tôi chính xác 201 bước để lưu giữ tất cả thông tin nhưng nó đã không làm điều đó. Thông tin kỹ thuật bị mất vì '1111' sắp xếp giữa '1' và '2' chẳng hạn.
Bây giờ, hãy thử chèn các ký tự khác nhau thay vì chỉ các số nguyên:
truncate table X_SEQ_STR;
INSERT INTO X_SEQ_STR
SELECT CHAR(10 + N) FROM dbo.GetNums(200)
UNION ALL
SELECT NULL;
UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;
DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 95 steps, RANGE_ROWS is 0 or 1 or 2
Không có sự khác biệt thực sự từ các thử nghiệm cuối cùng.
Bây giờ, hãy thử chèn các ký tự nhưng đặt các số khác nhau của mỗi ký tự vào bảng. Ví dụ: CHAR(11)
có 1 hàng, CHAR(12)
có 2 hàng, v.v.
truncate table X_SEQ_STR;
DECLARE
@loop_num INT;
BEGIN
SET NOCOUNT ON;
SET @loop_num = 0;
WHILE @loop_num < 200
BEGIN
SET @loop_num = @loop_num + 1;
INSERT INTO X_SEQ_STR WITH (TABLOCK)
SELECT CHAR(10 + @loop_num) FROM dbo.GetNums(@loop_num);
END;
END;
UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;
DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 148 steps, most with RANGE_ROWS of 0
Như trước đây tôi vẫn không nhận được chính xác 200 bước biểu đồ. Tuy nhiên, nhiều bước có RANGE_ROWS
0.
Đối với thử nghiệm cuối cùng, tôi sẽ chèn một chuỗi 5 ký tự ngẫu nhiên vào mỗi vòng lặp và thu thập số liệu thống kê mỗi lần. Đây là mã chuỗi ngẫu nhiên:
char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))
Dưới đây là biểu đồ của các hàng trong bảng so với các bước biểu đồ:
Lưu ý rằng số bước không giảm xuống dưới 100 khi nó bắt đầu tăng và giảm. Tôi đã nghe từ đâu đó (nhưng không thể lấy nguồn ngay bây giờ) rằng thuật toán xây dựng biểu đồ SQL Server kết hợp các bước biểu đồ khi nó hết chỗ cho chúng. Vì vậy, bạn có thể kết thúc với những thay đổi mạnh mẽ về số lượng bước chỉ bằng cách thêm một ít dữ liệu. Đây là một mẫu dữ liệu mà tôi thấy thú vị:
ROWS_IN_TABLE ROWS_SAMPLED STEPS
36661 36661 133
36662 36662 143
36663 36663 143
36664 36664 141
36665 36665 138
Ngay cả khi lấy mẫu với FULLSCAN
, việc thêm một hàng có thể tăng số bước lên 10, giữ cho nó không đổi, sau đó giảm 2 lần, sau đó giảm 3 bước.
Những gì chúng ta có thể tóm tắt từ tất cả những điều này? Tôi không thể chứng minh bất kỳ điều nào trong số này, nhưng những quan sát này dường như đúng:
- SQL Server sử dụng thuật toán sử dụng chung để tạo biểu đồ. Đối với một số phân phối dữ liệu, có thể tạo một bản trình bày dữ liệu đầy đủ hơn bằng tay.
- Nếu có dữ liệu NULL trong bảng và truy vấn thống kê tìm thấy dữ liệu đó thì dữ liệu NULL luôn có bước biểu đồ riêng.
- Giá trị tối thiểu được tìm thấy trong bảng sẽ có bước biểu đồ riêng với
RANGE_ROWS
= 0.
- Giá trị tối đa được tìm thấy trong bảng sẽ là giá trị cuối cùng
RANGE_HI_KEY
trong bảng.
- Vì SQL Server lấy mẫu nhiều dữ liệu hơn nên có thể cần kết hợp các bước hiện có để nhường chỗ cho dữ liệu mới mà nó tìm thấy. Nếu bạn nhìn vào biểu đồ đủ, bạn có thể thấy các giá trị phổ biến lặp lại cho
DISTINCT_RANGE_ROWS
hoặc RANGE_ROWS
. Ví dụ, 255 hiển thị một loạt các lần cho RANGE_ROWS
và DISTINCT_RANGE_ROWS
cho trường hợp thử nghiệm cuối cùng ở đây.
- Đối với các phân phối dữ liệu đơn giản, bạn có thể thấy SQL Server kết hợp dữ liệu tuần tự vào một bước biểu đồ không gây mất thông tin. Tuy nhiên, khi thêm các khoảng trống vào dữ liệu, biểu đồ có thể không điều chỉnh theo cách bạn mong muốn.
Khi nào tất cả điều này là một vấn đề? Đó là một vấn đề khi một truy vấn hoạt động kém do biểu đồ không thể biểu diễn phân phối dữ liệu theo cách để trình tối ưu hóa truy vấn đưa ra quyết định tốt. Tôi nghĩ rằng có xu hướng nghĩ rằng có nhiều bước biểu đồ hơn sẽ luôn tốt hơn và sẽ có sự phân biệt khi SQL Server tạo biểu đồ trên hàng triệu hàng trở lên nhưng không sử dụng chính xác 200 hoặc 201 bước biểu đồ. Tuy nhiên, tôi đã thấy rất nhiều vấn đề về chỉ số ngay cả khi biểu đồ có 200 hoặc 201 bước. Chúng tôi không có bất kỳ kiểm soát nào về số lượng bước biểu đồ mà SQL Server tạo cho một đối tượng thống kê vì vậy tôi sẽ không lo lắng về điều đó. Tuy nhiên, có một số bước bạn có thể thực hiện khi gặp phải các truy vấn hoạt động kém do vấn đề thống kê. Tôi sẽ đưa ra một cái nhìn tổng quan cực kỳ ngắn gọn.
Thu thập số liệu thống kê đầy đủ có thể giúp đỡ trong một số trường hợp. Đối với các bảng rất lớn, cỡ mẫu tự động có thể nhỏ hơn 1% số hàng trong bảng. Đôi khi điều đó có thể dẫn đến các kế hoạch xấu tùy thuộc vào sự gián đoạn dữ liệu trong cột. Tài liệu của microsofts về TẠO THỐNG KÊ và THỐNG KÊ CẬP NHẬT nói nhiều như sau:
SAMPLE hữu ích cho các trường hợp đặc biệt trong đó kế hoạch truy vấn, dựa trên lấy mẫu mặc định, không tối ưu. Trong hầu hết các tình huống, không cần thiết phải chỉ định SAMPLE vì trình tối ưu hóa truy vấn đã sử dụng lấy mẫu và xác định cỡ mẫu có ý nghĩa thống kê theo mặc định, theo yêu cầu để tạo các gói truy vấn chất lượng cao.
Đối với hầu hết các khối lượng công việc, không cần phải quét toàn bộ và lấy mẫu mặc định là đủ. Tuy nhiên, khối lượng công việc nhất định nhạy cảm với các phân phối dữ liệu khác nhau có thể yêu cầu tăng kích thước mẫu hoặc thậm chí quét toàn bộ.
Trong một số trường hợp tạo số liệu thống kê được lọc có thể giúp đỡ. Bạn có thể có một cột với dữ liệu sai lệch và nhiều giá trị khác biệt. Nếu có một số giá trị nhất định trong dữ liệu thường được lọc, bạn có thể tạo biểu đồ thống kê cho chỉ các giá trị chung đó. Trình tối ưu hóa truy vấn có thể sử dụng số liệu thống kê được xác định trên phạm vi dữ liệu nhỏ hơn thay vì số liệu thống kê được xác định trên tất cả các giá trị cột. Bạn vẫn không được đảm bảo có được 200 bước trong biểu đồ, nhưng nếu bạn tạo các số liệu thống kê được lọc trên chỉ một giá trị, bạn sẽ có một bước biểu đồ giá trị đó.
Sử dụng chế độ xem được phân vùng là một cách để có hiệu quả hơn 200 bước cho một bảng. Giả sử rằng bạn có thể dễ dàng chia một bảng lớn thành một bảng mỗi năm. Bạn tạo một UNION ALL
chế độ xem kết hợp tất cả các bảng hàng năm. Mỗi bảng sẽ có biểu đồ riêng. Lưu ý rằng số liệu thống kê gia tăng mới được giới thiệu trong SQL Server 2014 chỉ cho phép cập nhật thống kê hiệu quả hơn. Trình tối ưu hóa truy vấn sẽ không sử dụng số liệu thống kê được tạo trên mỗi phân vùng.
Có nhiều bài kiểm tra khác có thể được chạy ở đây, vì vậy tôi khuyến khích bạn thử nghiệm. Tôi đã thực hiện thử nghiệm này trên SQL Server 2014 express để thực sự không có gì ngăn cản bạn.