sql >> Database teknologi >  >> RDS >> PostgreSQL

Partitionsbeskæring baseret på kontrolbegrænsning fungerer ikke som forventet

Din kolonne created_at er timestamp without time zone .

Men now() returnerer timestamp with time zone . Udtrykket now() - '1 hour'::interval bliver tvunget til at timestamp [without time zone] , som rummer to problemer :

1.) Du bad ikke om denne, men udtrykket er upålideligt. Resultatet afhænger af den aktuelle tidszoneindstilling for den session, som forespørgslen udføres i. Detaljer her:

For at gøre udtrykket tydeligt kan du bruge:

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

Eller bare (læs manualen her) :

LOCALTIMESTAMP  -- explicitly take the local time

Jeg ville overveje at arbejde med timestamptz i stedet.
Ingen af ​​delene løser dit andet problem:

2.) Svar på dit spørgsmål. Udelukkelse af begrænsninger virker ikke. Pr. dokumentation:

Fed fremhævelse mine.

now() er Postgres-implementeringen af ​​CURRENT_TIMESTAMP . Som du kan se i systemkataloget, er det kun STABLE , ikke IMMUTABLE :

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

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

Løsninger

1.) Du kan overvinde begrænsningen ved at angive en konstant i WHERE betingelse (som altid er "uforanderlig"):

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

2.) Eller ved at "falske" en uforanderlig funktion:

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

Og så:

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

Vær dog forsigtig med, hvordan du bruger dette:while now() er STABLE (ændres ikke i en transaktions varighed), gør det det skift mellem transaktioner, så pas på ikke at bruge dette i forberedte udsagn (undtagen som parameterværdi) eller indekser eller andet, hvor det kan bide dig.

3.) Eller du kan tilføje en tilsyneladende redundant konstant WHERE klausuler til din aktuelle forespørgsel, der matcher begrænsningen på din partition:

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;

Bare sørg for, at now() - '1 hour'::interval falder ind i den rigtige partition, eller du får ingen resultater, naturligvis.

Til side:Jeg vil hellere bruge dette udtryk i CHECK begrænsninger og forespørgsel. Lettere at håndtere og gør det samme:

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



  1. Sådan tilføjes en primær nøgle til en eksisterende tabel i SQL Server (T-SQL-eksempler)

  2. WHERE-klausul i OPEN QUERY

  3. Kan et tabelfelt indeholde en bindestreg?

  4. Sådan adskilles tekst ved hjælp af understreng