Jawaban umumnya adalah menambahkan klausa WHERE:
WHERE ISDATE(a.valor) = 1
Namun ini bermasalah dalam situasi Anda karena beberapa alasan:
-
ISDATE()
belum tentu cocok dengan yang Anda inginkan tergantung pada pengaturan regional server, bahasa pengguna atau opsi format tanggal, dll. Misalnya:SET DATEFORMAT dmy; SELECT ISDATE('13/01/2012'); -- 1 SET DATEFORMAT mdy; SELECT ISDATE('13/01/2012'); -- 0
-
Anda tidak dapat benar-benar mengontrol bahwa SQL Server akan mencoba dan melakukan
CONVERT
setelah filter.
Anda bahkan tidak dapat menggunakan subkueri atau CTE untuk mencoba dan memisahkan filter dari CONVERT karena SQL Server dapat mengoptimalkan operasi dalam kueri dalam urutan apa pun yang dianggap lebih efisien.
Misalnya, dengan sampel terbatas, Anda mungkin akan menemukan bahwa ini berfungsi dengan baik:
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';
Tetapi saya telah melihat kasus dengan konstruksi ini di mana SQL Server telah mencoba mengevaluasi filter terlebih dahulu, yang mengarah ke kesalahan yang sama yang Anda dapatkan saat ini.
Beberapa solusi yang lebih aman:
Tambahkan kolom yang dihitung, mis.
ALTER TABLE dbo.mytable ADD valor_date
AS CONVERT(DATE, CASE WHEN ISDATE(valor) = 1 THEN valor
ELSE NULL END, 103);
Untuk melindungi diri Anda dari kemungkinan salah tafsir saat runtime, Anda harus menentukan format tanggal sebelum mengeluarkan kueri yang mereferensikan kolom yang dihitung, mis.
SET DATEFORMAT dmy;
SELECT valor, valor_date FROM dbo.mytable WHERE ...;
Buat tampilan:
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;
Sekali lagi, Anda ingin mengeluarkan SET DATEFORMAT
saat menanyakan tampilan.
Gunakan tabel sementara:
SELECT <cols>
INTO #foo
FROM dbo.mytable
WHERE ISDATE(valor) = 1;
SELECT <cols>, CONVERT(DATE, valor) FROM #foo WHERE ...;
Anda mungkin masih ingin menggunakan DATEFORMAT
untuk melindungi diri Anda dari konflik antara ISDATE
dan pengaturan pengguna.
Dan tidak, Anda harus tidak coba validasi string Anda sebagai tanggal menggunakan pencocokan pola string seperti yang disarankan dalam jawaban lain (sekarang dihapus):
like '%__/%' or like '%/%'
Anda harus memiliki validasi yang cukup rumit dan berat di sana untuk menangani semua tanggal yang valid termasuk tahun kabisat.