Bắt đầu với truy vấn ban đầu của bạn:
declare
@lower numeric(18,0) = 1000,
@upper numeric(18,0) = 1005;
select * from [messages]
where msg_id between @lower+1 and @upper;
Cái 1
mà bạn đã thêm có một kiểu dữ liệu integer
theo mặc định. Khi thêm một integer
giá trị vào một numeric(18,0)
giá trị SQL Server sẽ áp dụng các quy tắc ưu tiên kiểu dữ liệu . int
có mức độ ưu tiên thấp hơn để nó được chuyển đổi thành a numeric(1,0)
. Truy vấn của bạn tương đương như sau:
declare
@lower numeric(18,0) = 1000,
@upper numeric(18,0) = 1005;
select * from [messages]
where msg_id between @lower+CAST(1 AS NUMERIC(1, 0)) and @upper;
Một bộ quy tắc khác nhau xung quanh Độ chính xác, tỷ lệ và Độ dài được áp dụng để xác định loại dữ liệu của biểu thức liên quan @lower
. Không an toàn khi chỉ sử dụng NUMERIC(18,0)
vì có thể bị tràn (xem xét 999.999.999.999.999.999 và 1 làm ví dụ). Quy tắc áp dụng ở đây là:
╔═══════════╦═════════════════════════════════════╦════════════════╗
║ Operation ║ Result precision ║ Result scale * ║
╠═══════════╬═════════════════════════════════════╬════════════════╣
║ e1 + e2 ║ max(s1, s2) + max(p1-s1, p2-s2) + 1 ║ max(s1, s2) ║
╚═══════════╩═════════════════════════════════════╩════════════════╝
Đối với biểu thức của bạn, độ chính xác kết quả là:
max(0, 0) + max(18 - 0, 1 - 0) + 1 = 0 + 18 + 1 = 19
và thang đo kết quả là 0. Bạn có thể xác minh điều này bằng cách chạy đoạn mã sau trong SQL Server:
declare
@lower numeric(18,0) = 1000,
@upper numeric(18,0) = 1005;
SELECT
SQL_VARIANT_PROPERTY(@lower+1, 'BaseType') lower_exp_BaseType
, SQL_VARIANT_PROPERTY(@lower+1, 'Precision') lower_exp_Precision
, SQL_VARIANT_PROPERTY(@lower+1, 'Scale') lower_exp_Scale;
Điều này có nghĩa là truy vấn ban đầu của bạn tương đương như sau:
declare
@lower numeric(19,0) = 1000 + 1,
@upper numeric(18,0) = 1005;
select * from [messages]
where msg_id between @lower and @upper;
SQL Server chỉ có thể sử dụng @lower
để thực hiện tìm kiếm chỉ mục cụm nếu giá trị có thể được chuyển đổi hoàn toàn thành NUMERIC(18, 0)
. Nó không an toàn để chuyển đổi một NUMERIC(19,0)
giá trị sang NUMERIC(18,0)
. Kết quả là giá trị được áp dụng như một vị từ thay vì như một vị từ tìm kiếm. Một cách giải quyết là làm như sau:
declare
@lower numeric(18,0) = 1000,
@upper numeric(18,0) = 1005;
select * from [messages]
where msg_id between TRY_CAST(@lower+1 AS NUMERIC(18,0)) and @upper;
Truy vấn đó có thể xử lý cả hai bộ lọc dưới dạng tìm kiếm vị từ:
Lời khuyên của tôi là thay đổi kiểu dữ liệu trong bảng thành BIGINT
nếu có thể. BIGINT
yêu cầu ít hơn một byte so với NUMERIC(18,0)
và lợi ích từ tối ưu hóa hiệu suất không có sẵn để NUMERIC(18,0)
bao gồm hỗ trợ tốt hơn cho các bộ lọc bitmap.