getdate()
là một thời gian chạy hàm không đổi
và chỉ được đánh giá một lần cho mỗi tham chiếu hàm, đó là lý do tại sao
SELECT GETDATE()
FROM SomeBigTable
sẽ trả về cùng một kết quả cho tất cả các hàng bất kể thời gian chạy truy vấn.
Tuy nhiên, có một sự khác biệt giữa cả hai. Vì kế hoạch đầu tiên sử dụng một biến và kế hoạch được biên dịch trước khi biến được gán cho SQL Server sẽ (trong trường hợp không có biên dịch lại) giả định rằng 30% số hàng sẽ được trả về. Phỏng đoán này có thể khiến nó sử dụng một kế hoạch khác với truy vấn thứ hai.
Cần lưu ý điều gì đó khi sử dụng GETDATE()
trực tiếp trong bộ lọc là nó đánh giá GETDATE()
tại thời điểm biên dịch và sau đó, độ chọn lọc có thể thay đổi đáng kể mà không cần truy vấn hoặc dữ liệu thay đổi để kích hoạt biên dịch lại. Trong ví dụ bên dưới đối với bảng 1.000 hàng, truy vấn sử dụng một biến dẫn đến một kế hoạch với ước tính 300 hàng và quét toàn bộ bảng trong khi truy vấn với lệnh gọi hàm được nhúng ước tính 1 hàng và thực hiện tra cứu dấu trang. Điều này chính xác trong lần chạy đầu tiên nhưng trong lần chạy thứ hai do thời gian trôi qua, giờ đây tất cả các hàng đều đủ điều kiện và kết thúc là 1.000 lần tìm kiếm ngẫu nhiên như vậy.
USE tempdb;
CREATE TABLE [myTable]
(
CreatedDate datetime,
Filler char(8000) NULL
)
CREATE NONCLUSTERED INDEX ix ON [myTable](CreatedDate)
INSERT INTO [myTable](CreatedDate)
/*Insert 1 row that initially qualifies*/
SELECT DATEADD(D,-2001,getdate())
UNION ALL
/*And 999 rows that don't initially qualify*/
SELECT TOP 999 DATEADD(minute,1, DATEADD(D,-2000,getdate()))
FROM master..spt_values
EXEC('
DECLARE @myDate DATETIME = DATEADD(D,-2000,getdate())
SELECT *
FROM [myTable]
WHERE CreatedDate <= @myDate
')
EXEC('
SELECT *
FROM [myTable]
WHERE CreatedDate <= DATEADD(D,-2000,getdate())
')
RAISERROR ('Delay',0,1) WITH NOWAIT
WAITFOR DELAY '00:01:01'
EXEC('
DECLARE @myDate DATETIME = DATEADD(D,-2000,getdate())
SELECT *
FROM [myTable]
WHERE CreatedDate <= @myDate
')
EXEC('
SELECT *
FROM [myTable]
WHERE CreatedDate <= DATEADD(D,-2000,getdate())
')
DROP TABLE [myTable]