Sporo czasu poświęcam na odpowiedzi na pytania SQL dotyczące SO. Często spotykam się z zapytaniami tego rodzaju:
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'
tzn. albo polegając na domniemanej konwersji ciągów na datę (zły), podanych parametrów lub polegając na bazie danych, która konwertuje x milionów wartości wierszy bazy danych na ciąg i dokonuje porównania łańcucha (gorzej)
Od czasu do czasu robię komentarz, szczególnie jeśli jest to użytkownik z wysokim przedstawicielem, który pisze mądrą odpowiedź, ale moim zdaniem powinien być mniej niechlujny / rygorystycznie wpisywany przy użyciu swoich typów danych
Komentarz zwykle przyjmuje postać, że prawdopodobnie lepiej byłoby, gdyby jawnie przekonwertowali swoje ciągi na daty, używając to_date (Oracle), str_to_date (MySQL), convert (SQLSERVER) lub podobny mechanizm:
--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)
Moim technicznym uzasadnieniem jest to, że jest jawne co do formatu daty i zapewnia, że kilka parametrów źródłowych zdecydowanie stanie się typem danych kolumny docelowej. Zapobiega to wszelkim możliwym błędom niejawnej konwersji bazy danych (argument z 3 stycznia / 1 marca pierwszego przykładu) i uniemożliwia dbowi o przekonwertowaniu miliona wartości daty w tabeli na ciągi znaków (używając pewnej daty specyficznej dla serwera formatowanie, które może nawet nie pasować do formatu daty w parametrach ciągu w sql) w celu wykonania porównania - mnóstwo horrorów
Moim społecznym / akademickim uzasadnieniem jest to, że SO jest witryną do nauki; ludzie na nim zdobywają wiedzę w sposób dorozumiany lub jawny. Aby trafić nowicjusza za pomocą tego zapytania jako odpowiedzi:
SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'
Może ich przekonać, że jest to rozsądne, dostosowując datę dla preferowanego formatu:
SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'
Jeśli przynajmniej zobaczyli jakąś wyraźną próbę konwersji daty, mogliby zacząć to robić dla swojego dziwnego formatu daty i zabić kilka błędów, zanim się pojawią. W końcu (I) staramy się odwieść ludzi od przyzwyczajania się do iniekcji SQL (i czy ktokolwiek zalecałby sparametryzowanie zapytania, a następnie zadeklarowanie sterownika, który @pBirthdate
jest łańcuchem, gdy frontend ma typ godziny / godziny?)
Wróć do tego, co dzieje się po tym, jak wydam zalecenie: zazwyczaj otrzymuję komunikat zwrotny do zalecenia „bądź wyraźny, użyj x”, na przykład „wszyscy to robią”, „zawsze działa dla mnie”, „pokaż mi instrukcję lub dokument referencyjny to mówi, że powinienem wyraźnie powiedzieć „a nawet„ co? ”
W odpowiedzi na niektóre z nich zapytałem, czy przeszukaliby kolumnę int, WHERE age = '99'
przekazując wiek jako ciąg znaków. „Nie bądź głupi, nie musimy umieszczać słowa„ podczas wyszukiwania int ”, więc doceniamy różne typy danych w ich umyśle, ale być może nie ma żadnego związku z logicznym skokiem, który polega na wyszukiwaniu int kolumna, przekazując ciąg znaków (pozornie głupie) i przeszukując kolumnę daty, przekazując ciąg znaków (pozornie rozsądny), jest hipokryzją
Zatem w naszych SQL-ach mamy sposób na zapisywanie rzeczy jako liczb (używaj liczb, bez ograniczników), rzeczy jako ciągów znaków (używaj czegokolwiek między ogranicznikami apostrofów) .. Dlaczego nie ma ograniczników dla dat? To taki podstawowy typ danych w większości baz danych? Czy cała ta sprawa mogłaby zostać rozwiązana poprzez zapisanie daty w taki sam sposób, w jaki javascript pozwala nam określić wyrażenie regularne, umieszczając /
dowolną stronę niektórych znaków. /Hello\s+world/
. Dlaczego nie mieć czegoś na randki?
W rzeczywistości, o ile mi wiadomo, (tylko) Microsoft Access faktycznie ma symbole wskazujące, że „między tymi ogranicznikami została zapisana data”, dzięki czemu możemy uzyskać dobry skrót, WHERE datecolumn = #somedate#
ale prezentacja daty wciąż może powodować problemy, np. Mm / di vs dd / mm, ponieważ stwardnienie rozsiane zawsze grało szybko i luźno z rzeczami, które tłum VB uważał za dobry pomysł
Wracając do głównego punktu: argumentuję, że mądrze jest mówić wprost o tym medium, które zmusza nas do przekazywania wielu różnych typów danych jako ciągów ..
Czy to prawidłowe stwierdzenie?
Czy powinienem kontynuować tę krucjatę? Czy to prawda, że ciągłe pisanie na klawiaturze jest nowoczesnym „nie”? Czy też wszystkie RDBMS (w tym starożytne wersje) będą dostępne, gdy zapytanie zadaje WHERE datecolumn = 'string value'
absolutnie prawidłową konwersję ciągu na datę i wykonuje wyszukiwanie bez konwersji danych tabeli / utraty użycia indeksów? Podejrzewam, że nie, przynajmniej z własnego doświadczenia Oracle 9. Podejrzewam również, że mogą istnieć pewne scenariusze ucieczki, jeśli ciągi znaków są zawsze zapisywane w jakimś standardowym formacie ISO, a kolumna ma smak daty, to parametr ciągu będzie zawsze poprawnie niejawnie konwertowany. Czy to dobrze?
Czy to jest opłacalne zadanie?
Wydaje się, że wiele osób tego nie rozumie, nie przejmuje się nimi ani nie wykazuje hipokryzji, ponieważ ich intrygi są intami, ale ich daty są ciągami. Jednak większość z nich jest taka, że niewielu ludzi kiedykolwiek się odwróciło i powiedziało „wiesz co, zgadzam się z twoją tezą. Będę odtąd wyrażał się jasno o moich datach ".
WHERE age = '0x0F'
jest to dobry sposób, by mieć nadzieję, że baza danych wyszuka piętnastolatków.
WHERE datecolumn =
01/02/12 '', gdzie jest możliwe, że prosi o rok 1912, 2012, 2001, 1901, 12 lub 1. Jest to również problem poza światem bazy danych, liczbą programistów, którzy nie mogą zrozumieć, dlaczego konwersja"09"
na int jest przyczyną awarii są legion, 9 nie jest poprawnym cyfra ósemkowa oraz wiodącym 0 czyni ósemkowy ciąg w wielu systemach