Loại dữ liệu tham số
Như đã đề cập trong phần đầu tiên của loạt bài này, một trong những lý do tốt hơn nên tham số hóa rõ ràng là để bạn có toàn quyền kiểm soát các kiểu dữ liệu tham số. Tham số hóa đơn giản có một số điểm khó trong lĩnh vực này, có thể dẫn đến nhiều kế hoạch tham số hóa được lưu vào bộ nhớ đệm hơn dự kiến hoặc tìm ra kết quả khác so với phiên bản chưa được tham số hóa.
Khi SQL Server áp dụng tham số hóa đơn giản đối với một câu lệnh đặc biệt, nó đưa ra phỏng đoán về kiểu dữ liệu của tham số thay thế. Tôi sẽ trình bày lý do phỏng đoán ở phần sau của loạt bài này.
Hiện tại, hãy xem xét một số ví dụ sử dụng cơ sở dữ liệu Stack Overflow 2010 trên SQL Server 2019 CU 14. Khả năng tương thích cơ sở dữ liệu được đặt thành 150 và ngưỡng chi phí cho tính song song được đặt thành 50 để tránh song song ngay bây giờ:
ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE; GOSELECT U.DisplayNameFROM dbo.Users AS U WHERE U.Reputation =252; GOSELECT U.DisplayNameFROM dbo.Usplay AS U WHERE U.Reputation =25221; GOSELECTers U.Disbo U WHERE U.Reputation =252552;
Các câu lệnh này dẫn đến sáu kế hoạch được lưu trong bộ nhớ cache, ba Adhoc và ba Chuẩn bị :
Các kiểu đoán khác nhau
Lưu ý các kiểu dữ liệu tham số khác nhau trong phần Đã chuẩn bị kế hoạch.
Suy luận Loại Dữ liệu
Chi tiết về cách đoán của từng loại dữ liệu rất phức tạp và được ghi chép lại không đầy đủ. Như một điểm khởi đầu, SQL Server suy ra một kiểu cơ bản từ biểu diễn văn bản của giá trị, sau đó sử dụng kiểu con tương thích nhỏ nhất.
Đối với một chuỗi số không có dấu ngoặc kép hoặc dấu thập phân, SQL Server chọn từ tinyint
, smallint
và integer
. Đối với những số như vậy nằm ngoài phạm vi của integer
, SQL Server sử dụng numeric
với độ chính xác nhỏ nhất có thể. Ví dụ:số 2,147,483,648 được nhập là numeric(10,0)
. bigint
loại không được sử dụng để tham số phía máy chủ. Đoạn này giải thích các kiểu dữ liệu được chọn trong các ví dụ trước.
Chuỗi số với dấu thập phân được hiểu là numeric
, với độ chính xác và tỷ lệ vừa đủ lớn để chứa giá trị được cung cấp. Các chuỗi có tiền tố là ký hiệu tiền tệ được hiểu là money
. Các chuỗi trong ký hiệu khoa học dịch sang float
. smallmoney
và real
các loại không được sử dụng.
datetime
và uniqueidentifer
loại không thể được suy ra từ các định dạng chuỗi tự nhiên. Để lấy datetime
hoặc uniqueidentifier
loại tham số, giá trị chữ phải được cung cấp ở định dạng thoát ODBC. Ví dụ:{d '1901-01-01'}
, {ts '1900-01-01 12:34:56.790'}
hoặc {guid 'F85C72AB-15F7-49E9-A949-273C55A6C393'}
. Nếu không, ngày dự định hoặc ký tự UUID được nhập dưới dạng chuỗi. Các loại ngày và giờ khác với datetime
không được sử dụng.
Chuỗi chung và các ký tự nhị phân được nhập là varchar(8000)
, nvarchar(4000)
hoặc varbinary(8000)
nếu thích hợp, trừ khi ký tự vượt quá 8000 byte, trong trường hợp đó, max
biến thể được sử dụng. Lược đồ này giúp tránh ô nhiễm bộ nhớ cache và mức độ tái sử dụng thấp do sử dụng độ dài cụ thể.
Không thể sử dụng CAST
hoặc CONVERT
để đặt loại dữ liệu cho các tham số vì lý do tôi sẽ trình bày chi tiết ở phần sau của loạt bài này. Có một ví dụ về điều này trong phần tiếp theo.
Tôi sẽ không đề cập đến tham số bắt buộc trong loạt bài này, nhưng tôi muốn đề cập đến các quy tắc suy luận kiểu dữ liệu trong trường hợp đó có một số khác biệt quan trọng so với tham số hóa đơn giản . Tham số bắt buộc không được thêm vào cho đến SQL Server 2005, vì vậy Microsoft đã có cơ hội kết hợp một số bài học từ tham số hóa đơn giản kinh nghiệm và không phải lo lắng nhiều về các vấn đề tương thích ngược.
Loại số
Đối với các số có dấu thập phân và các số nguyên nằm ngoài phạm vi integer
, các quy tắc loại được suy luận đưa ra các vấn đề đặc biệt đối với việc tái sử dụng kế hoạch và ô nhiễm bộ nhớ cache.
Hãy xem xét truy vấn sau sử dụng số thập phân:
> =987.65432 VÀ T.SomeValue <123456.789;
Truy vấn này đủ điều kiện cho tham số hóa đơn giản . SQL Server chọn độ chính xác và tỷ lệ nhỏ nhất cho các tham số có thể chứa các giá trị được cung cấp. Điều này có nghĩa là nó chọn numeric(8,5)
cho 987.65432
và numeric(9,3)
cho 123456.789
:
Kiểu dữ liệu số được suy ra
Các loại suy luận này không khớp với decimal(19,8)
loại cột, vì vậy chuyển đổi xung quanh tham số sẽ xuất hiện trong kế hoạch thực thi:
Chuyển đổi sang loại cột
Những chuyển đổi này chỉ thể hiện sự kém hiệu quả trong thời gian chạy nhỏ trong trường hợp cụ thể này. Trong các tình huống khác, sự không khớp giữa kiểu dữ liệu cột và kiểu suy luận của một tham số có thể ngăn tìm kiếm chỉ mục hoặc yêu cầu SQL Server thực hiện thêm công việc để tạo tìm kiếm động.
Ngay cả khi kế hoạch thực hiện kết quả có vẻ hợp lý, sự không phù hợp về kiểu có thể dễ dàng ảnh hưởng đến chất lượng của kế hoạch do tác động của kiểu không phù hợp đối với ước tính bản số. Tốt nhất bạn nên sử dụng các kiểu dữ liệu phù hợp và chú ý cẩn thận đến các kiểu bắt nguồn từ các biểu thức.
Kế hoạch Tái sử dụng
Vấn đề chính với kế hoạch hiện tại là các loại được suy ra cụ thể ảnh hưởng đến việc đối sánh kế hoạch được lưu trong bộ nhớ cache và do đó sử dụng lại. Hãy chạy thêm một vài truy vấn có cùng dạng chung:
CHỌN T.SomeValue TỪ dbo.Test AS T WHERE T.SomeValue> =98,76 VÀ T.SomeValue <123,4567; GOSELECT T.SomeValue TỪ dbo.Test AS T WHERE T.SomeValue> =1.2 VÀ T.SomeValue <1234.56789; ĐI
Bây giờ hãy nhìn vào bộ nhớ cache của kế hoạch:
CHỌN CP.usecounts, CP.objtype, ST. [text] FROM sys.dm_exec_cached_plans NHƯ CPCROSS ÁP DỤNG sys.dm_exec_sql_text (CP.plan_handle) NHƯ Ở ĐÂU. [text] KHÔNG THÍCH '% dm_exec_cached_plans%' VÀ ST. text] LIKE '% SomeValue% Test%' ĐẶT HÀNG THEO CP.objtype ASC;
Nó hiển thị một AdHoc và Đã chuẩn bị tuyên bố cho mỗi truy vấn chúng tôi đã gửi:
Các câu lệnh được chuẩn bị riêng
Văn bản được tham số giống nhau, nhưng các kiểu dữ liệu tham số khác nhau, do đó các kế hoạch riêng biệt được lưu vào bộ nhớ đệm và không xảy ra việc sử dụng lại kế hoạch.
Nếu chúng tôi tiếp tục gửi các truy vấn với các kết hợp tỷ lệ hoặc độ chính xác khác nhau, thì một Chuẩn bị mới kế hoạch sẽ được tạo và lưu vào bộ nhớ cache mỗi lần. Hãy nhớ rằng loại suy luận của mỗi thông số không bị giới hạn bởi loại dữ liệu cột, vì vậy, chúng tôi có thể kết thúc với một số lượng lớn các kế hoạch được lưu trong bộ nhớ cache, tùy thuộc vào các ký tự số được gửi. Số lượng kết hợp từ numeric(1,0)
thành numeric(38,38)
đã lớn trước khi chúng ta nghĩ đến nhiều tham số.
Tham số rõ ràng
Vấn đề này không phát sinh khi chúng tôi sử dụng tham số hóa rõ ràng, lý tưởng là chọn cùng một kiểu dữ liệu như cột mà tham số được so sánh với:
ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE; GODECLARE @stmt nvarchar (4000) =N'SELECT T.SomeValue FROM dbo.Test AS T WHERE T.SomeValue> =@ P1 VÀ T.SomeValue <@ P2; ', @params nvarchar (4000) =N '@ P1 số (19,8), @ P2 số (19,8)'; THỰC HIỆN sys.sp_executesql @stmt, @params, @ P1 =987.65432, @ P2 =123456.789; THỰC HIỆN sys.sp_executesql @stmt, @params, @ P1 =98,76, @ P2 =123,4567; THỰC HIỆN sys.sp_executesql @stmt, @params, @ P1 =1.2, @ P2 =1234.56789;
Với tham số hóa rõ ràng, truy vấn bộ nhớ cache kế hoạch chỉ hiển thị một kế hoạch được lưu trong bộ nhớ cache, được sử dụng ba lần và không cần chuyển đổi loại:
Tham số rõ ràng
Lưu ý cuối cùng, tôi đã sử dụng decimal
và numeric
thay thế cho nhau trong phần này. Họ về mặt kỹ thuật các loại khác nhau, mặc dù được ghi nhận là từ đồng nghĩa và hoạt động tương đương nhau. Trường hợp này thường xảy ra, nhưng không phải lúc nào cũng vậy:
- Tăng lỗi 8120:- Cột 'dbo.Test.SomeValue' không hợp lệ trong danh sách chọn - vì nó không có trong hàm tổng hợp - hoặc mệnh đề GROUP BY.SELECT CONVERT (decimal ( 19,8), T.SomeValue) TỪ NHÓM dbo.Test AS T BẰNG CHUYỂN ĐỔI (số (19,8), T.SomeValue);
Đó có thể là một lỗi phân tích cú pháp nhỏ, nhưng nó vẫn phải trả để nhất quán (trừ khi bạn đang viết một bài báo và muốn chỉ ra một ngoại lệ thú vị).
Toán tử Số học
Có một trường hợp khác mà tôi muốn giải quyết, dựa trên một ví dụ được đưa ra trong tài liệu, nhưng chi tiết hơn một chút (và có lẽ là độ chính xác):
- Bảng dbo.LinkTypes chứa hai hàng - Sử dụng tham số hóa đơn giảnSELECT r =CONVERT (float, 1/7) FROM dbo.LinkTypes AS LT; - Không có tham số hóa đơn giản do-- so sánh hằng-hằngSELECT r =CONVERT (float, 1/7) FROM dbo.LinkTypes AS LT WHERE 1 =1;
Các kết quả khác nhau, như được ghi lại:
Các kết quả khác nhau
Với Tham số Đơn giản
Khi tham số hóa đơn giản xảy ra, SQL Server tham số hóa cả hai giá trị theo nghĩa đen. 1.
giá trị được nhập là numeric(1,0)
như mong đợi. Hơi mâu thuẫn, 7
được nhập là integer
(không phải tinyint
). Các quy tắc của kiểu suy luận đã được xây dựng theo thời gian, bởi các nhóm khác nhau. Các hành vi được duy trì để tránh phá vỡ mã kế thừa.
Bước tiếp theo liên quan đến /
toán tử số học. SQL Server yêu cầu các kiểu tương thích trước khi thực hiện phân chia. Đã cho numeric
(decimal
) có mức độ ưu tiên kiểu dữ liệu cao hơn integer
, integer
sẽ được chuyển đổi thành numeric
.
SQL Server cần chuyển đổi ngầm định số nguyên integer
thành numeric
. Nhưng sử dụng độ chính xác và quy mô nào? Câu trả lời có thể dựa trên nghĩa gốc, như SQL Server làm trong các trường hợp khác, nhưng nó luôn sử dụng numeric(10)
tại đây.
Kiểu dữ liệu của kết quả chia numeric(1,0)
bởi một numeric(10,0)
được xác định bởi người khác tập hợp các quy tắc, được đưa ra trong tài liệu về độ chính xác, tỷ lệ và độ dài. Cắm các con số vào các công thức cho độ chính xác của kết quả và tỷ lệ được cung cấp ở đó, chúng ta có:
- Độ chính xác của kết quả:
- p1 - s1 + s2 + max (6, s1 + p2 + 1)
- =1 - 0 + 0 + tối đa (6, 0 + 10 + 1)
- =1 + tối đa (6, 11)
- =1 + 11
- = 12
- Thang điểm kết quả:
- tối đa (6, s1 + p2 + 1)
- =max (6, 0 + 10 + 1)
- =max (6, 11)
- = 11
Kiểu dữ liệu của 1. / 7
do đó, là numeric(12, 11)
. Giá trị này sau đó được chuyển đổi thành float
theo yêu cầu và được hiển thị dưới dạng 0.14285714285
(có 11 chữ số sau dấu thập phân).
Không có tham số đơn giản
Khi tham số hóa đơn giản không được thực hiện, 1.
chữ được nhập là numeric(1,0)
như trước. 7
ban đầu được nhập là integer
cũng như đã thấy trước đây. Sự khác biệt chính là integer
được chuyển đổi thành numeric(1,0)
, vì vậy toán tử phân chia có các kiểu phổ biến để làm việc. Đây là độ chính xác và tỷ lệ nhỏ nhất có thể chứa giá trị 7
. Hãy nhớ tham số hóa đơn giản được sử dụng numeric(10,0)
tại đây.
Các công thức về độ chính xác và tỷ lệ để chia numeric(1,0)
bởi numeric(1,0)
đưa ra một kiểu dữ liệu kết quả là numeric(7,6)
:
- Độ chính xác của kết quả:
- p1 - s1 + s2 + max (6, s1 + p2 + 1)
- =1 - 0 + 0 + tối đa (6, 0 + 1 + 1)
- =1 + tối đa (6, 2)
- =1 + 6
- = 7
- Thang điểm kết quả:
- tối đa (6, s1 + p2 + 1)
- =max (6, 0 + 1 + 1)
- =max (6, 2)
- = 6
Sau lần chuyển đổi cuối cùng thành float
, kết quả hiển thị là 0.142857
(có sáu chữ số sau dấu thập phân).
Do đó, sự khác biệt quan sát được trong kết quả là do dẫn xuất kiểu tạm thời (numeric(12,11)
so với numeric(7,6)
) thay vì chuyển đổi cuối cùng thành float
.
Nếu bạn cần thêm bằng chứng, hãy chuyển đổi thành float
không chịu trách nhiệm, hãy xem xét:
- Tham số đơn giảnSELECT r =CONVERT (thập phân (13,12), 1/7) FROM dbo.LinkTypes AS LT; - Không có tham số đơn giảnSELECT r =CONVERT (thập phân (13,12), 1/7) FROM dbo.LinkTypes AS LT OPTION (MAXDOP 1);
Kết quả có số thập phân
Các kết quả khác nhau về giá trị và tỷ lệ so với trước đây.
Phần này không bao gồm mọi suy luận và chuyển đổi kiểu dữ liệu kỳ quặc với tham số hóa đơn giản bằng mọi cách. Như đã nói trước đây, bạn nên sử dụng các thông số rõ ràng với các loại dữ liệu đã biết nếu có thể.
Kết thúc Phần 2
Phần tiếp theo của loạt bài này mô tả cách tham số hóa đơn giản ảnh hưởng đến kế hoạch thực hiện.