sql >> Database >  >> RDS >> PostgreSQL

Uitsluitingsbeperking op een bitstringkolom met bitsgewijze AND-operator

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.

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.




  1. Zeer trage (1 seconde) verbindingen

  2. Node-mysql query invoegen met twee waarden?

  3. MySQL Linker Join + Min

  4. PGAdmin toont buitensporige hoeveelheid database van Heroku