Я витрачаю багато часу, відповідаючи на питання SQL над SO. Я часто стикаюся із запитами цієї лайки:
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'
тобто або покладаючись на неявне перетворення з рядка в дату (погано), заданих параметрів, або спираючись на базу даних, що перетворює x мільйони значень рядків бази даних у рядок і робить порівняння рядків (гірше)
Я час від часу коментую, особливо якщо це високопоставлений користувач, який пише розумну відповідь, але, на кого я вважаю, він повинен бути менш неохайним / строго набраним зі своїми типами даних
Зауваження зазвичай має таку форму, що, мабуть, було б краще, якби вони явно перетворили свої рядки в дати, використовуючи to_date (Oracle), str_to_date (MySQL), конвертувати (SQLSERVER) або якийсь подібний механізм:
--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)
Мої технічні виправдання для цього полягають у тому, що це чітко щодо формату дати та гарантує, що декілька вихідних параметрів безумовно стануть типом даних цільового стовпця. Це запобігає будь-якій можливості, що база даних отримає неявну конверсію помилково (аргумент 3-го січня / 1-го березня в першому прикладі), і це перешкоджає db вирішувати перетворювати мільйони значень дати в таблиці в рядки (використовуючи певну дату для сервера форматування, яке може навіть не відповідати формату дати в параметрах рядка в sql) для того, щоб зробити порівняння - жахи мають багато
Моє соціальне / академічне виправдання для цього полягає в тому, що SO - це навчальний майданчик; люди на ньому здобувають знання або неявно, або прямо. Щоб відповісти на новачків із цим запитом як відповідь:
SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'
Можливо, спонукають їх вважати це розумним, коригуючи дату під потрібний формат:
SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'
Якщо вони принаймні побачили явну спробу перетворити дату, вони можуть почати робити це для свого дивного формату дати та вбити кілька навіки-помилок до їх появи. Зрештою, ми (я) намагаємось відмовити людей від потрапляння у звичку ін'єкцій SQL (і чи хотів би хто-небудь прихилити параметризацію запиту, а потім оголосити драйверу, що @pBirthdate
це рядок, коли frontend має тип дати?)
Повернення до того, що станеться після того, як я висловлюю свою рекомендацію: я зазвичай отримую певний відклик до рекомендації "будь явний, використовуйте х", як "всі інші це роблять", "це завжди працює для мене", "покажіть мені якийсь посібник чи довідковий документ що говорить, що я повинен бути явним "або навіть" що ?? "
Я запитав у відповідь на деякі з них, чи шукають вони колонку int, WHERE age = '99'
передаючи вік як рядок. "Не будьте дурними, нам не потрібно ставити" під час пошуку int ", приходить відповідь, тому десь зважають на різні типи даних десь у їхній свідомості, але, можливо, просто немає зв'язку з логічним стрибком, що шукає int стовпець, передаючи рядок (мабуть, нерозумно) і шукає стовпець дати, передаючи рядок (мабуть, розумний) - це лицемірство
Таким чином, у наших SQL є спосіб записати речі як числа (використовувати числові цифри, без роздільників), речі як рядкові рядки (використовувати що-небудь між роздільниками апострофа). Чому немає обмежувачів для дат? Це такий фундаментальний тип даних у більшості БД? Невже все це можна, можливо, вирішити лише шляхом того, щоб записати дату так само, як javascript дозволяє нам вказати регулярний вираз, додавши /
обидві сторони деяких символів. /Hello\s+world/
. Чому б не мати щось на побачення?
Насправді, наскільки мені відомо, (лише) Microsoft Access насправді має символи, які вказують "дата була записана між цими роздільниками", тому ми можемо отримати хороший ярлик, як-от, WHERE datecolumn = #somedate#
але презентація дати все ще може викликати проблеми, наприклад, mm / di vs dd / мм, оскільки МС завжди грали швидко і вільно з речами, які натовп VB вважав гарною ідеєю
Назад до основного моменту: я стверджую, що розумно бути явним із цим носієм, який змушує нас передавати безліч різних типів даних у вигляді рядків.
Чи справедливе твердження?
Чи варто продовжувати цей хрестовий похід? Чи справедливо те, що строго набравши текст - це сучасний ні-ні? Або всі RDBMS (включаючи стародавні версії) там, коли запит WHERE datecolumn = 'string value'
абсолютно безумовно правильно перетворить рядок у дату і здійснить пошук, не перетворюючи дані таблиці / втрачаючи використання індексів? Я підозрюю, що ні, принаймні з особистого досвіду Oracle 9. Я підозрюю також, що можуть бути якісь сценарії відключення, якщо рядки завжди записуються у певному стандартному форматі ISO, а стовпець - якийсь аромат дати, то Параметр string завжди буде правильно неявно перетворений. Це робить це правильно?
Це гідне завдання?
Багато людей, схоже, не розуміють цього, або не хвилюються, або виявляють деяке лицемірство, оскільки їхні інти є внутрішніми, але їхні дати - це рядки. Загальним для більшості є те, що мало хто колись розвернувся і сказав: "Ви знаєте що, я погоджуюся з вашою точкою. Відтепер я буду чітко розповідати про свої дати ".
WHERE age = '0x0F'
є коректним способом сподіватися, що база даних шукатиме п'ятнадцятирічних дітей ...
WHERE datecolumn =
01/02 / 12'`, де можливо, вони просять рік 1912, 2012, 2001, 1901, 12 або 1. Це також проблема поза світом баз даних, числом Програмістів, які не можуть зрозуміти, чому перетворення"09"
на int викликає збій - легіон, 9 не є дійсною восьмеричною цифрою, а провідна 0 робить рядковий восьмерик у багатьох системах