À propos de ma croisade de types de données de date de base de données: valide? Digne d'intérêt? Quelqu'un d'autre le ressent-il?

13

Je passe beaucoup de temps à répondre aux questions SQL sur SO. Je rencontre fréquemment des requêtes de cet acabit:

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'

c'est-à-dire soit en s'appuyant sur une conversion implicite de chaîne en date (mauvaise), des paramètres donnés, soit en se basant sur la base de données convertissant x millions de valeurs de ligne de base de données en chaîne et effectuant une comparaison de chaîne (pire)

Je fais parfois un commentaire, en particulier si c'est un utilisateur de haut niveau qui écrit une réponse intelligente, mais que je pense vraiment devrait être moins bâclé / tapé avec leurs types de données

Le commentaire prend généralement la forme qu'il serait probablement préférable de convertir explicitement leurs chaînes en dates, en utilisant to_date (Oracle), str_to_date (MySQL), convert (SQLSERVER) ou un mécanisme similaire:

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

Ma justification technique pour ce faire est qu'elle est explicite quant au format de la date et garantit que les quelques paramètres source deviennent définitivement le type de données de la colonne cible. Cela empêche toute possibilité que la base de données obtienne une conversion implicite incorrecte (l'argument du 3 janvier / 1er mars du tout premier exemple) et empêche la base de données de décider de convertir un million de valeurs de date dans la table en chaînes (en utilisant une date spécifique au serveur formatage qui pourrait même ne pas correspondre au format de la date dans les paramètres de chaîne dans le sql) afin de faire la comparaison - les horreurs abondent

Ma justification sociale / académique pour le faire est que SO est un site d'apprentissage; les gens y acquièrent des connaissances implicitement ou explicitement. Pour frapper un débutant avec cette requête comme réponse:

SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'

Cela pourrait les amener à penser que cela est raisonnable, en ajustant la date pour un format qu'ils préfèrent:

SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'

S'ils ont au moins vu une tentative explicite de convertir la date, ils pourraient commencer à le faire pour leur format de date étrange, et tuer des bugs pour toujours avant qu'ils ne surviennent. Après tout, nous (j') essayons de dissuader les gens de prendre l'habitude d'injection SQL (et est-ce que quelqu'un préconiserait de paramétrer une requête puis de déclarer au pilote qu'il @pBirthdates'agit d'une chaîne, lorsque le frontend a un type datetime?)

Revenons à ce qui se passe après avoir fait ma recommandation: j'obtiens généralement une réponse à la recommandation "soyez explicite, utilisez x", comme "tout le monde le fait", "ça marche toujours pour moi", "montrez-moi un manuel ou un document de référence qui dit que je devrais être explicite "ou même" quoi ?? "

J'ai demandé, en réponse à certains d'entre eux, s'ils rechercheraient une colonne int en faisant WHERE age = '99'passer l'âge comme une chaîne. "Ne sois pas stupide, nous n'avons pas besoin de mettre" lors de la recherche int "vient la réponse, donc il y a une certaine appréciation pour différents types de données dans leur esprit quelque part, mais peut-être juste aucun lien avec le saut logique que la recherche d'un int colonne en passant une chaîne (apparemment idiot) et la recherche d'une colonne de date en passant une chaîne (apparemment sensible) est de l'hypocrisie

Donc, dans nos SQL, nous avons un moyen d'écrire des choses sous forme de nombres (utilisez des chiffres, sans délimiteurs), des choses sous forme de chaînes de caractères (utilisez n'importe quoi entre les délimiteurs d'apostrophe). Pourquoi pas de délimiteurs pour les dates? C'est un type de données fondamental dans la plupart des bases de données? Est-ce que tout cela pourrait être résolu simplement en ayant un moyen d'écrire une date de la même manière que javascript nous permet de spécifier une expression régulière en mettant de /chaque côté de certains caractères. /Hello\s+world/. Pourquoi ne pas avoir quelque chose pour les dates?

En fait, à ma connaissance, (uniquement) Microsoft Access a en fait des symboles qui indiquent "une date a été écrite entre ces délimiteurs" afin que nous puissions obtenir un bon raccourci comme WHERE datecolumn = #somedate#mais la présentation de la date est toujours susceptible de poser des problèmes, par exemple mm / di vs dd / mm, parce que les MS ont toujours joué vite et avec les trucs que la foule VB pensait être une bonne idée


Revenons au point principal: je soutiens qu'il est sage d'être explicite avec ce médium qui nous oblige à passer une multitude de types de données différents sous forme de chaînes.

Est-ce une affirmation valable?

Dois-je continuer cette croisade? Est-ce un point valable que la saisie en chaîne est un non-non moderne? Ou est-ce que tous les SGBDR (y compris les versions anciennes) là-bas, lorsque poussé une requête, WHERE datecolumn = 'string value'convertiront certainement correctement la chaîne en une date et effectueront la recherche sans convertir les données de table / perdre l'utilisation des index? Je soupçonne que non, du moins par expérience personnelle d'Oracle 9. Je soupçonne également qu'il peut y avoir des scénarios d'évasion si les chaînes sont toujours écrites dans un certain format standard ISO, et la colonne est une saveur de date, puis le Le paramètre de chaîne sera toujours correctement implicitement converti. Est-ce que cela le rend juste?

Est-ce une tâche valable?

Beaucoup de gens ne semblent pas comprendre, ou s'en moquent, ou présentent une certaine hypocrisie dans la mesure où leurs ints sont des ints mais leurs dates sont des chaînes. quoi, je suis d'accord avec votre point. Je serai explicite sur mes dates à partir de maintenant ".

Caius Jard
la source
J'ai même vu quelqu'un avoir des problèmes avec WHERE datecolumn = 01/02/12 '' où il est possible qu'ils demandent l'année 1912, 2012, 2001, 1901, 12 ou 1. C'est aussi un problème en dehors du monde de la base de données, le nombre des programmeurs qui ne comprennent pas pourquoi la conversion "09"en int entraîne un crash sont légion, 9 n'est pas un chiffre octal valide et un 0 de tête rend la chaîne octale dans de nombreux systèmes
Steve Barnes
2
J'ai pensé à étendre mon exemple pour demander si WHERE age = '0x0F'c'est un moyen valable d'espérer qu'une base de données recherchera des jeunes de quinze ans.
Caius Jard
1
J'ai supprimé une question hors sujet ici - nous ne faisons pas de demandes de ressources. Un des 2 votes serrés a été donné pour cette raison. Sinon, je pense que c'est une question valable, même si elle pourrait être trop large. J'espère que la suppression de la question hors sujet contribuera à affiner un peu les choses.
Thomas Owens
TL; DR mais dans les systèmes de production, je m'attendrais à ce que les dates comme celle-ci soient presque toujours dans les paramètres. Le codage en dur des dates dans les requêtes est un problème plus important que si vous utilisez des conversions implicites. Si j'écris une requête jetable, cela fonctionne ou non. Je ne fais jamais ça de toute façon (car je ne me souviens jamais du format de date par défaut) mais je ne suis pas sûr que cela compte beaucoup.
JimmyJames
1
La vie consiste à choisir vos batailles. À mon avis, celui-ci ne vaut tout simplement pas la peine d'être combattu ...
Robbie Dee

Réponses:

7

Tu as écrit:

sont ces paramètres du 1er janvier au 3 janvier ou 1er mars.

C'est en effet une source potentielle d'erreurs. Le signaler à un demandeur peut être utile à d'autres lecteurs, alors oui, c'est une préoccupation valable. Cependant, pour être constructif, je voudrais

  • se référer à ANSI SQL et utiliser les littéraux DATE ou DATETIME de cette norme

  • utiliser le format datetime habituel et sans ambiguïté d'un SGBD spécifique (et mentionner le dialecte SQL utilisé)

Malheureusement, tous les SGBD ne prennent pas en charge les littéraux de date ANSI SQL exactement de la même manière (s'ils le prennent en charge), donc cela conduira généralement à une variante de la deuxième approche. Le fait que "la norme" ne soit pas implémentée de manière rigide par différents fournisseurs de bases de données fait probablement partie du problème ici.

Notez en outre que, pour de nombreux systèmes du monde réel, les gens peuvent réellement compter sur un environnement local fixe spécifique sur le serveur de base de données, même si les applications clientes sont localisées, car il n'y a qu'un seul type de serveur, toujours configuré de la même manière. Ainsi, '01 / 03/2017 'peut souvent être supposé avoir le format fixe' jj / mm / aaaa 'ou' mm / jj / aaaa 'pour tout SQL utilisé sur le système spécifique avec lequel ils travaillent. Donc, si quelqu'un vous dit "ça marche toujours pour moi", c'est peut-être une réponse sensée pour son environnement . Si tel est le cas, il est moins utile de discuter de ce sujet.

Parler de "raisons de performances": tant qu'il n'y a pas de problèmes de performances mesurables, il est assez superstitieux de discuter des "problèmes de performances potentiels". Si une base de données effectue un million de conversions de chaînes à jour ou non, peu importe quand la différence de temps n'est que de 1/1000 seconde, et le véritable goulot d'étranglement est le réseau qui fait que la requête dure 10 secondes. Il vaut donc mieux mettre ces préoccupations de côté tant que quelqu'un demande explicitement des considérations de performance.

Dois-je continuer cette croisade?

Je vous dis un secret: je déteste les guerres de religion. Ils ne mènent à rien d'utile. Donc, si des spécifications de date / heure ambigües dans SQL peuvent entraîner des problèmes, mentionnez-les, mais n'essayez pas de forcer les gens à être plus rigides si cela ne leur apporte pas vraiment d'avantages dans leur contexte actuel.

Doc Brown
la source
Ce n'est pas tellement une question sur l'ambiguïté des formats de date américains vs sensibles. Il s'agit de savoir s'il est judicieux de passer des dates dans une instruction SQL sous forme de chaîne et de s'appuyer sur une conversion implicite à date. La question de la base de données devant faire un million de conversions date-> str pour tous les millions de lignes est un aspect de performance, et cela peut prendre seulement 1 / 1000e de seconde pour une requête, mais imaginez-le maintenant dans le contexte de plusieurs dizaines de concurrents utilisateurs. Le plus gros problème de performances est que la conversion des données signifie que les index ne peuvent plus être utilisés et que cela peut être vraiment grave
Caius Jard
@CaiusJard: ma réponse tient: c'est parfois sensé, parfois non, cela dépend du contexte. Et honnêtement, je refuse de "... imaginer ..." quoi que ce soit ici. Quand il s'agit de performances, discuter de tout cas hypothétique n'est pas utile. Lorsqu'il y a des problèmes de performances mesurables, il est temps d'optimiser, et parfois de micro-optimiser, pas au préalable.
Doc Brown
Il est intéressant de voir cela comme hypothétique; Je vois que le fait de s'appuyer sur un comportement implicite est une opportunité claire de se produire des bogues et des complications de performance (pour des raisons bien documentées: les index ne fonctionnent pas si toutes les données de la colonne sont transformées avant d'être recherchées), et avec des instructions explicites, cela ne peut pas se produire
Caius Jard
@CaiusJard: ne jouez pas avec les mots - avec "hypothétique" je ne veux pas dire "improbable", j'ai utilisé le terme pour tout type de scénario imaginaire, par opposition à "situation réelle existante" où l'on peut mesurer ce qui se passe.
Doc Brown
1
@CaiusJard: si vous voulez impressionner d'autres professionnels de l'industrie, vous devez savoir exactement pourquoi "l'optimisation des performances" est très différente de "l'optimisation de la sécurité", et c'est exactement mon point ici - les problèmes de performances peuvent être traités après leur apparition, ce qui est rarement trop tard. Les problèmes de sécurité non, ils doivent être soigneusement évités avant qu'ils ne surviennent. Veuillez donc ne pas comparer les pommes avec les oranges. Si vous aimez les croisades, les arguments de sécurité sont bien mieux adaptés à cela ;-)
Doc Brown
5

Votre croisade ne résout pas le problème.

Il y a deux problèmes distincts:

  • conversion de type implicite en SQL

  • formats de date ambigus comme 05/06/07

Je vois d'où vous venez avec votre croisade, mais je ne pense pas que la conversion explicite résout réellement le problème actuel:

  • La conversion implicite se produit toujours en cas de non-correspondance entre les types dans une comparaison. Si une chaîne est comparée à une date, SQL tentera de convertir d'abord la chaîne en date. Ainsi, la comparaison d'une colonne de type date à une valeur de date convertie explicitement est exactement la même chose que la comparaison à une date au format chaîne. La seule différence que je vois est que si vous comparez une valeur de date à une colonne qui ne contient pas réellement de dates mais des chaînes - mais ce serait une erreur dans tous les cas.

  • L'utilisation d'une conversion explicite ne résout pas l'ambiguïté dans les formats de date non ISO.

La seule solution que je vois:

  • ne comparez pas les colonnes de type chaîne à des valeurs non-chaîne.
  • n'utilisez que des formats de date de type ISO.

Et bien sûr, ne stockez jamais les dates dans une colonne de type chaîne. Mais encore une fois, la conversion explicite des littéraux de date n'empêchera pas cela.

On peut dire que les conversions implicites étaient une erreur dans SQL, mais étant donné la conception du langage, je ne vois pas l'avantage de la conversion explicite. De toute façon, il n'évitera pas la conversion implicite et ne fera que rendre le code plus difficile à lire et à écrire.

JacquesB
la source
Vrai. Peut-être devrais-je souligner dans cette perspective que la chose la plus raisonnable à faire est de s'assurer que l'opérande de la colonne de données et l'opérande de valeur ont le même type de données (que ce soit une chaîne, une date, peu importe). Je fais spécifiquement cette recommandation uniquement dans les questions où je sais que la colonne du tableau est DATETIME et leur exemple de réponse utilise un opérande de chaîne avec conversion implicite ..
Caius Jard
Quelque chose ne me convient pas sur cette réponse. Vous faites quelques remarques intéressantes, mais j'ai l'impression que la conclusion est idéaliste. Du point de vue de la conception, oui, les formats de date non ISO sont ambigus à l'œil humain, mais si vous utilisez une conversion explicite, syntaxiquement, elle n'est pas ambiguë pour l'analyseur. De même, de nombreux processus ETL impliquant des dates vont nécessiter une comparaison (sous la forme d'une importation de fichier) d'une chaîne au format de date de la base de données. Essayer d'éliminer les comparaisons de chaînes à ce jour me semble irréaliste.
DanK
@DanK: ETL est un problème différent - si vous lisez des données à partir d'un fichier CSV ou quelque chose, vous devez évidemment traiter les données sous forme de chaînes et les analyser explicitement en valeurs typées. Mais ce n'est pas le scénario que l'OP décrit.
JacquesB
Cela pourrait facilement être le point que je décris cependant; il n'y a rien de spécial dans une chaîne de nombres stockée dans un csv qui exige de déclarer explicitement le format lors de l'analyse et cela devient pertinent pour l'argument que je fais si un débutant lit une réponse dans SO où le pro ne fait aucun effort pour explicitement déclarer le format de la date, amenant le débutant à supposer qu'il n'a pas à s'en soucier (ou que la base de données l'analysera correctement tout le temps)
Caius Jard
@CaiusJard: Je pense que ce sont des scénarios très différents. Lorsque je parle de SQL dans des scénarios normaux, je suppose que les colonnes ont les types appropriés - c'est-à-dire que les colonnes entières sont de type entier, les colonnes de date sont de type données et ainsi de suite. Si vous n'avez pas les bons types dans les tableaux (c'est-à-dire stocker les dates sous forme de chaînes), vous êtes en grande difficulté et la conversion explicite des littéraux de date dans les requêtes ne vous sauvera pas , ce qui est mon point.
JacquesB
3

D'abord et avant tout, vous avez raison. Les dates ne doivent pas être mises en chaînes. Les moteurs de base de données sont des bêtes complexes où vous n'êtes jamais sûr à 100% de ce qui se passera exactement sous le capot, étant donné une requête arbitraire. La conversion en dates rend les choses sans ambiguïté et peut augmenter les performances.

MAIS

Ce n'est pas un problème qui mérite un effort de réflexion supplémentaire à résoudre pour la plupart des gens. S'il était facile d'utiliser des littéraux de date dans une requête, il serait facile de défendre votre position. Mais ce n'est pas le cas. J'utilise principalement SQL Server, donc essayer de me souvenir de ce gâchis pour convertir une date ne se produit tout simplement pas.

Pour la plupart des gens, le gain de performances est négligeable. "Pourquoi oui M. Boss-man, j'ai passé 10 minutes supplémentaires à corriger ce simple bug (j'ai dû chercher comment convertir les dates sur google parce que cette syntaxe est ... spéciale ...). Mais j'ai économisé 0,00001 seconde de plus sur une requête rarement exécutée. " Cela ne va pas voler dans la plupart des endroits où j'ai travaillé.

Mais cela supprime l'ambiguïté dans les formats de date que vous dites. Encore une fois, pour de nombreuses applications (applications internes à l'entreprise, administration locale, etc., etc.), ce n'est pas vraiment un problème. Et pour les applications où il s'agit d'un problème (applications de grande taille, internationales ou d'entreprise), cela devient un problème d'interface utilisateur / de couche métier ou ces entreprises ont déjà une équipe de DBA bien informés qui le savent déjà. TL / DR: si l'internationalisation est une préoccupation, quelqu'un y pense déjà et a déjà fait ce que vous suggérez (ou a autrement atténué le problème).

Et maintenant?

Si vous vous sentez si enclin, continuez à combattre le bon combat. Mais ne soyez pas surpris si la plupart des gens ne pensent pas que c'est suffisamment important pour s'inquiéter. Tout simplement parce qu'il y a des situations où cela compte, cela ne signifie pas que c'est la situation de tout le monde (et ce n'est probablement pas le cas). Ne soyez donc pas surpris lorsque vous obtenez un coup de pouce pour quelque chose qui est techniquement correct et meilleur, mais pas vraiment pertinent.

Becuzz
la source
1

Je soutiens qu'il est sage d'être explicite avec ce médium qui nous oblige à passer une multitude de types de données différents sous forme de chaînes.

En supposant que des "dates" sont transmises "dans" les chaînes, alors oui; Je suis absolument d'accord que vous avez raison de le faire.

Quand est le "01/04/07"?
* 4 janvier?
* 1er avril?
* 7 avril [2001]?

Tout ou partie de ces informations peuvent être correctes, selon la façon dont "l'ordinateur" choisit de les interpréter.

Si vous devez créer du SQL dynamique avec des littéraux, votre formatage de date doit être bien défini et, de préférence, indépendant de la machine (j'en avais un étrange sur un serveur Windows où le traitement basé sur la date dans un service Windows a mal tourné car un opérateur s'est connecté à la console avec des préférences de format de date différentes!). Personnellement, j'utilise exclusivement [d] le format "aaaa-mm-jj".

Pourtant ...

La meilleure solution consiste à utiliser des requêtes paramétrées qui forcent le type de données à être converti avant que SQL ne s'implique - obtenir une valeur de "date" dans un paramètre de date force la conversion de type dès le début (ce qui en fait purement un problème de codage, pas SQL) .

Phill W.
la source
Je suis d'accord, bien que le même problème puisse être résolu avec des requêtes paramétrées, en faisant WHERE datecolumn = @dateParameteret ensuite dans le code frontal, en indiquant au pilote de base de données qui @dateParameterest de type varchar, et en y collant "01/04/07". L'inspiration originale de ma question est que je soupçonne que quiconque me dirait que je suis fou de le faire pour une requête paramétrée donnerait alors, dans le même souffle, une réponse SO d'une ligne qui ressemble à WHERE datecol = 'some string that looks like a date'(et s'attend à ce qu'un débutant sache c'est juste un indice / paramétrez-le pour éviter les problèmes)
Caius Jard