Wie vergleiche ich Daten in Datums- / Uhrzeitfeldern in Postgresql?

186

Beim Vergleich der Daten in postgresql (Version 9.2.4 in Windows) war ich mit einem seltsamen Szenario konfrontiert. Ich habe eine Spalte in meiner Tabelle mit der Aufschrift update_date vom Typ 'Zeitstempel ohne Zeitzone'. Der Kunde kann dieses Feld nur mit Datum (dh: 2013-05-03) oder Datum mit Uhrzeit (dh: 2013-05-03 12:20:00) durchsuchen. Diese Spalte hat den Wert als Zeitstempel für alle aktuellen Zeilen und den gleichen Datumsteil (03.05.2013), jedoch den Zeitunterschied.

Wenn ich über diese Spalte vergleiche, erhalte ich unterschiedliche Ergebnisse. Wie die folgenden:

select * from table where update_date >= '2013-05-03' AND update_date <= '2013-05-03' -> No results

select * from table where update_date >= '2013-05-03' AND update_date < '2013-05-03' -> No results

select * from table where update_date >= '2013-05-03' AND update_date <= '2013-05-04' -> results found

select * from table where update_date >= '2013-05-03' -> results found

Meine Frage ist, wie ich die erste Abfrage ermöglichen kann, um Ergebnisse zu erhalten. Ich meine, warum die dritte Abfrage funktioniert, aber nicht die erste?

Kann mir jemand dabei helfen? Danke im Voraus.

user2866264
quelle

Antworten:

274

@Nicolai hat Recht mit dem Casting und warum die Bedingung für Daten falsch ist. Ich denke, Sie bevorzugen die erste Form, weil Sie Datumsmanipulationen an der Eingabezeichenfolge vermeiden möchten, richtig? Sie müssen keine Angst haben:

SELECT *
FROM table
WHERE update_date >= '2013-05-03'::date
AND update_date < ('2013-05-03'::date + '1 day'::interval);
nur jemand
quelle
Ist diese Syntax ( '2013-05-03'::dateund '1 day'::interval) PostgreSQL spezifisch?
Frozen Flame
5
@FrozenFlame ja das ist es. Die Standardsyntax wäre CAST('2013-05-03' AS DATE) + CAST('1 day' AS INTERVAL)(IIRC). YMMV über die Existenz und das Verhalten von DATEund INTERVAL.
Nur jemand
@FrozenFlame ist korrekt, die Antwort funktioniert nicht, ohne die Zeichenfolgen in Datumstypen umzuwandeln. Eine Besetzung fehlt noch. Es muss ein ::DATEerster Teil der where-Klausel hinzugefügt werden
StillLearningToCode
Würde nicht WHERE update_date::date = '2013-05-03' so gut funktionieren und vielleicht etwas besser lesbar sein?
MikeF
@ MikeF OP sagte update_datewar timestamp without timezone. Ich nahm einen Index für diese Spalte an. Ihr Prädikat würde diesen Index nicht verwenden.
Nur jemand
45

Wenn Sie update_date >= '2013-05-03'Postgres vergleichen, werden Werte in denselben Typ umgewandelt, um Werte zu vergleichen. Ihr '2013-05-03' wurde also in '2013-05-03 00:00:00' umgewandelt.

Für update_date = '2013-05-03 14:45:00' lautet Ihr Ausdruck also wie folgt:

'2013-05-03 14:45:00' >= '2013-05-03 00:00:00' AND '2013-05-03 14:45:00' <= '2013-05-03 00:00:00'

Das ist immer so false

Um dieses Problem zu lösen, setzen Sie update_date auf date:

select * from table where update_date::date >= '2013-05-03' AND update_date::date <= '2013-05-03' -> Will return result
Nicolai
quelle
1
Das Umwandeln aller update_dateWerte in der Tabelle im Vergleich zum Umwandeln des einzelnen Werts des Abfrageparameters ist äußerst ineffizient und stellt sicher, dass der Server keine Indizes für diese Spalte nutzen kann. Ich bin versucht, dies zu tun.
Nur jemand
3
Ja, ich bin damit einverstanden, dass das Casting jedes Werts ineffizient ist und Sie für diese Lösung -1 angeben können. Aber ich habe den Grund des Problems beschrieben und ein Beispiel gegeben, das das Problem demonstriert. Jetzt weiß user2866264, warum seine Abfrage keine erwarteten Zeilen zurückgibt, und entscheidet, welche Lösung für seinen Einzelfall genau besser ist.
Nicolai
@Nicolai: Vielen Dank für deine Antwort. Es funktioniert, indem Sie Ihrer Antwort folgen. Danke auch für die Erklärung.
user2866264
1
@Nicolai - Wenn Sie sagen, dass Postgres das Datumsliteral auf Mitternacht erweitert, wäre das Ziel, Datensätze zu finden, die an einem einzigen Datum (3. Mai) markiert sind, korrekt und effizienter: SELECT * FROM my_table WHERE update_date >= '2013-05-03' AND update_date < '2013-05-04'; (Beachten Sie die Verwendung des 4. Mai eher als 3. und mit einem WENIGER ALS ZEICHEN eher als weniger als oder gleich.)
Basil Bourque
1

Verwenden Sie Datumskonvertierung, um mit Datum zu vergleichen: Versuchen Sie Folgendes:

select * from table 
where TO_DATE(to_char(timespanColumn,'YYYY-MM-DD'),'YYYY-MM-DD') = to_timestamp('2018-03-26', 'YYYY-MM-DD')
Yenky Bustamante
quelle