Über meinen Datenbanktyp Kreuzzug: Gültig? Lohnend? Fühlt es noch jemand?


13

Ich verbringe viel Zeit damit, SQL-Fragen zu SO zu beantworten. Ich stoße häufig auf Fragen dieser Art:

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'

dh entweder auf eine implizite Konvertierung der angegebenen Parameter von Zeichenfolge zu Datum (schlecht) oder auf die Datenbank, die x Millionen Datenbankzeilenwerte in Zeichenfolge konvertiert und einen Zeichenfolgenvergleich durchführt (schlechter)

Ich mache gelegentlich einen Kommentar, besonders wenn es sich um einen High-Rep-Benutzer handelt, der eine kluge Antwort schreibt, der jedoch meiner Meinung nach mit seinen Datentypen weniger schlampig / streng getippt sein sollte

Der Kommentar hat normalerweise die Form, dass es wahrscheinlich besser wäre, wenn sie ihre Zeichenfolgen explizit mit to_date (Oracle), str_to_date (MySQL), convert (SQLSERVER) oder einem ähnlichen Mechanismus in Datumsangaben konvertieren:

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

Meine technische Rechtfertigung dafür ist, dass das Format des Datums explizit angegeben wird und sichergestellt ist, dass die wenigen Quellparameter definitiv zum Datentyp der Zielspalte werden. Dies verhindert, dass die Datenbank implizit eine falsche Konvertierung erhält (das Argument vom 3. Januar / 1. März des allerersten Beispiels), und verhindert, dass die Datenbank beschließt, eine Million Datumswerte in der Tabelle in Zeichenfolgen zu konvertieren (wobei ein serverspezifisches Datum verwendet wird) Formatierungen, die möglicherweise nicht einmal mit dem Format des Datums in den Zeichenfolgenparametern innerhalb von sql) übereinstimmen, um den Vergleich durchzuführen - Horror gibt es zuhauf

Meine soziale / akademische Rechtfertigung dafür ist, dass SO eine Lernseite ist; Personen, die sich damit befassen, erwerben implizit oder explizit Wissen. So treffen Sie einen Neuling mit dieser Abfrage als Antwort:

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

Könnte sie dazu bringen, dies für sinnvoll zu halten und das Datum für ein von ihnen bevorzugtes Format anzupassen:

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

Wenn sie zumindest einen expliziten Versuch gesehen haben, das Datum zu konvertieren, könnten sie damit beginnen, es für ihr seltsames Datumsformat zu tun und einige Ewige-Fehler zu beseitigen, bevor sie auftauchen. Schließlich versuchen wir (ich), die Leute davon abzuhalten, in die Gewohnheit der SQL-Injection einzusteigen (und würde jemand befürworten, eine Abfrage zu parametrisieren und dann dem Treiber zu deklarieren, dass @pBirthdatees sich um eine Zeichenfolge handelt, wenn das Frontend einen Datetime-Typ hat?).

Zurück zu dem, was passiert, nachdem ich meine Empfehlung abgegeben habe: Normalerweise erhalte ich einen Pushback zu der Empfehlung "explizit sein, x verwenden", wie "jeder tut es", "es funktioniert immer für mich", "zeige mir ein Handbuch oder ein Referenzdokument das sagt, ich sollte explizit "oder sogar" was? "

Als Antwort auf einige dieser Fragen habe ich gefragt, ob sie eine int-Spalte durchsuchen würden, indem WHERE age = '99'sie das Alter als Zeichenfolge übergeben. "Seien Sie nicht albern, wir müssen nicht 'setzen, wenn Sie nach int suchen", lautet die Antwort. Daher haben sie irgendwo eine gewisse Wertschätzung für verschiedene Datentypen, aber vielleicht auch keine Verbindung zu dem logischen Sprung, der beim Suchen eines int besteht Spalte durch Übergeben einer Zeichenfolge (scheinbar albern) und Durchsuchen einer Datumsspalte durch Übergeben einer Zeichenfolge (scheinbar sinnvoll) ist Heuchelei

Also haben wir in unseren SQLs eine Möglichkeit, Dinge als Zahlen zu schreiben (verwenden Sie Zahlen ohne Begrenzer), Dinge als Zeichenketten (verwenden Sie irgendetwas zwischen Apostroph-Begrenzern). Warum keine Begrenzer für Datumsangaben? Es ist so ein grundlegender Datentyp in den meisten DB? Könnte diese ganze Sache vielleicht einfach dadurch gelöst werden, dass man ein Datum auf die gleiche Weise schreibt, wie Javascript es uns ermöglicht, einen regulären Ausdruck zu spezifizieren, indem man /beide Seiten einiger Zeichen einsetzt. /Hello\s+world/. Warum nicht etwas für Dates haben?

Meines Wissens verfügt Microsoft Access (nur) tatsächlich über Symbole, die angeben, dass "ein Datum zwischen diesen Begrenzern geschrieben wurde", sodass wir eine gute Abkürzung erhalten, WHERE datecolumn = #somedate#aber die Datumsdarstellung kann weiterhin Probleme verursachen, z. B. mm / di vs dd / mm, weil MS immer schnell und locker mit dem Zeug gespielt haben, fand das VB-Publikum eine gute Idee


Zurück zum Hauptpunkt: Ich behaupte, es ist ratsam, mit diesem Medium explizit umzugehen, das uns zwingt, eine Vielzahl verschiedener Datentypen als Zeichenfolgen zu übergeben.

Ist es eine gültige Behauptung?

Soll ich diesen Kreuzzug fortsetzen? Ist es ein gültiger Punkt, dass das strikte Tippen ein modernes Nein-Nein ist? Oder werden alle RDBMSs (einschließlich alter Versionen) da draußen, wenn eine Abfrage durchgeführt WHERE datecolumn = 'string value'wird, die Zeichenfolge absolut sicher korrekt in ein Datum konvertieren und die Suche durchführen, ohne Tabellendaten zu konvertieren / die Verwendung von Indizes zu verlieren? Ich vermute nein, zumindest aus persönlicher Erfahrung mit Oracle 9. Ich vermute auch, dass es einige Ausweichszenarien geben kann, wenn Zeichenfolgen immer in einem ISO-Standardformat geschrieben werden und die Spalte eine Datumsangabe enthält string parameter werden implizit immer korrekt konvertiert. Macht das alles richtig?

Lohnt es sich?

Viele Leute scheinen es nicht zu verstehen, oder es ist ihnen egal, oder sie zeigen eine Heuchelei, weil ihre Ints Ints sind, aber ihre Daten Zeichenketten Ich stimme Ihrem Punkt zu. Ich werde meine Daten von nun an explizit nennen. "


Ich habe sogar gesehen, dass jemand Probleme mit WHERE datecolumn = 01/02 / 12'` hat, wenn er möglicherweise nach dem Jahr 1912, 2012, 2001, 1901, 12 oder 1 fragt. Es ist auch ein Problem außerhalb der Datenbankwelt, der Nummer von Programmierern, die nicht verstehen können, warum das Konvertieren "09"in ein int einen Absturz verursacht, sind Legion, 9 ist keine gültige Oktalziffer und eine führende 0 macht den String in vielen Systemen oktal
Steve Barnes

2
Ich habe darüber nachgedacht, mein Beispiel zu erweitern, um zu fragen, ob WHERE age = '0x0F'eine Datenbank nach 15-Jährigen sucht.
Caius Jard,

1
Ich habe eine Frage entfernt, die hier nicht zum Thema gehört. Wir führen keine Ressourcenanforderungen durch. Aus diesem Grund wurde eine der 2 Stimmen vergeben. Ansonsten halte ich dies für eine berechtigte Frage, auch wenn sie möglicherweise zu weit gefasst ist. Ich hoffe, dass das Entfernen der Off-Topic-Frage dazu beiträgt, die Dinge ein wenig einzugrenzen.
Thomas Owens

TL; DR, aber in Produktionssystemen würde ich erwarten, dass Daten wie diese fast immer in Parametern vorliegen. Das Hardcodieren von Daten in Abfragen ist ein größeres Problem als die Verwendung impliziter Konvertierungen. Wenn ich eine Wegwerfabfrage schreibe, funktioniert sie entweder oder nicht. Ich mache das sowieso nie (weil ich mich nie an das voreingestellte Datumsformat erinnern kann), aber ich bin mir nicht sicher, ob es wichtig ist.
JimmyJames

1
Im Leben geht es darum, deine Schlachten auszusuchen. Meiner Meinung nach ist dieser Kampf einfach nicht wert ...
Robbie Dee

Antworten:


7

Sie schrieben:

sind diese Parameter 1. Januar bis 3. Januar oder 1. März ..

Das ist in der Tat eine potenzielle Fehlerquelle. Wenn Sie dies einem Fragesteller mitteilen, kann dies für andere Leser hilfreich sein. Dies ist also ein berechtigtes Anliegen. Um jedoch konstruktiv zu sein, würde ich

  • Verweisen Sie auf ANSI SQL, und verwenden Sie die DATE- oder DATETIME-Literale aus diesem Standard

  • Verwenden Sie das übliche, eindeutige Datums- / Uhrzeitformat eines bestimmten DBMS (und geben Sie an, welcher SQL-Dialekt verwendet wird).

Leider unterstützt nicht jeder DBMS ANSI SQL-Datumsliterale auf genau ähnliche Weise (sofern überhaupt), sodass dies in der Regel zu einer Variante des zweiten Ansatzes führt. Die Tatsache, dass "der Standard" von verschiedenen DB-Anbietern nicht starr implementiert wird, ist wahrscheinlich ein Teil des Problems.

Beachten Sie außerdem, dass sich viele reale Systeme auf ein bestimmtes, festes Gebietsschema auf dem Datenbankserver verlassen können, auch wenn die Clientanwendungen lokalisiert sind, da es nur einen Servertyp gibt, der immer auf die gleiche Weise konfiguriert ist. Daher wird häufig angenommen, dass '01 / 03/2017 'das feste Format' tt / mm / jjjj 'oder' mm / tt / jjjj 'für jede SQL hat, die auf dem jeweiligen System verwendet wird, mit dem sie arbeiten. Wenn also jemand sagt, "es funktioniert immer für mich", ist dies möglicherweise eine vernünftige Antwort für seine Umgebung . Wenn dies der Fall ist, lohnt es sich weniger, dieses Thema zu diskutieren.

Apropos "Leistungsgründe": Solange es keine messbaren Leistungsprobleme gibt, ist dies durchaus abergläubisch, um mit "potenziellen Leistungsproblemen" zu argumentieren. Ob eine Datenbank eine Million Konvertierungen von Zeichenfolgen bis zum aktuellen Datum ausführt oder nicht, spielt wahrscheinlich keine Rolle, wenn der Zeitunterschied nur 1/1000 Sekunde beträgt und der eigentliche Engpass das Netzwerk ist, das die Abfrage für 10 Sekunden veranlasst. Also lassen Sie diese Bedenken lieber beiseite, solange jemand explizit nach Leistungsaspekten fragt.

Soll ich diesen Kreuzzug fortsetzen?

Ich verrate dir ein Geheimnis: Ich hasse Religionskriege. Sie führen zu nichts Nützlichem. Wenn also ambitionierte Datums- / Zeitangaben in SQL zu Problemen führen könnten, erwähnen Sie sie, aber versuchen Sie nicht, die Leute zu mehr Starrheit zu zwingen, wenn dies in ihrem aktuellen Kontext keine wirklichen Vorteile bringt.


Dies ist jedoch nicht so sehr eine Frage der Mehrdeutigkeit von amerikanischen oder vernünftigen Datumsformaten. Es geht darum, ob es sinnvoll ist, Daten in einer SQL-Anweisung als Zeichenfolge zu übergeben und sich auf die implizite Konvertierung in Datum zu verlassen. Die Frage, ob die Datenbank für alle Millionen Zeilen eine Million Date-> Str-Konvertierungen durchführen muss, ist ein Leistungsaspekt. Für eine Abfrage wird möglicherweise nur ein Tausendstel einer Sekunde benötigt. Stellen Sie sich dies jetzt im Kontext von alsoands of Concurrent vor Benutzer. Das größere Leistungsproblem besteht darin, dass durch das Konvertieren von Daten Indizes nicht mehr verwendet werden können und dass dies sehr schwerwiegend sein kann
Caius Jard,

@CaiusJard: meine antwort lautet: es ist manchmal sinnvoll und manchmal nicht, es hängt vom kontext ab. Und ehrlich gesagt lehne ich es ab, mir hier irgendetwas vorzustellen . Wenn es um Leistung geht, ist es nicht sinnvoll, einen hypothetischen Fall zu diskutieren. Wenn es messbare Leistungsprobleme gibt, ist es an der Zeit, nicht im Voraus zu optimieren und manchmal auch zu optimieren.
Doc Brown

Es ist interessant, dass Sie es als hypothetisch ansehen. Ich sehe das Verlassen auf implizites Verhalten als eindeutige Möglichkeit für das Auftreten von Fehlern und Leistungskomplikationen (aus gut dokumentierten Gründen: Indizes funktionieren nicht, wenn die gesamten Spaltendaten vor dem Durchsuchen transformiert werden), und mit expliziten Anweisungen kann dies nicht passieren
Caius Jard

@CaiusJard: Spiel nicht mit Worten - mit "hypothetisch" meine ich nicht "unwahrscheinlich", ich habe den Begriff für jede Art von imaginiertem Szenario verwendet, im Gegensatz zu "real existierender Situation", in der man messen kann, was passiert.
Doc Brown

1
@CaiusJard: Wenn Sie andere Branchenfachleute beeindrucken möchten, sollten Sie genau wissen, warum "Leistungsoptimierung" sich stark von "Sicherheitsoptimierung" unterscheidet, und genau darum geht es hier - Leistungsprobleme können nur selten behandelt werden, nachdem sie aufgetreten sind zu spät. Sicherheitsprobleme nicht, sollten sie gründlich vermieden werden, bevor sie auftreten. Vergleichen Sie also bitte keine Äpfel mit Orangen. Wenn Sie Kreuzzüge mögen, sind Sicherheitsargumente dafür viel besser geeignet ;-)
Doc Brown

5

Ihr Kreuzzug löst das Problem nicht.

Es gibt zwei verschiedene Probleme:

  • implizite Typkonvertierung in SQL

  • mehrdeutige Datumsformate wie 05/06/07

Ich sehe, woher Sie mit Ihrem Kreuzzug kommen, aber ich glaube nicht, dass die explizite Konvertierung das vorliegende Problem tatsächlich löst:

  • Eine implizite Konvertierung tritt immer noch auf, wenn die Typen in einem Vergleich nicht übereinstimmen. Wenn eine Zeichenfolge mit einem Datum verglichen wird, versucht SQL zunächst, die Zeichenfolge in ein Datum zu konvertieren. Der Vergleich einer Spalte vom Typ Datum mit einem explizit konvertierten Datumswert entspricht also genau dem Vergleich mit einem Datum im Zeichenfolgenformat. Der einzige Unterschied, den ich sehe, besteht darin, dass Sie einen Datumswert mit einer Spalte vergleichen, die eigentlich keine Daten, sondern Zeichenfolgen enthält. Dies wäre jedoch in jedem Fall ein Fehler.

  • Die Verwendung der expliziten Konvertierung löst die Mehrdeutigkeit in Nicht-ISO-Datumsformaten nicht.

Die einzige Lösung, die ich sehe:

  • Vergleichen Sie Spalten vom Typ "Zeichenfolge" nicht mit Werten, die keine Zeichenfolgen sind.
  • Verwenden Sie nur Datumsformate vom Typ ISO.

Und natürlich sollten Sie niemals Daten in einer Spalte vom Typ String speichern. Die explizite Konvertierung von Datumsliteralen wird dies jedoch nicht verhindern.

Implizite Konvertierungen waren wohl ein Fehler in SQL, aber angesichts der Gestaltung der Sprache sehe ich den Vorteil der expliziten Konvertierung nicht. Eine implizite Konvertierung wird nicht vermieden, und der Code ist nur schwieriger zu lesen und zu schreiben.


Wahr. Vielleicht sollte ich aus dieser Perspektive darauf hinweisen, dass es am sinnvollsten ist, sicherzustellen, dass der Datenspaltenoperand und der Wertoperand denselben Datentyp haben (sei es Zeichenfolge, Datum, was auch immer). Ich gebe diese Empfehlung speziell nur bei Fragen ab, bei denen ich weiß, dass die Tabellenspalte DATETIME ist und deren Beispielantwort die Verwendung eines String-Operanden mit impliziter Konvertierung ist.
Caius Jard

Bei dieser Antwort stimmt etwas nicht. Sie machen einige interessante Punkte, aber ich halte die Schlussfolgerung für idealistisch. Aus gestalterischer Sicht sind Nicht-ISO-Datumsformate für das menschliche Auge nicht eindeutig, aber wenn eine explizite Konvertierung verwendet wird, ist sie für den Parser syntaktisch nicht mehrdeutig. Ebenso viele ETL - Prozesse Daten beteiligt werden erfordern einig Vergleich (in Form einer Datei importieren) eine Zeichenfolge das Datumsformat der Datenbank. Der Versuch, Vergleiche zwischen Zeichenfolgen und Datum zu beseitigen, erscheint mir unrealistisch.
DanK

@DanK: ETL ist ein anderes Problem - wenn Sie Daten aus einer CSV-Datei oder etwas anderem lesen, müssen Sie die Daten offensichtlich als Zeichenfolgen verarbeiten und explizit in typisierte Werte zerlegen. Dies ist jedoch nicht das Szenario, das das OP beschreibt.
JacquesB

Es könnte jedoch leicht der Punkt sein, den ich beschreibe; Es gibt nichts Besonderes an einer Zeichenfolge von Zahlen, die in einer CSV gespeichert ist und die explizite Deklaration des Formats beim Parsen erfordert, und sie wird für das von mir vorgebrachte Argument relevant, wenn ein Neuling eine Antwort in SO liest, für die der Profi keine expliziten Anstrengungen unternimmt Deklarieren Sie das Datumsformat, sodass Neulinge davon ausgehen, dass sie sich keine Sorgen machen müssen (oder dass die Datenbank es die ganze Zeit korrekt parsen wird)
Caius Jard

@CaiusJard: Ich glaube, das sind sehr unterschiedliche Szenarien. Wenn ich in normalen Szenarien über SQL spreche, gehe ich davon aus, dass Spalten die entsprechenden Typen haben - dh Ganzzahlspalten sind Ganzzahlspalten, Datumsspalten sind Datentypen und so weiter. Wenn Sie nicht die richtigen Typen in den Tabellen haben (dh Daten als Zeichenfolgen speichern), haben Sie große Probleme, und die explizite Konvertierung von Datumsliteralen in Abfragen wird Sie nicht retten .
JacquesB

3

In erster Linie haben Sie einen Punkt. Daten sollten nicht in Strings geschrieben werden. Datenbank-Engines sind komplexe Monster, bei denen Sie nie zu 100% sicher sind, was genau unter der Haube bei einer willkürlichen Abfrage passieren wird. Durch die Konvertierung in Datumsangaben werden die Dinge eindeutig und die Leistung kann gesteigert werden.

ABER

Für die meisten Menschen ist es kein Problem, das es wert ist, überlegt zu werden. Wenn es einfach wäre, Datumsliterale in einer Abfrage zu verwenden, wäre es einfach, Ihre Position zu verteidigen. Ist es aber nicht. Ich verwende meistens SQL Server, deshalb passiert es nicht, dass sich das Durcheinander beim Konvertieren eines Datums merken muss.

Für die meisten Menschen ist der Leistungszuwachs vernachlässigbar. "Warum ja, Herr Boss, ich habe zusätzliche 10 Minuten damit verbracht, diesen einfachen Fehler zu beheben (ich musste googeln, wie Daten konvertiert werden, weil diese Syntax ... speziell ... ist). Aber ich habe zusätzliche 0,00001 Sekunden gespart eine selten ausgeführte Abfrage. " Das wird an den meisten Orten, an denen ich gearbeitet habe, nicht funktionieren.

Aber es beseitigt Unklarheiten in Datumsformaten, die Sie sagen. Auch hier ist es für viele Anwendungen (firmeninterne Anwendungen, lokale Behörden usw. usw.) kein wirkliches Problem. Und für diejenigen Anwendungen, bei denen es ein Problem ist (große, internationale oder Unternehmensanwendungen), wird dies entweder zu einem UI- / Business-Layer-Problem, oder diese Unternehmen verfügen bereits über ein Team von erfahrenen Datenbankadministratoren, die dies bereits wissen. TL / DR: Wenn Internationalisierung ein Problem ist, denkt jemand bereits darüber nach und hat bereits getan, was Sie vorschlagen (oder hat das Problem auf andere Weise gemildert).

So was nun?

Wenn Sie sich so geneigt fühlen, kämpfen Sie weiter gegen die guten Kämpfe. Aber wundern Sie sich nicht, wenn die meisten Menschen der Meinung sind, dass dies nicht wichtig genug ist, um sich Sorgen zu machen. Nur weil es Situationen gibt, in denen es darauf ankommt, heißt das nicht, dass dies die Situation aller ist (und wahrscheinlich auch nicht). Seien Sie also nicht überrascht, wenn Sie etwas zurückfordern, das technisch korrekt und besser, aber nicht wirklich relevant ist.


1

Ich behaupte, es ist ratsam, mit diesem Medium explizit umzugehen, das uns zwingt, eine Vielzahl verschiedener Datentypen als Zeichenfolgen zu übergeben.

Angenommen , "Datteln" werden in " Strings " herumgereicht, dann ja; Ich stimme absolut zu, dass Sie Recht haben, dies zu tun.

Wann ist "01/04/07"?
* 4. Januar?
* 1. April?
* 7. April [2001]?

Einige oder alle davon sind möglicherweise korrekt, je nachdem, wie "der Computer" sie interpretiert.

Wenn Sie haben dynamische SQL mit Literalen in ihnen zu bauen, dann Formatierung Datum werden muss , gut definierte und vorzugsweise maschinenunabhängige (ich hatte einen sonderbaren auf einem Windows - Server , auf das Datum basierte Verarbeitung in einem Windows - Dienst ging schief weil sich ein Bediener mit unterschiedlichen Datumsformatvorgaben an der Konsole angemeldet hat!). Ich persönlich verwende [d] ausschließlich das Format "JJJJ-MM-TT".

Jedoch ...

Die beste Lösung ist die Verwendung von parametrisierten Abfragen, bei denen der Datentyp zuvor konvertiert werden muss SQL eingebunden wird. Wenn ein Datumswert in einen Datumsparameter eingefügt wird, wird die Typkonvertierung frühzeitig erzwungen (was die Konvertierung zu einem reinen Codierungsproblem und nicht zu einem SQL-Problem macht). .


Ich stimme zu, obwohl das gleiche Problem mit parametrisierten Abfragen erzwungen werden kann, indem Sie WHERE datecolumn = @dateParameterund dann im Front-End-Code dem DB-Treiber @dateParametermitteilen, dass er vom Typ varchar ist, und sich "01/04/07"daran halten. Die ursprüngliche Inspiration für meine Frage ist, dass ich vermute, dass jeder, der mir sagen würde, dass ich verrückt danach bin, eine parametrisierte Abfrage zu machen, dann im gleichen Atemzug eine Zeile mit einer SO-Antwort geben würde, die aussieht WHERE datecol = 'some string that looks like a date'(und von einem Neuling erwartet, dass er es weiß Es ist nur ein Hinweis / parametrisieren Sie es, um Probleme zu vermeiden)
Caius Jard
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.