PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

L'élagage de partition basé sur la contrainte de vérification ne fonctionne pas comme prévu

Votre colonne created_at est timestamp without time zone .

Mais now() renvoie timestamp with time zone . L'expression now() - '1 hour'::interval est contraint à timestamp [without time zone] , qui pose deux problèmes :

1.) Vous n'avez pas demandé celui-ci, mais l'expression n'est pas fiable. Son résultat dépend du paramètre de fuseau horaire actuel de la session dans laquelle la requête est exécutée. Détails ici :

Pour rendre l'expression claire, vous pouvez utiliser :

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

Ou simplement (lire le manuel ici) :

LOCALTIMESTAMP  -- explicitly take the local time

J'envisagerais de travailler avec timestamptz à la place.
Aucun ne résout votre deuxième problème :

2.) Répondez à votre question. L'exclusion de contraintes ne fonctionne pas. Par documentation :

J'insiste sur moi.

now() est l'implémentation Postgres de CURRENT_TIMESTAMP . Comme vous pouvez le voir dans le catalogue système, il n'est que STABLE , pas IMMUTABLE :

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

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

Solutions

1.) Vous pouvez contourner la limitation en fournissant une constante dans le WHERE condition (qui est toujours "immuable") :

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

2.) Soit en "simulant" une fonction immuable :

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

Et ensuite :

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

Faites attention à la façon dont vous l'utilisez cependant :while now() est STABLE (ne change pas pendant la durée d'une transaction), il fait changer entre les transactions, veillez donc à ne pas l'utiliser dans des instructions préparées (sauf en tant que valeur de paramètre) ou des index ou quoi que ce soit où cela pourrait vous mordre.

3.) Ou vous pouvez ajouter une constante apparemment redondante WHERE clauses à votre requête actuelle qui correspondent à la contrainte sur votre 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;

Assurez-vous simplement que now() - '1 hour'::interval tombe dans la bonne partition ou vous n'obtenez aucun résultat, évidemment.

A part :je préférerais utiliser cette expression dans CHECK contraintes et requête. Plus facile à manipuler et fait la même chose :

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