PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Partitionsbereinigung basierend auf Check Constraint funktioniert nicht wie erwartet

Ihre Spalte created_at ist timestamp without time zone .

Aber now() gibt timestamp with time zone zurück . Der Ausdruck now() - '1 hour'::interval wird auf timestamp [without time zone] gezwungen , die zwei Probleme mit sich bringt :

1.) Sie haben nicht danach gefragt, aber der Ausdruck ist unzuverlässig. Das Ergebnis hängt von der aktuellen Zeitzoneneinstellung der Sitzung ab, in der die Abfrage ausgeführt wird. Details hier:

Um den Ausdruck klarer zu machen, könnten Sie verwenden:

now() AT TIME ZONE 'Europe/London' -- your time zone here

Oder einfach (hier das Handbuch lesen) :

LOCALTIMESTAMP  -- explicitly take the local time

Ich würde in Betracht ziehen, mit timestamptz zu arbeiten stattdessen.
Keines von beiden löst Ihr zweites Problem:

2.) Antwort auf Ihre Frage. Der Einschränkungsausschluss funktioniert nicht. Pro Dokumentation:

Fettdruck von mir.

now() ist die Postgres-Implementierung von CURRENT_TIMESTAMP . Wie Sie im Systemkatalog sehen können, ist es nur STABLE , nicht IMMUTABLE :

SELECT proname, provolatile FROM pg_proc WHERE proname = 'now';

proname | provolatile
--------+------------
now     | s              -- meaning: STABLE

Lösungen

1.) Sie können die Einschränkung überwinden, indem Sie eine Konstante im WHERE angeben Bedingung (die immer "unveränderlich" ist):

select count(*) from events
where created_at > '2015-05-25 15:49:20.037815'::timestamp;  -- derived from your example

2.) Oder durch "Fake" einer unveränderlichen Funktion:

CREATE FUNCTION f_now_immutable()
  RETURNS timestamp AS
$func$
SELECT now() AT TIME ZONE 'UTC'  -- your time zone here
$func$  LANGUAGE sql IMMUTABLE;

Und dann:

select count(*) from events
where created_at > f_now_immutable() - interval '1 hour'

Seien Sie jedoch vorsichtig, wie Sie dies verwenden:while now() ist STABLE (ändert sich für die Dauer einer Transaktion nicht), es ändert zwischen Transaktionen ändern, achten Sie also darauf, dies nicht in vorbereiteten Anweisungen (außer als Parameterwert) oder Indizes oder irgendetwas zu verwenden, wo es Sie beißen könnte.

3.) Oder Sie können die scheinbar überflüssige Konstante WHERE hinzufügen Klauseln zu Ihrer aktuellen Abfrage, die der Einschränkung Ihrer Partition entsprechen:

SELECT count(*)
FROM   events
WHERE  created_at > now() - '1 hour'::interval
AND    created_at >= '2015-04-01 00:00:00'::timestamp
AND    created_at <= '2015-04-30 23:59:59.999999'::timestamp;

Stellen Sie einfach selbst sicher, dass now() - '1 hour'::interval fällt in die richtige Partition oder Sie erhalten offensichtlich keine Ergebnisse.

Übrigens:Ich würde diesen Ausdruck lieber in CHECK verwenden Einschränkungen und Abfrage. Einfacher zu handhaben und tut dasselbe:

       created_at >= '2015-04-01 0:0'::timestamp
AND    created_at <  '2015-05-01 0:0'::timestamp