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 @pBirthdate
es 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. "
quelle
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 oktalWHERE age = '0x0F'
eine Datenbank nach 15-Jährigen sucht.Antworten:
Sie schrieben:
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.
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.
quelle
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:
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.
quelle
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.
quelle
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). .
quelle
WHERE datecolumn = @dateParameter
und dann im Front-End-Code dem DB-Treiber@dateParameter
mitteilen, 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 aussiehtWHERE 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)