Zoals je bewerking verduidelijkt, heb je de extensie btree_gist geïnstalleerd code>
. Zonder dit zou het voorbeeld al mislukken op name WITH =
.
CREATE EXTENSION btree_gist;
De operatorklassen geïnstalleerd door btree_gist
bestrijken veel operators. Helaas, de &
exploitant is er niet bij. Uiteraard omdat het geen boolean
retourneert die van een operator zou worden verwacht om zich te kwalificeren.
Alternatieve oplossing
Ik zou een combinatie van een b-tree multi-column index . gebruiken (voor snelheid) en een trigger in plaats van. Overweeg deze demo, getest op 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.
-
De extensie
btree_gist
is niet vereist in dit scenario. -
Ik heb de trigger beperkt tot INSERT of UPDATE van relevante kolommen voor efficiëntie.
-
Een controlebeperking zou niet werken. Ik citeer de handleiding op
CREATE TABLE
:Vetgedrukte nadruk van mij:
Zou heel goed moeten presteren, eigenlijk beter dan de uitsluitingsbeperking, omdat het onderhoud van een b-tree-index goedkoper is dan een GiST-index. En de look-up met basic =
operators moeten sneller zijn dan hypothetische opzoekingen met de &
telefoniste.
Deze oplossing is niet zo veilig als een uitsluitingsbeperking, omdat triggers gemakkelijker kunnen worden omzeild, bijvoorbeeld bij een volgende trigger op dezelfde gebeurtenis of als de trigger tijdelijk wordt uitgeschakeld. Wees bereid om extra controles uit te voeren op de hele tafel als dergelijke voorwaarden van toepassing zijn.
Meer complexe toestand
De voorbeeldtrigger vangt alleen de inversie van waarde
. Zoals je in je opmerking hebt verduidelijkt, heb je in plaats daarvan een voorwaarde als deze nodig:
IF EXISTS (
SELECT 1 FROM t
WHERE name = NEW.name
AND value & NEW.value <> B'00000000'::bit(8)
) THEN
Deze voorwaarde is iets duurder, maar kan nog steeds een index gebruiken. De index met meerdere kolommen van bovenaf zou werken - als je het toch nodig hebt. Of, iets efficiënter, een eenvoudige index op naam:
CREATE INDEX t_name_idx ON t (name);
Zoals je hebt opgemerkt, kunnen er maximaal 8 verschillende rijen zijn per naam
, in de praktijk minder. Dit zou dus nog steeds snel moeten zijn.
Ultieme INSERT-prestaties
Als INSERT
prestaties zijn van het grootste belang, vooral als veel pogingen tot INSERT's niet aan de voorwaarde voldoen, kunt u meer doen:een gematerialiseerde weergave maken die vooraf de waarde
samenvoegt per naam
:
CREATE TABLE mv_t AS
SELECT name, bit_or(value) AS value
FROM t
GROUP BY 1
ORDER BY 1;
naam
is hier gegarandeerd uniek. Ik zou een PRIMAIRE SLEUTEL
gebruiken op naam
om de index te leveren die we zoeken:
ALTER TABLE mv_t SET (fillfactor=90);
ALTER TABLE mv_t
ADD CONSTRAINT mv_t_pkey PRIMARY KEY(name) WITH (fillfactor=90);
Dan uw INSERT
zou er zo uit kunnen zien:
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 (...)
De fillfactor
is alleen handig als je tafel veel updates krijgt.
Werk rijen in de gerealiseerde weergave bij in een TRIGGER NA INSERT OF UPDATE OF name, value OR DELETE
om het actueel te houden. De kosten van de extra objecten moeten worden afgewogen tegen de winst. Hangt grotendeels af van uw typische belasting.