Я трачу много времени, отвечая на вопросы 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 datecolumn =
01/02/12 ', где возможно, что они просят 1912, 2012, 2001, 1901, 12 или 1 год. Это также проблема за пределами мира баз данных, число программистов, которые не могут понять, почему преобразование"09"
в int вызывает сбой, легион, 9 не является действительной восьмеричной цифрой, а ведущий 0 делает строку восьмеричной во многих системахWHERE age = '0x0F'
является ли верным способ надеяться, что база данных будет искать пятнадцатилетних ...Ответы:
Вы написали:
Это действительно потенциальный источник ошибок. Указание этого на вопрос может быть полезным для других читателей, так что да, это серьезная проблема. Однако, чтобы быть конструктивным, я бы
обратитесь к ANSI SQL и используйте литералы DATE или DATETIME из этого стандарта
используйте обычный, однозначный формат даты-времени конкретной СУБД (и укажите, какой диалект SQL используется)
К сожалению, не каждая СУБД поддерживает литералы даты ANSI SQL точно таким же образом (если они вообще его поддерживают), поэтому это обычно приводит к варианту второго подхода. Тот факт, что «стандарт» жестко не реализован различными поставщиками БД, вероятно, является частью проблемы.
Обратите внимание, что во многих реальных системах люди могут полагаться на конкретную фиксированную локаль на сервере базы данных, даже если клиентские приложения локализованы, поскольку существует только один тип сервера, всегда настроенный одинаково. Поэтому часто можно предположить, что '01 / 03/2017 'имеет фиксированный формат' dd / mm / yyyy 'или' mm / dd / yyyy 'для любого SQL, используемого в конкретной системе, с которой они работают. Так что, если кто-то говорит вам, «это всегда работает для меня», это, возможно, действительно разумный ответ для его окружения . Если дело обстоит так, это делает менее целесообразным обсуждение этой темы.
Говоря о «причинах производительности»: пока нет измеримых проблем с производительностью, спорить с «потенциальными проблемами с производительностью» довольно суеверно. Если база данных выполняет миллион преобразований строки в дату или нет, вероятно, не имеет значения, когда разница во времени составляет всего 1/1000 секунды, и реальным узким местом является сеть, которая заставляет запрос длиться 10 секунд. Так что лучше отложить эти проблемы в сторону, пока кто-то явно просит соображения производительности.
Я раскрываю вам секрет: я ненавижу религиозные войны. Они не приводят ни к чему полезному. Поэтому, если неоднозначные спецификации даты / времени в SQL могут привести к проблемам, упомяните их, но не пытайтесь заставить людей быть более жесткими, если это не принесет им никакой пользы в их текущем контексте.
источник
Ваш крестовый поход не решает проблему.
Есть две отдельные проблемы:
неявное преобразование типов в SQL
неоднозначные форматы даты, такие как 05/06/07
Я вижу, откуда вы идете с вашим крестовым походом, но я не думаю, что явное преобразование фактически решает проблему под рукой:
Неявное преобразование все еще происходит в случае несоответствия между типами в сравнении. Если строка сравнивается с датой, SQL сначала попытается преобразовать строку в дату. Таким образом, сравнение столбца типа даты с явно преобразованным значением даты точно такое же, как сравнение с датой в строковом формате. Единственное отличие, которое я вижу, заключается в том, что вы сравниваете значение даты со столбцом, который на самом деле содержит не даты, а строки - но в любом случае это будет ошибкой.
Использование явного преобразования не решает неоднозначность в форматах даты, отличных от ISO.
Единственное решение, которое я вижу:
И, конечно же, никогда не храните даты в столбце строкового типа. Но опять же, явное преобразование литералов даты не помешает этому.
Можно утверждать, что неявные преобразования были ошибкой в SQL, но, учитывая, как устроен язык, я не вижу преимущества явного преобразования. В любом случае это не предотвратит неявное преобразование, а только усложнит чтение и запись кода.
источник
Прежде всего, у вас есть точка зрения. Даты не должны быть приведены в строки. Механизмы баз данных - это сложные звери, в которых вы никогда не уверены на 100%, что именно произойдет под капотом при произвольном запросе. Преобразование в даты делает вещи однозначными и может повысить производительность.
НО
Для большинства людей эта проблема не стоит дополнительных усилий. Если бы в запросе было легко использовать литералы даты, было бы легко защитить вашу позицию. Но это не так. Я в основном использую SQL Server, поэтому попытки запомнить этот беспорядок для преобразования даты просто не происходят.
Для большинства людей прирост производительности незначителен. «Да, мистер Босс-мэн, я потратил дополнительные 10 минут на исправление этой простой ошибки (мне пришлось поискать, как конвертировать даты, потому что этот синтаксис особенный…). Но я сэкономил лишние 0,00001 секунды на редко выполняемый запрос. " Это не полетит в большинстве мест, где я работал.
Но это устраняет двусмысленность в форматах даты, которые вы говорите. Опять же, для многих приложений (внутренних приложений компании, местных органов власти и т. Д. И т. Д.) Это не является проблемой. А для тех приложений, для которых это важно (большие, международные или корпоративные приложения), это либо становится проблемой пользовательского интерфейса / бизнес-уровня, либо у тех компаний уже есть команда опытных администраторов баз данных, которые уже знают это. TL / DR: если интернационализация является проблемой, кто-то уже думает об этом и уже сделал, как вы предлагаете (или иным образом смягчил проблему).
И что теперь?
Если вы чувствуете себя таким склонным, продолжайте бороться за хороший бой. Но не удивляйтесь, если большинство людей не считают, что это достаточно важно для беспокойства. То, что есть ситуации, когда это важно, не означает, что это ситуация каждого (и, вероятно, это не так). Так что не удивляйтесь, когда вас подталкивают к чему-то технически правильному и лучше, но не очень актуальному.
источник
Предполагая, что «даты» передаются «в» строках, тогда да; Я абсолютно согласен, что вы правы в этом.
Когда это «01/04/07»?
* 4 января?
* 1 апреля?
* 7 апреля [2001]?
Любые или все из них могут быть правильными, в зависимости от того, как «компьютер» решает их интерпретировать.
Если вам нужно построить динамический SQL с литералами в них, то форматирование даты должно быть четко определено и, предпочтительно, независимо от компьютера (у меня был странный пример на Windows Server, где обработка на основе даты в службе Windows шла не так, как надо) потому что оператор вошел в консоль с другими настройками формата даты!). Лично я исключительно использую [d] формат "гггг-мм-дд".
Тем не мение ...
Лучшим решением является использование параметризованных запросов , которые заставляют тип данных , которые будут преобразованы , прежде чем SQL вовлекается - получение «дата» значение в Дату силы параметров преобразования типа на ранних стадиях (делая это исключительно проблема кодирования, а не SQL один) ,
источник
WHERE datecolumn = @dateParameter
и затем в коде внешнего интерфейса, сообщив драйверу БД, который@dateParameter
имеет тип varchar, и вставив"01/04/07"
в него. Первоначальное вдохновение для моего вопроса заключается в том, что я подозреваю, что любой, кто скажет мне, что я сумасшедший за то, что сделал это с параметризованным запросом, затем, на одном дыхании, даст какой-нибудь однострочный ТАК ответ, который выглядит какWHERE datecol = 'some string that looks like a date'
(и ожидать, что новичок должен знать это всего лишь подсказка / параметризация, чтобы избежать проблем)