Sobre a cruzada do tipo de dados da data do meu banco de dados: Válido? Que vale a pena? Alguém mais sente isso?


13

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 ".


Eu até vi alguém tendo problemas com 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 sistemas
Steve Barnes

2
I fez pensar sobre como estender o meu exemplo para perguntar se WHERE age = '0x0F'é uma forma válida para esperar um banco de dados irá procurar por jovens de quinze anos ..
Caio Jard

1
Eu removi uma pergunta que está fora do tópico aqui - não fazemos solicitações de recursos. Uma das duas votações apertadas foi dada por esse motivo. Caso contrário, acho que essa é uma pergunta válida, embora possa parecer muito ampla. Espero que a remoção da questão fora do tópico ajude a restringir um pouco as coisas.
Thomas Owens

TL; DR, mas em sistemas de produção, eu esperaria que datas como essa quase sempre estivessem em parâmetros. A codificação das datas nas consultas é um problema maior do que se você usa conversões implícitas. Se eu estiver escrevendo alguma consulta descartável, ela funciona ou não. Eu nunca faço isso de qualquer maneira (porque nunca consigo me lembrar do formato de data padrão), mas não tenho certeza se isso importa muito.
JimmyJames

1
A vida é sobre escolher suas batalhas. A meu ver, este apenas não vale a pena lutar ...
Robbie Dee

Respostas:


7

Você escreveu:

são esses parâmetros de 1º de janeiro a 3 de janeiro ou 1º de março.

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.

Devo continuar esta cruzada?

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.


Porém, essa não é uma questão muito grande sobre a ambiguidade dos formatos de data American vs Sensible. É sobre se é sensato passar datas em uma instrução SQL como uma string e confiar na conversão implícita até a data. A questão do banco de dados ter que fazer um milhão de conversões date-> str para todos os milhões de linhas é um aspecto do desempenho, e pode levar apenas 1/1000 milésimos de segundo para uma consulta, mas agora imagine-o no contexto de milhares de concorrentes Comercial. O problema de desempenho maior é que a conversão significa que os dados índices não pode mais ser usado e que pode ser muito grave
Caio Jard

@ CaioJard: minha resposta está: às vezes é sensata, e às vezes não, depende do contexto. E honestamente, eu me recuso a "... imaginar ..." qualquer coisa aqui. Quando se trata de desempenho, discutir qualquer caso hipotético não é útil. Quando há problemas mensuráveis ​​de desempenho, é hora de otimizar e, às vezes, otimizar, não antes.
Doc Brown

É interessante que você o veja como hipotético; Vejo confiar no comportamento implícito como uma clara oportunidade de surgimento de erros e complicações de desempenho (por razões bem documentadas: os índices não funcionam se os dados da coluna inteira forem transformados antes de serem pesquisados) e, com instruções explícitas, isso não pode acontecer
Caius Jard

@CaiusJard: não brinque com palavras - com "hipotético", não quero dizer "improvável", usei o termo para qualquer tipo de cenário imaginado, em oposição a "situação real existente", onde é possível medir o que acontece.
Doc Brown

1
@CaiusJard: se você deseja impressionar outros profissionais do setor, deve saber exatamente por que "otimização de desempenho" é muito diferente de "otimização de segurança", e esse é exatamente o meu ponto aqui - os problemas de desempenho podem ser resolvidos depois que ocorrem, isso raramente é muito tarde. Problemas de segurança, não, eles devem ser completamente evitados antes que ocorram. Então, por favor, não compare maçãs com laranjas. Se você gosta cruzadas, argumentos de segurança são muito mais adequado para este ;-)
Doc Brown

5

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:

  • não compare colunas do tipo string com valores que não sejam da string.
  • use apenas os formatos de data do tipo ISO.

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.


Verdade. Talvez eu deva apontar dessa perspectiva, que a coisa mais sensata a fazer é garantir que o operando da coluna de dados e o operando de valor tenham o mesmo tipo de dados (seja string, data, qualquer que seja). Eu especificamente fazer esta recomendação apenas em questões onde eu conheço a coluna da tabela é DATETIME e sua resposta exemplo é usar um operando string com conversão implícita ..
Caio Jard

Algo não está certo comigo nesta resposta. Você faz alguns pontos interessantes, mas eu sinto que a conclusão é idealista. Do ponto de vista do design, sim, os formatos de data não ISO são ambíguos para o olho humano, mas se estiverem usando conversão explícita, sintaticamente, não é ambíguo para o analisador. Da mesma forma, muitos processos de ETL envolvendo datas exigirão alguma comparação (na forma de importação de arquivo) de uma cadeia de caracteres com o formato de data do banco de dados. Tentar eliminar comparações de strings até a data parece irreal para mim.
DANK

@ DanK: ETL é uma questão diferente - se você estiver lendo dados de um arquivo CSV ou algo assim, obviamente você precisará processar os dados como strings e analisar explicitamente os valores digitados. Mas esse não é o cenário que o OP está descrevendo.
precisa saber é o seguinte

Poderia facilmente ser o ponto que estou descrevendo; não há nada de especial em uma série de números armazenados em um csv que exige declarar explicitamente o formato ao analisar e se torna relevante para o argumento que estou fazendo se um novato lê alguma resposta no SO, em que o profissional não faz nenhum esforço para explicitamente formato de data declarar, levando novato a assumir que eles não precisam se preocupar com isso (ou que a db irá analisá-lo corretamente o tempo todo)
Caio Jard

@CaiusJard: Eu acredito que estes são cenários muito diferentes. Ao falar sobre SQL em cenários normais, presumo que as colunas tenham os tipos apropriados - ou seja, colunas inteiras são do tipo inteiro, colunas da data são do tipo de dados e assim por diante. Se você não possui os tipos corretos nas tabelas (ou seja, armazena as datas como seqüências de caracteres), está com problemas profundos e os literais explícitos da data da conversão nas consultas não o salvarão , e esse é o meu ponto.
precisa saber é o seguinte

3

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.


1

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.

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) .


Concordo, embora o mesmo problema possa ser reforçado com consultas parametrizadas, executando-o WHERE datecolumn = @dateParametere 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 parece WHERE 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)
Caio Jard
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.