Tentang perang data datatype tanggal database saya: Valid? Bermanfaat? Apakah ada orang lain yang merasakannya?

13

Saya menghabiskan banyak waktu menjawab pertanyaan SQL pada SO. Saya sering menemukan pertanyaan sejenis ini:

SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'

SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'

SELECT * FROM person WHERE birthdate BETWEEN 'some string' AND 'other string'

yaitu baik mengandalkan konversi implisit dari string ke tanggal (buruk), dari parameter yang diberikan atau mengandalkan pada basis data yang mengonversi x juta nilai baris basis data ke string dan melakukan perbandingan string (lebih buruk)

Saya kadang-kadang membuat komentar, terutama jika itu adalah pengguna rep tinggi yang menulis jawaban cerdas, tetapi yang saya rasa benar-benar harus menjadi kurang ceroboh / diketik ketat dengan tipe data mereka

Komentar biasanya mengambil bentuk yang mungkin akan lebih baik jika mereka secara eksplisit mengkonversi string mereka ke tanggal, menggunakan to_date (Oracle), str_to_date (MySQL), convert (SQLSERVER) atau mekanisme serupa:

    --oracle
    SELECT * FROM person WHERE birthdate BETWEEN TO_DATE('20170101', 'YYYYMMDD') AND TO_DATE('20170301', 'YYYYMMDD')

    --mysql
    SELECT * FROM person WHERE birthdate BETWEEN STR_TO_DATE('20170101', '%Y%m%d') AND STR_TO_DATE('20170301', '%Y%m%d')

    --SQLS, ugh; magic numbers
    SELECT * FROM person WHERE birthdate BETWEEN CONVERT(datetime, '20170101', 112) AND CONVERT(datetime, '20170301', 112)

Justifikasi teknis saya untuk melakukan ini adalah bahwa itu eksplisit mengenai format tanggal, dan memastikan bahwa beberapa parameter sumber pasti menjadi tipe data dari kolom target. Ini mencegah segala kemungkinan bahwa database akan mendapatkan konversi implisit yang salah (argumen 3 Januari / 1 Maret dari contoh pertama) dan mencegah db memutuskan untuk mengonversi jutaan nilai tanggal dalam tabel menjadi string (menggunakan beberapa tanggal khusus server pemformatan yang bahkan mungkin tidak cocok dengan format tanggal dalam parameter string dalam sql) untuk melakukan perbandingan - kengerian berlimpah

Pembenaran sosial / akademis saya untuk melakukan itu adalah bahwa SO adalah situs pembelajaran; orang-orang di dalamnya memperoleh pengetahuan baik secara implisit atau eksplisit. Memukul pemula dengan pertanyaan ini sebagai jawaban:

SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'

Mungkin membuat mereka berpikir ini masuk akal, menyesuaikan tanggal untuk beberapa format yang mereka sukai:

SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'

Jika mereka setidaknya melihat beberapa upaya eksplisit untuk mengkonversi tanggal, mereka mungkin mulai melakukannya untuk format tanggal aneh mereka, dan membunuh beberapa bug selamanya sebelum mereka muncul. Setelah semua, kami (saya) mencoba dan mencegah orang untuk masuk ke kebiasaan injeksi SQL (dan apakah ada yang akan mengadvokasi parameterisasi permintaan dan kemudian menyatakan kepada driver yang @pBirthdatemerupakan string, ketika frontend memiliki tipe datetime?)

Kembali ke apa yang terjadi setelah saya membuat rekomendasi saya: Saya biasanya mendapatkan beberapa pushback ke rekomendasi "menjadi eksplisit, gunakan x", seperti "semua orang melakukannya", "selalu bekerja untuk saya", "tunjukkan kepada saya beberapa dokumen manual atau referensi yang mengatakan saya harus eksplisit "atau bahkan" apa ?? "

Saya telah bertanya, dalam menanggapi beberapa di antaranya, apakah mereka akan mencari kolom int dengan melakukan WHERE age = '99'melewati usia sebagai string. "Jangan konyol, kita tidak perlu menempatkan 'ketika mencari int" datang jawabannya, jadi ada beberapa penghargaan untuk tipe data yang berbeda di pikiran mereka di suatu tempat, tapi mungkin tidak ada koneksi ke lompatan logis yang mencari sebuah int kolom dengan melewatkan string (tampaknya konyol) dan mencari kolom tanggal dengan melewatkan string (tampaknya masuk akal) adalah kemunafikan

Jadi dalam SQL kami, kami memiliki cara untuk menulis sesuatu sebagai angka (menggunakan angka, tanpa pembatas), hal-hal sebagai string string (menggunakan apa pun di antara pembatas apostrof). Mengapa tidak ada pembatas untuk tanggal? Ini adalah tipe data fundamental dalam kebanyakan DB? Mungkinkah semua ini bisa diselesaikan hanya dengan memiliki cara menulis tanggal dengan cara yang sama javascript memungkinkan kita menentukan regex dengan menempatkan /kedua sisi beberapa karakter. /Hello\s+world/. Mengapa tidak punya sesuatu untuk kencan?

Sebenarnya, sepengetahuan saya, (hanya) Microsoft Access sebenarnya memiliki simbol yang menunjukkan "tanggal telah ditulis di antara pembatas ini" sehingga kita bisa mendapatkan jalan pintas yang baik seperti WHERE datecolumn = #somedate#tetapi presentasi tanggal masih bertanggung jawab untuk memberikan masalah misalnya mm / di vs dd / mm, karena MS selalu bermain cepat dan longgar dengan hal-hal yang dianggap kerumunan VB adalah ide yang bagus


Kembali ke poin utama: Saya berpendapat bahwa adalah bijaksana untuk secara eksplisit dengan media ini yang memaksa kita untuk melewati banyak tipe data yang berbeda sebagai string ..

Apakah ini pernyataan yang valid?

Haruskah saya melanjutkan perang salib ini? Apakah ini poin yang valid bahwa pengetikan ketat adalah modern tidak-tidak? Atau akankah setiap RDBMS (termasuk versi kuno) di luar sana, ketika mendorong kueri WHERE datecolumn = 'string value'benar-benar pasti mengubah string ke tanggal dan melakukan pencarian tanpa mengkonversi data tabel / kehilangan penggunaan indeks? Saya curiga tidak, setidaknya dari pengalaman pribadi Oracle 9. Saya menduga juga bahwa mungkin ada beberapa skenario "get-away-with-it" jika string selalu ditulis dalam beberapa format standar ISO, dan kolom adalah beberapa rasa tanggal, maka parameter string akan selalu dikonversi dengan benar secara implisit. Apakah ini benar?

Apakah ini tugas yang berharga?

Banyak orang tampaknya tidak mendapatkannya, atau tidak peduli, atau menunjukkan kemunafikan karena int mereka adalah int tetapi kencan mereka adalah string. Namun yang paling umum adalah bahwa beberapa orang pernah berbalik dan berkata "Anda tahu apa, saya setuju dengan poin Anda. Saya akan secara eksplisit tentang tanggal saya mulai sekarang ".

Caius Jard
sumber
Saya bahkan melihat seseorang mendapatkan masalah dengan WHERE datecolumn = 01/02/12 di mana mungkin mereka meminta untuk tahun 1912, 2012, 2001, 1901, 12 atau 1. Ini juga merupakan masalah di luar dunia basis data, nomor pemrogram yang tidak dapat memahami mengapa konversi "09"ke int menyebabkan crash sangat banyak, 9 bukan digit oktal yang valid dan 0 terkemuka membuat string oktal dalam banyak sistem
Steve Barnes
2
Saya memang berpikir untuk memperluas contoh saya untuk bertanya apakah WHERE age = '0x0F'ada cara yang valid untuk berharap database akan mencari anak berusia lima belas tahun ..
Caius Jard
1
Saya menghapus pertanyaan di luar topik di sini - kami tidak melakukan permintaan sumber daya. Salah satu dari 2 suara dekat diberikan untuk alasan ini. Kalau tidak, saya pikir ini adalah pertanyaan yang valid, meskipun mungkin terlalu lebar. Saya berharap bahwa penghapusan pertanyaan di luar topik membantu mempersempit hal-hal sedikit.
Thomas Owens
TL; DR tetapi dalam sistem produksi, saya berharap tanggal seperti ini hampir selalu dalam parameter. Memodifikasi tanggal ke dalam kueri adalah masalah yang lebih besar daripada apakah Anda menggunakan konversi implisit. Jika saya menulis beberapa permintaan membuang, itu berfungsi atau tidak. Saya tidak pernah melakukan ini (karena saya tidak pernah dapat mengingat format tanggal default) tetapi saya tidak yakin itu penting.
JimmyJames
1
Hidup adalah tentang memilih pertempuran Anda. Dalam pandangan saya, yang ini tidak layak untuk diperjuangkan ...
Robbie Dee

Jawaban:

7

Kau menulis:

adalah parameter 1 Jan hingga 3 Jan, atau 1 Mar ..

Itu memang sumber kesalahan potensial. Menunjukkan hal ini kepada penanya dapat membantu pembaca lain, jadi ya, ini adalah masalah yang valid. Namun, untuk menjadi konstruktif, saya akan melakukannya

  • lihat ANSI SQL dan gunakan literal DATE atau DATETIME dari standar itu

  • menggunakan format datetime biasa dan tidak ambigu dari DBMS tertentu (dan sebutkan dialek SQL mana yang digunakan)

Sayangnya, tidak setiap DBMS mendukung literal tanggal ANSI SQL dengan cara yang persis sama (jika mereka mendukungnya sama sekali), jadi ini biasanya akan mengarah pada varian dari pendekatan kedua. Fakta "standar" tidak diterapkan secara kaku oleh vendor DB yang berbeda mungkin merupakan bagian dari masalah di sini.

Catatan lebih lanjut, untuk banyak sistem dunia nyata, orang benar-benar dapat mengandalkan lokal tertentu yang tetap pada server database, bahkan jika aplikasi klien dilokalkan, karena hanya ada satu jenis server, selalu dikonfigurasi dengan cara yang sama. Jadi '01 / 03/2017 'sering diasumsikan memiliki format tetap' dd / mm / yyyy ', atau' mm / dd / yyyy 'untuk SQL apa pun yang digunakan pada sistem spesifik yang mereka gunakan. Jadi jika seseorang mengatakan kepada Anda "itu selalu berhasil untuk saya", ini mungkin memang jawaban yang masuk akal untuk lingkungannya . Jika ini masalahnya, kurang pantas membahas topik ini.

Berbicara tentang "alasan kinerja": selama tidak ada masalah kinerja yang dapat diukur, ini cukup takhayul untuk berdebat dengan "masalah kinerja potensial". Jika database melakukan jutaan string-to-date konversi atau tidak mungkin tidak masalah ketika perbedaan waktu hanya 1/1000 detik, dan hambatan sebenarnya adalah jaringan yang menyebabkan kueri bertahan 10 detik. Jadi lebih baik mengesampingkan masalah ini selama ada yang meminta pertimbangan kinerja secara eksplisit.

Haruskah saya melanjutkan perang salib ini?

Saya memberi tahu Anda sebuah rahasia: Saya benci perang agama. Mereka tidak mengarah pada sesuatu yang bermanfaat. Jadi, jika spesifikasi tanggal / waktu yang ambigu dalam SQL dapat menyebabkan masalah, sebutkanlah, tetapi jangan mencoba memaksa orang untuk menjadi lebih kaku jika itu tidak benar-benar memberi mereka manfaat dalam konteks mereka saat ini.

Doc Brown
sumber
Ini bukan pertanyaan tentang ambiguitas format tanggal Amerika vs Sensible. Ini tentang apakah masuk akal untuk melewati tanggal dalam pernyataan SQL sebagai string, dan bergantung pada konversi implisit hingga saat ini. Pertanyaan tentang database yang harus melakukan satu juta konversi tanggal untuk semua juta baris adalah satu aspek kinerja, dan mungkin hanya membutuhkan 1/1000 detik untuk satu permintaan, tetapi sekarang bayangkan dalam konteks demikian dan bersamaan. pengguna. Masalah kinerja yang lebih besar adalah bahwa mengkonversi data berarti indeks tidak lagi dapat digunakan dan itu bisa sangat serius
Caius Jard
@CaiusJard: jawaban saya adalah: kadang-kadang masuk akal, dan kadang tidak, tergantung pada konteksnya. Dan jujur, saya menolak untuk "... membayangkan ..." apa pun di sini. Ketika datang ke kinerja, mendiskusikan kasus hipotetis tidak berguna. Ketika ada masalah kinerja yang terukur, maka sudah saatnya untuk mengoptimalkan, dan kadang-kadang untuk mengoptimalkan mikro, bukan sebelumnya.
Doc Brown
Sangat menarik bahwa Anda melihatnya sebagai hipotesis; Saya melihat mengandalkan perilaku implisit sebagai peluang yang jelas untuk timbulnya bug dan komplikasi kinerja (untuk alasan yang terdokumentasi dengan baik: indeks tidak berfungsi jika seluruh data kolom diubah sebelum dicari), dan dengan instruksi eksplisit ini tidak dapat terjadi
Caius Jard
@CaiusJard: jangan bermain dengan kata-kata - dengan "hipotetis" Saya tidak bermaksud "tidak mungkin", saya menggunakan istilah untuk segala jenis skenario yang dibayangkan, berlawanan dengan "situasi nyata yang ada" di mana seseorang dapat mengukur apa yang terjadi.
Doc Brown
1
@CaiusJard: jika Anda ingin mengesankan profesional industri lainnya, Anda harus tahu persis mengapa "optimasi kinerja" sangat berbeda dari "optimasi keamanan", dan itulah poin saya di sini - masalah kinerja dapat ditangani setelah terjadi, yang jarang terjadi sangat terlambat. Masalah keamanan tidak, mereka harus dihindari secara menyeluruh sebelum terjadi. Jadi tolong jangan bandingkan apel dengan jeruk. Jika Anda menyukai perang salib, argumen keamanan jauh lebih cocok untuk ini ;-)
Doc Brown
5

Perang salib Anda tidak menyelesaikan masalah.

Ada dua masalah terpisah:

  • konversi tipe implisit dalam SQL

  • format tanggal ambigu seperti 05/06/07

Saya melihat dari mana Anda berasal dengan perang salib, tetapi saya tidak berpikir konversi eksplisit benar-benar menyelesaikan masalah:

  • Konversi implisit masih terjadi dalam kasus ketidakcocokan antara jenis dalam perbandingan. Jika sebuah string dibandingkan dengan suatu tanggal, SQL akan berusaha untuk mengubah string ke suatu tanggal terlebih dahulu. Jadi membandingkan kolom tipe tanggal dengan nilai tanggal yang dikonversi secara eksplisit persis sama dengan membandingkan tanggal dalam format string. Satu-satunya perbedaan yang saya lihat adalah jika Anda membandingkan nilai tanggal ke kolom yang sebenarnya tidak mengandung tanggal tetapi string - tetapi ini akan menjadi kesalahan dalam hal apa pun.

  • Menggunakan konversi eksplisit tidak menyelesaikan ambiguitas dalam format tanggal non-ISO.

Satu-satunya solusi yang saya lihat:

  • jangan bandingkan kolom tipe string dengan nilai non-string.
  • hanya pernah menggunakan format tanggal tipe ISO.

Dan tentu saja, jangan pernah menyimpan tanggal dalam kolom tipe string. Tetapi sekali lagi, konversi eksplisit literal tanggal tidak akan mencegah hal ini.

Konversi, implisit konversi adalah kesalahan dalam SQL, tetapi mengingat bagaimana bahasa dirancang, saya tidak melihat manfaat dari konversi eksplisit. Itu tidak akan menghindari konversi implisit, dan itu hanya membuat kode lebih sulit untuk dibaca dan ditulis.

JacquesB
sumber
Benar. Mungkin saya harus menunjukkannya dari perspektif ini, bahwa hal yang paling masuk akal untuk dilakukan adalah memastikan bahwa operand datecolumn dan nilai operan memiliki tipe data yang sama (baik itu string, tanggal, apa pun). Saya secara khusus membuat rekomendasi ini hanya dalam pertanyaan di mana saya tahu kolom tabel adalah DATETIME dan jawaban contoh mereka menggunakan string operan dengan konversi implisit ..
Caius Jard
Sesuatu tidak cocok dengan saya pada jawaban ini. Anda membuat beberapa poin menarik tetapi saya merasa kesimpulannya idealis. Dari perspektif desain, ya, format tanggal non-ISO ambigu bagi mata manusia tetapi jika menggunakan konversi eksplisit, secara sintaksis tidak ambigu bagi parser. Demikian juga, banyak proses ETL yang melibatkan tanggal akan memerlukan beberapa perbandingan (dalam bentuk impor file) dari sebuah string ke format tanggal dari database. Mencoba menghilangkan perbandingan string to date sepertinya tidak realistis bagi saya.
DanK
@DanK: ETL adalah masalah yang berbeda - jika Anda membaca data dari file CSV atau sesuatu, jelas Anda harus memproses data sebagai string dan secara eksplisit mem-parsing ke dalam nilai yang diketikkan. Tapi itu bukan skenario yang OP jelaskan.
JacquesB
Ini bisa dengan mudah menjadi titik yang saya jelaskan; tidak ada yang istimewa tentang serangkaian angka yang disimpan dalam csv yang menuntut secara eksplisit menyatakan format ketika parsing dan itu menjadi relevan dengan argumen yang saya buat jika seorang pemula membaca beberapa jawaban dalam SO di mana pro tidak berusaha untuk secara eksplisit mendeklarasikan format tanggal, mengarahkan pemula untuk menganggap mereka tidak perlu khawatir tentang hal itu (atau bahwa db akan menguraikannya dengan benar setiap saat)
Caius Jard
@CaiusJard: Saya percaya ini adalah skenario yang sangat berbeda. Ketika berbicara tentang SQL dalam skenario normal, saya menganggap kolom memiliki tipe yang sesuai - yaitu kolom integer adalah tipe integer, kolom tanggal adalah tipe data dan sebagainya. Jika Anda tidak memiliki tipe yang benar dalam tabel (mis. Menyimpan tanggal sebagai string) Anda berada dalam masalah besar dan secara eksplisit mengkonversi literal tanggal dalam kueri tidak akan menyelamatkan Anda , yang merupakan poin saya.
JacquesB
3

Pertama dan terpenting, Anda benar. Tanggal tidak harus dimasukkan ke dalam string. Mesin basis data adalah binatang buas yang kompleks di mana Anda tidak pernah 100% yakin apa yang sebenarnya akan terjadi di bawah tenda ketika diberi pertanyaan sewenang-wenang. Konversi ke tanggal membuat hal-hal menjadi tidak ambigu dan dapat meningkatkan kinerja.

TAPI

Ini bukan masalah yang sepadan dengan upaya ekstra pemikiran untuk dipecahkan bagi kebanyakan orang. Jika mudah menggunakan literal tanggal dalam kueri, akan mudah untuk mempertahankan posisi Anda. Tapi ternyata tidak. Saya kebanyakan menggunakan SQL Server, jadi mencoba mengingat bahwa kekacauan untuk mengkonversi tanggal tidak terjadi.

Bagi kebanyakan orang, perolehan kinerja dapat diabaikan. "Kenapa ya, Pak Bos-man, saya menghabiskan 10 menit ekstra untuk memperbaiki bug sederhana ini (saya harus mencari cara mengonversi tanggal karena sintaks itu adalah ... istimewa ...). Tetapi saya menyimpan 0,00001 detik tambahan pada permintaan yang jarang dieksekusi. " Itu tidak akan terbang di sebagian besar tempat saya bekerja.

Tapi itu menghilangkan ambiguitas dalam format tanggal yang Anda katakan. Sekali lagi, untuk banyak aplikasi (aplikasi internal perusahaan, hal-hal pemerintah daerah, dll.) Itu tidak benar-benar menjadi perhatian. Dan untuk aplikasi yang menjadi perhatian (aplikasi besar, internasional atau perusahaan), yang menjadi UI / lapisan bisnis atau perusahaan tersebut sudah memiliki tim DBA yang berpengalaman yang sudah mengetahui hal ini. TL / DR: jika internasionalisasi menjadi perhatian, seseorang sudah memikirkannya dan telah melakukan seperti yang Anda sarankan (atau telah mengurangi masalah ini).

Jadi bagaimana sekarang?

Jika Anda merasa cenderung, teruslah berjuang dengan perjuangan yang baik. Tetapi jangan terkejut jika kebanyakan orang tidak merasa ini cukup penting untuk dikhawatirkan. Hanya karena ada situasi di mana itu penting, tidak berarti itu adalah situasi semua orang (dan kemungkinan tidak). Jadi jangan kaget ketika Anda mendapat dorongan kembali untuk sesuatu yang secara teknis benar-dan-lebih baik-tetapi-tidak-benar-relevan.

Becuzz
sumber
1

Saya berpendapat bahwa adalah bijaksana untuk secara eksplisit dengan media ini yang memaksa kita untuk melewati banyak tipe data yang berbeda sebagai string.

Dengan asumsi bahwa "tanggal" sedang diedarkan "di" String maka ya; Saya sepenuhnya setuju bahwa Anda benar untuk melakukan ini.

Ketika adalah "01/04/07"?
* 4 Januari?
* 1 April?
* 7 April [2001]?

Setiap atau semua ini mungkin benar, tergantung pada bagaimana "komputer" memilih untuk menafsirkannya.

Jika Anda harus membangun SQL dinamis dengan literal di dalamnya, maka format tanggal Anda harus didefinisikan dengan baik dan, lebih disukai, mesin-independen (saya punya yang aneh pada Server Windows di mana pemrosesan berbasis tanggal dalam Layanan Windows menjadi serba salah. karena operator masuk ke konsol dengan preferensi format tanggal yang berbeda!). Secara pribadi, saya secara eksklusif menggunakan [d] format "yyyy-mm-dd".

Namun ...

The terbaik solusi adalah dengan menggunakan parameterised Pertanyaan yang memaksa tipe data yang akan dikonversi sebelum SQL mendapat terlibat - mendapatkan "tanggal" nilai menjadi Tanggal pasukan Parameter tipe konversi awal (membuat murni masalah coding, tidak satu SQL) .

Phill W.
sumber
Saya setuju, meskipun masalah yang sama dapat dipaksakan dengan query parameter, dengan melakukan WHERE datecolumn = @dateParameterdan kemudian di kode ujung depan, memberi tahu driver DB yang @dateParameterbertipe varchar, dan tetap "01/04/07"di dalamnya. Inspirasi orisinal untuk pertanyaan saya adalah bahwa saya mencurigai siapa pun yang akan memberi tahu saya bahwa saya gila karena melakukan hal itu pada permintaan parameter, kemudian, dengan napas yang sama, akan memberikan satu baris jawaban SO yang terlihat seperti WHERE datecol = 'some string that looks like a date'(dan berharap seorang pemula harus tahu itu hanya petunjuk / parameterisasi untuk menghindari masalah)
Caius Jard