Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Bagaimana cara mengonversi varchar menjadi tanggal hanya ketika berisi tanggal yang valid?

Jawaban umumnya adalah menambahkan klausa WHERE:

WHERE ISDATE(a.valor) = 1

Namun ini bermasalah dalam situasi Anda karena beberapa alasan:

  1. 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
    
  2. 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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Perbandingan string gagal dengan variabel varchar

  2. Kesalahan saat memodifikasi DAL, System.ArgumentException, Entri dengan kunci yang sama sudah ada

  3. Cara Memperbaiki "Pernyataan EXECUTE gagal karena klausa WITH RESULT SETS nya ditentukan 1 set hasil ..." di SQL Server

  4. Cara memulihkan database SQL Server melalui kode C#

  5. Parameter keluaran prosedur tersimpan di SQL Server Profiler