Câu trả lời điển hình là thêm mệnh đề WHERE:
WHERE ISDATE(a.valor) = 1
Tuy nhiên, điều này có vấn đề trong tình huống của bạn vì một số lý do:
-
ISDATE()
sẽ không nhất thiết phải khớp theo cách bạn muốn tùy thuộc vào cài đặt khu vực của máy chủ, ngôn ngữ của người dùng hoặc tùy chọn định dạng ngày, v.v. Ví dụ:SET DATEFORMAT dmy; SELECT ISDATE('13/01/2012'); -- 1 SET DATEFORMAT mdy; SELECT ISDATE('13/01/2012'); -- 0
-
Bạn thực sự không thể kiểm soát rằng SQL Server sẽ thử và thực hiện
CONVERT
sau bộ lọc.
Bạn thậm chí không thể sử dụng truy vấn con hoặc CTE để thử và tách bộ lọc khỏi CHUYỂN ĐỔI vì SQL Server có thể tối ưu hóa các hoạt động trong truy vấn theo bất kỳ thứ tự nào mà nó cho là hiệu quả hơn.
Ví dụ:với một mẫu giới hạn, bạn có thể sẽ thấy rằng điều này hoạt động ổn:
SET DATEFORMAT dmy;
SELECT valor, valor_date FROM (
SELECT valor, valor_date = CONVERT(DATE,
CASE WHEN ISDATE(valor) = 1 THEN valor ELSE NULL END, 103)
FROM dbo.mytable
WHERE ISDATE(valor) = 1
) AS sub WHERE valor_date BETWEEN '01/01/2012' AND '01/03/2012';
Nhưng tôi đã gặp các trường hợp với ngay cả cấu trúc này trong đó SQL Server đã cố gắng đánh giá bộ lọc trước, dẫn đến lỗi tương tự mà bạn hiện đang gặp phải.
Một số cách giải quyết an toàn hơn:
Thêm một cột được tính toán, ví dụ:
ALTER TABLE dbo.mytable ADD valor_date
AS CONVERT(DATE, CASE WHEN ISDATE(valor) = 1 THEN valor
ELSE NULL END, 103);
Để bảo vệ bản thân khỏi các hiểu sai có thể xảy ra trong thời gian chạy, bạn nên chỉ định định dạng ngày trước khi đưa ra truy vấn tham chiếu đến cột được tính toán, ví dụ:
SET DATEFORMAT dmy;
SELECT valor, valor_date FROM dbo.mytable WHERE ...;
Tạo chế độ xem:
CREATE VIEW dbo.myview
AS
SELECT valor, valor_date = CONVERT(DATE,
CASE WHEN ISDATE(valor) = 1 THEN valor ELSE NULL END, 103)
FROM dbo.mytable
WHERE ISDATE(valor) = 1;
Một lần nữa, bạn sẽ muốn phát hành một SET DATEFORMAT
khi truy vấn chế độ xem.
Sử dụng bảng tạm thời:
SELECT <cols>
INTO #foo
FROM dbo.mytable
WHERE ISDATE(valor) = 1;
SELECT <cols>, CONVERT(DATE, valor) FROM #foo WHERE ...;
Bạn vẫn có thể muốn sử dụng DATEFORMAT
để bảo vệ bạn khỏi xung đột giữa ISDATE
và cài đặt người dùng.
Và không, bạn không nên không cố gắng xác thực các chuỗi của bạn dưới dạng ngày tháng bằng cách sử dụng đối sánh mẫu chuỗi như đã được đề xuất trong một câu trả lời khác (hiện đã bị xóa):
like '%__/%' or like '%/%'
Bạn sẽ phải có một số xác thực khá phức tạp và nặng tay ở đó để xử lý tất cả các ngày hợp lệ bao gồm cả năm nhuận.