Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Hành vi kế hoạch truy vấn bảng tạm thời của SQL Server 2016

Có phiên bản SQL Server mới không phải là điều tuyệt vời phải không? Đây là điều chỉ xảy ra vài năm một lần và trong tháng này, chúng tôi đã thấy một sản phẩm đạt được Tình trạng sẵn có chung. (Được rồi, tôi biết rằng chúng tôi nhận được phiên bản mới của Cơ sở dữ liệu SQL trong Azure gần như liên tục, nhưng tôi tính đây là phiên bản khác.) Công nhận bản phát hành mới này, ngày thứ Ba T-SQL của tháng này (do Michael Swart - @mjswart tổ chức) là chủ đề về mọi thứ SQL Server 2016!

Vì vậy, hôm nay tôi muốn xem xét tính năng Bảng tạm thời của SQL 2016 và xem xét một số tình huống kế hoạch truy vấn mà bạn có thể thấy. Tôi yêu Bàn tạm thời, nhưng đã bắt gặp một chút lỗi mà bạn có thể muốn biết.

Bây giờ, mặc dù thực tế là SQL Server 2016 hiện đã ở trong RTM, tôi đang sử dụng AdventureWorks2016CTP3, bạn có thể tải xuống tại đây - nhưng không chỉ tải xuống AdventureWorks2016CTP3.bak , cũng lấy SQLServer2016CTP3Samples.zip từ cùng một trang web.

Bạn thấy đấy, trong kho lưu trữ Mẫu, có một số tập lệnh hữu ích để thử các tính năng mới, bao gồm một số tập lệnh cho Bảng tạm thời. Đôi bên cùng có lợi - bạn được thử một loạt các tính năng mới và tôi không phải lặp lại quá nhiều kịch bản trong bài đăng này. Dù sao, hãy đi và lấy hai tập lệnh về Bảng tạm thời, chạy AW 2016 CTP3 Temporal Setup.sql , theo sau là Temporal System-Versioning Sample.sql .

Các tập lệnh này thiết lập các phiên bản tạm thời của một số bảng, bao gồm HumanResources.Employee . Nó tạo ra HumanResources.Employee_Temporal (mặc dù, về mặt kỹ thuật, nó có thể được gọi là bất cứ thứ gì). Ở cuối CREATE TABLE câu lệnh, bit này xuất hiện, thêm hai cột ẩn để sử dụng để cho biết thời điểm hàng hợp lệ và cho biết rằng một bảng sẽ được tạo có tên HumanResources.Employee_Temporal_History để lưu trữ các phiên bản cũ.

  ...
  ValidFrom datetime2(7) GENERATED ALWAYS AS ROW START HIDDEN NOT NULL,
  ValidTo datetime2(7)   GENERATED ALWAYS AS ROW END   HIDDEN NOT NULL,
  PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)
) WITH (SYSTEM_VERSIONING = ON 
  (HISTORY_TABLE = [HumanResources].[Employee_Temporal_History])
);

Điều tôi muốn khám phá trong bài đăng này là điều gì xảy ra với các kế hoạch truy vấn khi lịch sử được sử dụng.

Nếu tôi truy vấn bảng để xem hàng mới nhất cho một BusinessEntityID cụ thể , Tôi nhận được Tìm kiếm chỉ mục theo cụm, như mong đợi.

SELECT e.BusinessEntityID, e.ValidFrom, e.ValidTo
FROM HumanResources.Employee_Temporal AS e
WHERE e.BusinessEntityID = 4;

Tôi chắc chắn rằng tôi có thể truy vấn bảng này bằng cách sử dụng các chỉ mục khác, nếu nó có bất kỳ chỉ mục nào. Nhưng trong trường hợp này thì không. Hãy tạo một cái.

CREATE UNIQUE INDEX rf_ix_Login 
on HumanResources.Employee_Temporal(LoginID);

Bây giờ tôi có thể truy vấn bảng bằng LoginID và sẽ thấy Tra cứu khóa nếu tôi yêu cầu các cột khác ngoài Loginid hoặc BusinessEntityID . Điều này không đáng ngạc nhiên.

SELECT * FROM HumanResources.Employee_Temporal e
WHERE e.LoginID = N'adventure-works\rob0';

Hãy sử dụng SQL Server Management Studio trong một phút và xem bảng này trông như thế nào trong Object Explorer.

Chúng ta có thể thấy bảng Lịch sử được đề cập trong HumanResources.Employee_Temporal và các cột và chỉ mục từ cả bảng và bảng lịch sử. Nhưng trong khi các chỉ mục trên bảng thích hợp là Khóa chính (trên BusinessEntityID ) và chỉ mục tôi vừa tạo, bảng Lịch sử không có chỉ mục phù hợp.

Chỉ mục trên bảng lịch sử nằm trên ValidToValidFrom . Chúng tôi có thể nhấp chuột phải vào chỉ mục và chọn Thuộc tính, và chúng tôi thấy hộp thoại này:

Một hàng mới được chèn vào bảng Lịch sử này khi nó không còn hợp lệ trong bảng chính vì nó vừa bị xóa hoặc thay đổi. Các giá trị trong ValidTo cột được điền tự nhiên với thời gian hiện tại, vì vậy ValidTo hoạt động như một khóa tăng dần, giống như một cột nhận dạng, để các phần chèn mới xuất hiện ở cuối cấu trúc b-tree.

Nhưng điều này hoạt động như thế nào khi bạn muốn truy vấn bảng?

Nếu chúng ta muốn truy vấn bảng của mình về những gì hiện tại tại một thời điểm cụ thể, thì chúng ta nên sử dụng cấu trúc truy vấn như:

SELECT * FROM HumanResources.Employee_Temporal
FOR SYSTEM_TIME AS OF '20160612 11:22';

Truy vấn này cần nối các hàng thích hợp từ bảng chính với các hàng thích hợp từ bảng lịch sử.

Trong trường hợp này, các hàng hợp lệ tại thời điểm tôi chọn đều nằm trong bảng lịch sử, nhưng tuy nhiên, chúng ta thấy Bảng quét chỉ mục được phân cụm dựa trên bảng chính, được lọc bởi toán tử Bộ lọc. Vị từ của bộ lọc này là:

[HumanResources].[Employee_Temporal].[ValidFrom] <= '2016-06-12 11:22:00.0000000' 
AND [HumanResources].[Employee_Temporal].[ValidTo] > '2016-06-12 11:22:00.0000000'

Hãy xem lại điều này trong giây lát.

Tìm kiếm chỉ mục theo cụm trên bảng Lịch sử rõ ràng phải tận dụng một Dự đoán Tìm kiếm trên ValidTo. Bắt đầu Quét phạm vi của Tìm kiếm là HumanResources.Employee_Temporal_History.ValidTo > Toán tử vô hướng ('2016-06-12 11:22:00') , nhưng không có Kết thúc, bởi vì mọi hàng có ValidTo sau thời gian chúng tôi quan tâm là hàng ứng cử viên và phải được kiểm tra để tìm ValidFrom thích hợp giá trị bằng Vị từ dư, là HumanResources.Employee_Temporal_History.ValidFrom <= '2016-06-12 11:22:00' .

Bây giờ, các khoảng thời gian rất khó lập chỉ mục; đó là một điều đã biết đã được thảo luận trên nhiều blog. Hầu hết các giải pháp hiệu quả đều xem xét những cách sáng tạo để viết truy vấn, nhưng không có phương pháp thông minh nào như vậy được tích hợp vào Bảng tạm thời. Tuy nhiên, bạn cũng có thể đặt chỉ mục trên các cột khác, chẳng hạn như trên ValidFrom, hoặc thậm chí có chỉ mục phù hợp với các loại truy vấn bạn có thể có trên bảng chính. Với chỉ mục được phân nhóm là khóa tổng hợp trên cả ValidToValidFrom , hai cột này được đưa vào mọi cột khác, tạo cơ hội tốt cho một số thử nghiệm Vị trí dư.

Nếu tôi biết mình quan tâm đến loginid nào, kế hoạch của tôi sẽ có một hình dạng khác.

Nhánh trên cùng của toán tử Nối trông tương tự như trước đây, mặc dù toán tử Bộ lọc đó đã nhập cuộc xung đột để loại bỏ bất kỳ hàng nào không hợp lệ, nhưng Tìm kiếm chỉ mục theo cụm ở nhánh dưới có Cảnh báo. Đây là cảnh báo Vị từ dư, giống như các ví dụ trong một bài đăng trước của tôi. Nó có thể lọc các mục nhập có hiệu lực cho đến một thời điểm nào đó sau thời gian mà chúng tôi quan tâm, nhưng Chức năng dự đoán còn lại hiện lọc thành LoginID cũng như ValidFrom .

[HumanResources].[Employee_Temporal_History].[ValidFrom] <= '2016-06-12 11:22:00.0000000' 
AND [HumanResources].[Employee_Temporal_History].[LoginID] = N'adventure-works\rob0'

Các thay đổi đối với các hàng của rob0 sẽ chỉ là một tỷ lệ nhỏ so với các hàng trong Lịch sử. Cột này sẽ không duy nhất như trong bảng chính, vì hàng có thể đã được thay đổi nhiều lần, nhưng vẫn có một ứng cử viên tốt để lập chỉ mục.

CREATE INDEX rf_ixHist_loginid
ON HumanResources.Employee_Temporal_History(LoginID);

Chỉ số mới này có ảnh hưởng đáng chú ý đến kế hoạch của chúng tôi.

Giờ đây, nó đã thay đổi Tìm kiếm chỉ mục theo cụm của chúng tôi thành Quét chỉ mục theo cụm !!

Bạn thấy đấy, Trình tối ưu hóa Truy vấn hiện đã phát hiện ra rằng điều tốt nhất nên làm là sử dụng chỉ mục mới. Nhưng nó cũng quyết định rằng nỗ lực tìm kiếm để lấy tất cả các cột khác (vì tôi đang yêu cầu tất cả các cột) sẽ đơn giản là quá nhiều việc. Đã đạt đến điểm giới hạn (thật đáng tiếc là một giả định không chính xác trong trường hợp này) và thay vào đó, bạn đã chọn QUÉT chỉ mục theo cụm. Ngay cả khi không có chỉ mục không phân cụm, tùy chọn tốt nhất sẽ là sử dụng Tìm kiếm chỉ mục được phân cụm, khi chỉ mục không được phân nhóm đã được xem xét và từ chối vì lý do tới hạn, nó sẽ chọn quét.

Thật thất vọng, tôi chỉ mới tạo chỉ mục này và số liệu thống kê của nó phải tốt. Nên biết rằng Tìm kiếm yêu cầu chính xác một lần tra cứu sẽ tốt hơn so với Quét chỉ mục theo cụm (chỉ bằng số liệu thống kê - nếu bạn đang nghĩ nó nên biết điều này vì LoginID là duy nhất trong bảng chính, hãy nhớ rằng nó có thể không phải lúc nào cũng vậy). Vì vậy, tôi nghi ngờ rằng nên tránh tra cứu trong bảng lịch sử, mặc dù tôi chưa thực hiện đầy đủ nghiên cứu về vấn đề này.

Bây giờ chúng ta chỉ truy vấn các cột xuất hiện trong chỉ mục không phân cụm của chúng ta, chúng ta sẽ có được hành vi tốt hơn nhiều. Giờ đây, không cần tra cứu, chỉ mục mới của chúng tôi trên bảng lịch sử đã được sử dụng một cách vui vẻ. Nó vẫn cần áp dụng Vị trí dư thừa dựa trên việc chỉ có thể lọc thành LoginIDValidTo , nhưng nó hoạt động tốt hơn nhiều so với việc rơi vào Quét chỉ mục theo cụm.

SELECT LoginID, ValidFrom, ValidTo
FROM HumanResources.Employee_Temporal
FOR SYSTEM_TIME AS OF '20160612 11:22'
WHERE LoginID = N'adventure-works\rob0'

Vì vậy, hãy lập chỉ mục các bảng lịch sử của bạn theo nhiều cách khác, xem xét cách bạn sẽ truy vấn chúng. Bao gồm các cột cần thiết để tránh tra cứu, vì bạn đang thực sự tránh Quét.

Các bảng lịch sử này có thể lớn lên nếu dữ liệu thường xuyên thay đổi. Vì vậy, hãy lưu ý đến cách chúng đang được xử lý. Trường hợp tương tự cũng xảy ra khi sử dụng FOR SYSTEM_TIME khác cấu trúc, vì vậy bạn nên (như mọi khi) xem lại các kế hoạch mà truy vấn của bạn đang tạo ra và lập chỉ mục để đảm bảo rằng bạn đang ở vị trí tốt để tận dụng tính năng rất mạnh mẽ của SQL Server 2016.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Thực thi một tập lệnh SQL lớn (với các lệnh GO)

  2. Làm cách nào để tạo một ràng buộc duy nhất cũng cho phép null?

  3. Chuyển đổi tên tháng thành số tháng trong SQL Server

  4. Stuff và 'For Xml Path' hoạt động như thế nào trong SQL Server?

  5. Làm cách nào để chuyển giá trị cho một tham số thủ tục được lưu trữ trong thành phần Nguồn OLE DB?