Passo muito tempo respondendo perguntas sobre SQL no SO. Costumo encontrar perguntas deste tipo:
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'
ou seja, baseando-se em uma conversão implícita de string para data (inválida), dos parâmetros fornecidos ou no banco de dados convertendo x milhões de valores de linha de banco de dados em string e fazendo uma comparação de strings (pior)
Ocasionalmente, faço um comentário, especialmente se é um usuário de alta reputação que escreve uma resposta inteligente, mas que, na minha opinião, realmente deveria estar sendo menos desleixado / digitado com seus tipos de dados
O comentário geralmente assume a forma de que provavelmente seria melhor se eles convertessem explicitamente suas strings em datas, usando to_date (Oracle), str_to_date (MySQL), convert (SQLSERVER) ou algum mecanismo semelhante:
--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)
Minhas justificativas técnicas para fazer isso são explícitas quanto ao formato da data e garantem que os poucos parâmetros de origem se tornem definitivamente o tipo de dados da coluna de destino. Isso evita qualquer possibilidade de o banco de dados ter uma conversão implícita incorreta (o argumento de 3 de janeiro / 1º de março do primeiro exemplo) e impede que o banco de dados decida converter um milhão de valores de data na tabela em seqüências de caracteres (usando alguma data específica do servidor formatação que pode nem coincidir com o formato da data nos parâmetros da string no sql) para fazer a comparação - os horrores são abundantes
Minha justificativa social / acadêmica para fazer isso é que o SO é um site de aprendizado; as pessoas nele adquirem conhecimento de forma implícita ou explícita. Para acertar um novato com esta consulta como resposta:
SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'
Pode levá-los a pensar que isso é sensato, ajustando a data para algum formato que preferirem:
SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'
Se eles pelo menos viram alguma tentativa explícita de converter a data, eles podem começar a fazê-lo em seu formato estranho de data e matar alguns bugs eternos antes que surjam. Afinal, nós (I) tentamos dissuadir as pessoas de adotar o hábito de injeção de SQL (e alguém defenderia parametrizar uma consulta e depois declarar para o driver que @pBirthdate
é uma string, quando o frontend tem um tipo de data e hora?)
Voltando ao que acontece depois que eu faço minha recomendação: geralmente recebo alguma resposta à recomendação "seja explícito, use x", como "todo mundo faz isso", "sempre funciona para mim", "me mostre algum documento de referência ou manual que diz que eu deveria ser explícito "ou mesmo" o que? "
Perguntei, em resposta a alguns deles, se eles pesquisariam uma coluna int WHERE age = '99'
passando a idade como uma string. "Não seja bobo, não precisamos colocar 'ao pesquisar int", vem a resposta; portanto, há alguma apreciação por diferentes tipos de dados em sua mente em algum lugar, mas talvez apenas nenhuma conexão com o salto lógico que procura um int coluna passando uma string (aparentemente boba) e pesquisando uma coluna de data passando uma string (aparentemente sensata) é hipocrisia
Assim, em nossos SQLs, temos uma maneira de escrever coisas como números (use numéricos, sem delimitadores), coisas como cadeias de caracteres (use qualquer coisa entre delimitadores de apóstrofo) .. Por que não há delimitadores para datas? É um tipo de dados tão fundamental na maioria dos bancos de dados? Talvez tudo isso possa ser resolvido apenas com uma maneira de escrever uma data da mesma maneira que o javascript nos permite especificar uma regex colocando os /
dois lados de alguns caracteres. /Hello\s+world/
. Por que não ter algo para datas?
Na verdade, que eu saiba, (apenas) o Microsoft Access realmente possui símbolos que indicam "uma data foi escrita entre esses delimitadores" para que possamos obter um bom atalho como, WHERE datecolumn = #somedate#
mas a apresentação da data ainda pode causar problemas, por exemplo, mm / di vs dd / mm, porque o MS sempre tocou rápido e solto com as coisas que o público da VB achou que era uma boa ideia
Voltando ao ponto principal: estou argumentando que é sensato ser explícito com esse meio que nos força a passar uma infinidade de tipos de dados diferentes como strings.
É uma afirmação válida?
Devo continuar esta cruzada? É um ponto válido que a digitação estrita é um não-não moderno? Ou todos os RDBMSs (incluindo versões antigas) lá fora, quando lançados uma consulta, WHERE datecolumn = 'string value'
certamente convertem corretamente a string em uma data e fazem a pesquisa sem converter dados da tabela / perder o uso de índices? Suspeito que não, pelo menos com a experiência pessoal do Oracle 9. Suspeito também que possa haver alguns cenários de fuga com isso, se as strings sempre forem escritas em algum formato padrão ISO e a coluna tiver algum sabor de data, então o O parâmetro string sempre será convertido corretamente implicitamente. Isso faz certo?
É uma tarefa que vale a pena?
Muitas pessoas parecem não entender, ou não se importam, ou exibem alguma hipocrisia, porque suas ints são ints, mas suas datas são seqüências de caracteres. Comum, no entanto, é que poucas pessoas já se viraram e disseram "você sabe concordo com o seu ponto. Serei explícito sobre minhas datas a partir de agora ".
fonte
WHERE datecolumn =
01/02/12 '', onde é possível que eles estejam pedindo o ano de 1912, 2012, 2001, 1901, 12 ou 1. Também é um problema fora do mundo do banco de dados, o número de programadores que não conseguem entender por que a conversão"09"
para um int está causando um acidente são legião, 9 não é um dígito octal válido e um 0 faz com que o octal corda em um monte de sistemasWHERE age = '0x0F'
é uma forma válida para esperar um banco de dados irá procurar por jovens de quinze anos ..Respostas:
Você escreveu:
Essa é realmente uma fonte potencial de erros. Apontar isso para um solicitante pode ajudar outros leitores, portanto, sim, essa é uma preocupação válida. No entanto, para ser construtivo, gostaria
consulte ANSI SQL e use os literais DATE ou DATETIME desse padrão
use o formato de data e hora usual e inequívoco de um DBMS específico (e mencione qual dialeto SQL é usado)
Infelizmente, nem todo DBMS suporta literais de data ANSI SQL exatamente da maneira semelhante (se é que o suporta), portanto, isso normalmente levará a uma variante da segunda abordagem. O fato de "o padrão" não ser rigidamente implementado por diferentes fornecedores de banco de dados é provavelmente parte do problema aqui.
Observe ainda que, para muitos sistemas do mundo real, as pessoas podem contar com um local fixo específico no servidor de banco de dados, mesmo que os aplicativos clientes estejam localizados, porque existe apenas um tipo de servidor, sempre configurado da mesma maneira. Portanto, pode-se presumir que '01 / 03/2017 'tenha o formato fixo' dd / mm / aaaa 'ou' mm / dd / aaaa 'para qualquer SQL usado no sistema específico com o qual eles estão trabalhando. Portanto, se alguém lhe diz "sempre funciona para mim", essa talvez seja uma resposta sensata para o ambiente dele . Se for esse o caso, torna menos interessante discutir esse tópico.
Falando sobre "razões de desempenho": enquanto não houver problemas mensuráveis de desempenho, é supersticioso argumentar com "problemas potenciais de desempenho". Se um banco de dados está realizando um milhão de conversões de string para data ou não, provavelmente não importa quando a diferença horária é de apenas 1/1000 segundo e o gargalo real é a rede que faz com que a consulta dure 10 segundos. Portanto, é melhor deixar de lado essas preocupações, desde que alguém solicite explicitamente considerações de desempenho.
Eu lhe digo um segredo: eu odeio guerras religiosas. Eles não levam a nada útil. Portanto, se especificações ambíguas de data / hora no SQL podem causar problemas, mencione-as, mas não tente forçar as pessoas a serem mais rígidas se isso realmente não lhes trouxer benefícios no contexto atual.
fonte
Sua cruzada não resolve o problema.
Existem dois problemas separados:
conversão implícita de tipo em SQL
formatos de data ambíguos, como 05/06/07
Vejo de onde você vem com sua cruzada, mas não acho que a conversão explícita realmente resolva o problema em questão:
A conversão implícita ainda ocorre em caso de incompatibilidade entre os tipos em uma comparação. Se uma string for comparada a uma data, o SQL tentará converter a string em uma data primeiro. Portanto, comparar uma coluna do tipo data com um valor de data convertido explicitamente é exatamente o mesmo que comparar com uma data no formato de sequência. A única diferença que vejo é se você comparar um valor de data a uma coluna que na verdade não contém datas, mas strings - mas isso seria um erro em qualquer caso.
O uso da conversão explícita não resolve a ambiguidade em formatos de data não ISO.
A única solução que vejo:
E, é claro, nunca armazene datas em uma coluna do tipo string. Mas, novamente, a conversão explícita de literais de data não impedirá isso.
Indiscutivelmente, as conversões implícitas foram um erro no SQL, mas, como a linguagem é projetada, não vejo o benefício da conversão explícita. De qualquer maneira, não evitará a conversão implícita e apenas tornará o código mais difícil de ler e escrever.
fonte
Em primeiro lugar, você tem razão. As datas não devem ser colocadas em strings. Os mecanismos de banco de dados são bestas complexas, nas quais você nunca está 100% certo do que exatamente acontecerá sob o capô, mediante uma consulta arbitrária. A conversão para datas torna as coisas inequívocas e pode aumentar o desempenho.
MAS
Não é um problema que vale o esforço de reflexão extra para resolver para a maioria das pessoas. Se fosse fácil usar literais de data em uma consulta, seria fácil defender sua posição. Mas não é. Eu uso principalmente o SQL Server, portanto, tentar lembrar aquela bagunça para converter uma data simplesmente não está acontecendo.
Para a maioria das pessoas, o ganho de desempenho é insignificante. "Por que sim, senhor chefe, eu gastei 10 minutos extras corrigindo esse bug simples (eu tinha que pesquisar no google como converter datas porque essa sintaxe é ... especial ...). Mas economizei 0,00001 segundos extras em uma consulta raramente executada ". Isso não vai voar na maioria dos lugares em que trabalhei.
Mas remove a ambiguidade nos formatos de data que você diz. Novamente, para muitas aplicações (aplicações internas da empresa, assuntos do governo local, etc. etc.), isso não é realmente uma preocupação. E para os aplicativos em que há uma preocupação (aplicativos grandes, internacionais ou corporativos), isso se torna uma preocupação da camada de interface do usuário / negócios ou essas empresas já têm uma equipe de DBAs bem versados que já sabem disso. TL / DR: se a internacionalização é uma preocupação, alguém já está pensando nisso e já fez o que você sugere (ou mitigou o problema).
E agora?
Se você se sentir tão inclinado, continue lutando a boa luta. Mas não se surpreenda se a maioria das pessoas não achar que isso é importante o suficiente para se preocupar. Só porque há situações em que isso importa, não significa que essa seja a situação de todos (e provavelmente não). Portanto, não se surpreenda ao receber algo que seja tecnicamente correto e melhor, mas não realmente relevante.
fonte
Supondo que "datas" estão sendo passadas "em" Strings, então sim; Eu concordo absolutamente que você está certo em fazer isso.
Quando é "01/04/07"?
* 4 de janeiro?
* 1 de abril?
* 7 de abril de 2001?
Qualquer um ou todos estes podem estar corretos, dependendo de como "o computador" optar por interpretá-los.
Se vocês precisar criar SQL dinâmico com literais, a formatação da data deverá ser bem definida e, de preferência, independente da máquina (eu tinha uma estranha no Windows Server, onde o processamento baseado em datas no Serviço do Windows deu errado porque um operador fez logon no console com diferentes preferências de formato de data!). Pessoalmente, uso exclusivamente [d] o formato "aaaa-mm-dd".
Contudo ...
A melhor solução é usar as consultas parametrizadas que forçam o tipo de dados a ser convertido antes que o SQL seja envolvido - obter um valor "date" em um Date Parameter força a conversão do tipo desde o início (tornando-o um problema de codificação e não um SQL) .
fonte
WHERE datecolumn = @dateParameter
e depois no código de front-end, informando o driver DB que@dateParameter
é do tipo varchar e mantendo"01/04/07"
-o. A inspiração original para minha pergunta é que eu suspeito que qualquer pessoa que me diga que eu sou louca por fazer isso em uma consulta parametrizada iria, no mesmo fôlego, fornecer uma resposta SO de uma linha que pareceWHERE datecol = 'some string that looks like a date'
(e espera que um novato deva saber é apenas uma sugestão / parametrizar-lo para questões evitar)