Я трачу много времени, отвечая на вопросы 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), convert (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 марта самого первого примера), и предотвращает решение БД о преобразовании миллиона значений даты в таблице в строки (используя некоторую специфическую для сервера дату форматирование, которое может даже не совпадать с форматом даты в строковых параметрах в 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'
Если они хотя бы увидели какую-то явную попытку конвертировать дату, они могли бы начать делать это для своего странного формата даты и убить несколько вечных ошибок до того, как они появятся. В конце концов, мы (I) пытаемся отговорить людей от пристрастия к SQL-инъекциям (и будет ли кто-либо выступать за параметризацию запроса, а затем объявлять драйверу, который @pBirthdate
является строкой, когда у интерфейса есть тип datetime?)
Возвращаясь к тому, что происходит после того, как я сделаю свою рекомендацию: я обычно получаю некоторый откат к рекомендации «будь явным, используй x», как «все остальные делают это», «она всегда работает для меня», «покажи мне какое-нибудь руководство или справочный документ что говорит, что я должен быть явным "или даже" что ?? "
В ответ на некоторые из них я спросил, будут ли они искать в столбце int, WHERE age = '99'
передавая значение age в виде строки. «Не будь глупым, нам не нужно ставить« при поиске int », приходит ответ, так что в их сознании где-то есть понимание различных типов данных, но, возможно, просто нет связи с логическим скачком, который ищет int столбец, передавая строку (очевидно глупо) и ища столбец даты, передавая строку (очевидно разумно), является лицемерием
Таким образом, в наших SQL у нас есть способ записывать вещи в виде чисел (использовать числа без разделителей), вещи в виде строковых строк (использовать что-либо между разделителями апострофов). Почему нет разделителей для дат? Это такой фундаментальный тип данных в большинстве БД? Может быть, все это может быть решено просто путем написания даты таким же образом, как javascript позволяет нам указать регулярное выражение, поместив /
любую сторону некоторых символов. /Hello\s+world/
, Почему бы не иметь что-то для свиданий?
На самом деле, насколько мне известно, (только) Microsoft Access на самом деле имеет символы, которые обозначают «дата была записана между этими разделителями», поэтому мы можем получить хороший ярлык, WHERE datecolumn = #somedate#
но представление даты по-прежнему может вызвать проблемы, например, mm / di vs dd мм, потому что MS всегда играли быстро и свободно с вещами, которые толпа VB считала хорошей идеей
Возвращаясь к основному вопросу: я утверждаю, что разумно быть явным с этим средством, которое заставляет нас передавать множество различных типов данных в виде строк.
Это правильное утверждение?
Должен ли я продолжить этот крестовый поход? Является ли верным утверждение о том, что строгая типизация является современной нет-нет? Или все РСУБД (включая древние версии) будут там, когда отправляют запрос WHERE datecolumn = 'string value'
абсолютно точно правильно, преобразуют строку в дату и выполняют поиск без преобразования табличных данных / потери использования индексов? Я подозреваю, что нет, по крайней мере, из личного опыта Oracle 9. Я также подозреваю, что могут существовать некоторые сценарии, в которых можно с этим справиться, если строки всегда пишутся в каком-то стандартном формате ISO, а в столбце указан некоторый вариант даты, тогда Строковый параметр всегда будет правильно неявно преобразован. Это делает это правильно?
Это стоящая задача?
Многие люди, кажется, не понимают, или не заботятся, или демонстрируют какое-то лицемерие в том, что их целые числа - это целые, но их даты - строки. Хотя для большинства характерно, что мало кто когда-либо поворачивался и говорил: «Вы знаете, Что, я согласен с вашей точкой зрения. Я буду прямо сейчас о моих датах ".
WHERE age = '0x0F'
является ли верным способ надеяться, что база данных будет искать пятнадцатилетних ...
WHERE datecolumn =
01/02/12 ', где возможно, что они просят 1912, 2012, 2001, 1901, 12 или 1 год. Это также проблема за пределами мира баз данных, число программистов, которые не могут понять, почему преобразование"09"
в int вызывает сбой, легион, 9 не является действительной восьмеричной цифрой, а ведущий 0 делает строку восьмеричной во многих системах