Comme votre modification a été clarifiée, vous avez installé l'extension btree_gist
. Sans cela, l'exemple échouerait déjà à name WITH =
.
CREATE EXTENSION btree_gist;
Les classes d'opérateurs installées par btree_gist
couvrent de nombreux opérateurs. Malheureusement, le &
l'opérateur n'en fait pas partie. Évidemment parce qu'il ne renvoie pas de boolean
ce qui serait attendu d'un opérateur pour se qualifier.
Solution alternative
J'utiliserais une combinaison d'un index multi-colonnes b-tree (pour la vitesse) et un déclencheur Au lieu. Considérez cette démo, testée sur PostgreSQL 9.1 :
CREATE TABLE t (
name text
,value bit(8)
);
INSERT INTO t VALUES ('a', B'10101010');
CREATE INDEX t_name_value_idx ON t (name, value);
CREATE OR REPLACE FUNCTION trg_t_name_value_inversion_prohibited()
RETURNS trigger AS
$func$
BEGIN
IF EXISTS (
SELECT 1 FROM t
WHERE (name, value) = (NEW.name, ~ NEW.value) -- example: exclude inversion
) THEN
RAISE EXCEPTION 'Your text here!';
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER insup_bef_t_name_value_inversion_prohibited
BEFORE INSERT OR UPDATE OF name, value -- only involved columns relevant!
ON t
FOR EACH ROW
EXECUTE PROCEDURE trg_t_name_value_inversion_prohibited();
INSERT INTO t VALUES ('a', ~ B'10101010'); -- fails with your error msg.
-
L'extension
btree_gist
n'est pas requis dans ce scénario. -
J'ai limité le déclencheur à INSERT ou UPDATE des colonnes pertinentes pour plus d'efficacité.
-
Une contrainte de vérification ne fonctionnerait pas. Je cite le manuel sur
CREATE TABLE
:J'insiste en gras :
Devrait très bien fonctionner, en fait mieux que la contrainte d'exclusion, car la maintenance d'un index b-tree est moins chère qu'un index GiST. Et la recherche avec de base =
les opérateurs devraient être plus rapides que les recherches hypothétiques avec le &
opérateur.
Cette solution n'est pas aussi sûre qu'une contrainte d'exclusion, car les déclencheurs peuvent plus facilement être contournés - dans un déclencheur ultérieur sur le même événement par exemple, ou si le déclencheur est temporairement désactivé. Soyez prêt à exécuter des vérifications supplémentaires sur l'ensemble de la table si de telles conditions s'appliquent.
Condition plus complexe
L'exemple de déclencheur n'attrape que l'inversion de value
. Comme vous l'avez précisé dans votre commentaire, vous avez en fait besoin d'une condition comme celle-ci :
IF EXISTS (
SELECT 1 FROM t
WHERE name = NEW.name
AND value & NEW.value <> B'00000000'::bit(8)
) THEN
Cette condition est légèrement plus chère, mais peut toujours utiliser un index. L'index multi-colonnes ci-dessus fonctionnerait - si vous en avez besoin de toute façon. Ou, légèrement plus efficace, un simple index sur le nom :
CREATE INDEX t_name_idx ON t (name);
Comme vous l'avez commenté, il ne peut y avoir qu'un maximum de 8 lignes distinctes par name
, moins en pratique. Cela devrait donc être rapide.
Performances INSERT ultimes
Si INSERT
les performances sont primordiales, en particulier si de nombreuses tentatives d'INSERT échouent à la condition, vous pouvez faire plus :créer une vue matérialisée qui pré-agrège la value
par name
:
CREATE TABLE mv_t AS
SELECT name, bit_or(value) AS value
FROM t
GROUP BY 1
ORDER BY 1;
name
est assuré d'être unique ici. J'utiliserais une PRIMARY KEY
sur name
pour fournir l'index que nous recherchons :
ALTER TABLE mv_t SET (fillfactor=90);
ALTER TABLE mv_t
ADD CONSTRAINT mv_t_pkey PRIMARY KEY(name) WITH (fillfactor=90);
Ensuite, votre INSERT
pourrait ressembler à ceci :
WITH i(n,v) AS (SELECT 'a'::text, B'10101010'::bit(8))
INSERT INTO t (name, value)
SELECT n, v
FROM i
LEFT JOIN mv_t m ON m.name = i.n
AND m.value & i.v <> B'00000000'::bit(8)
WHERE m.n IS NULL; -- alternative syntax for EXISTS (...)
Le fillfactor
n'est utile que si votre table reçoit beaucoup de mises à jour.
Mettre à jour les lignes de la vue matérialisée dans un TRIGGER AFTER INSERT OR UPDATE OF name, value OR DELETE
pour le tenir à jour. Le coût des objets supplémentaires doit être mis en balance avec le gain. Dépend en grande partie de votre charge typique.