sql >> Databáze >  >> RDS >> PostgreSQL

Omezení vyloučení na sloupci bitstring s bitovým operátorem AND

Jak vaše úprava objasnila, nainstalovali jste rozšíření btree_gist . Bez něj by příklad již selhal na name WITH = .

CREATE EXTENSION btree_gist;

Třídy operátorů nainstalované btree_gist pokrývat mnoho operátorů. Bohužel & operátor mezi nimi není. Je zřejmé, že nevrací boolean což se očekává od operátora, aby se kvalifikoval.

Alternativní řešení

Použil bych kombinaci vícesloupcového indexub-stromu (pro rychlost) a spouštěč namísto. Zvažte toto demo, testované na 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.

Mělo by fungovat velmi dobře, ve skutečnosti lépe než omezení vyloučení, protože údržba indexu b-stromu je levnější než index GiST. A vyhledávání pomocí základního = operátory by měly být rychlejší než hypotetické vyhledávání pomocí & operátor.

Toto řešení není tak bezpečné jako omezení vyloučení, protože spouštěče lze snadněji obejít – například při následné spouštěcí události stejné události nebo pokud je spouštěč dočasně zakázán. Buďte připraveni spustit dodatečné kontroly celého stolu, pokud takové podmínky platí.

Složitější podmínka

Příklad spouštěče zachytí pouze inverzi value . Jak jste ve svém komentáři objasnili, ve skutečnosti potřebujete podmínku jako je tato:

IF EXISTS (
      SELECT 1 FROM t
      WHERE  name = NEW.name
      AND    value & NEW.value <> B'00000000'::bit(8)
      ) THEN

Tato podmínka je o něco dražší, ale stále lze použít index. Fungoval by vícesloupcový index shora - pokud jej přesto potřebujete. Nebo, o něco efektivnější, jednoduchý index na jméno:

CREATE INDEX t_name_idx ON t (name);

Jak jste uvedli, na name může být maximálně 8 různých řádků , v praxi méně. Takže by to mělo být stále rychlé.

Maximální výkon INSERT

Pokud INSERT výkon je prvořadý, zvláště pokud mnoho pokusů o INSERT nesplňuje podmínku, můžete udělat více:vytvořit materializovaný pohled s předem agregovanou hodnotou podle jména :

CREATE TABLE mv_t AS 
SELECT name, bit_or(value) AS value
FROM   t
GROUP  BY 1
ORDER  BY 1;

jméno je zde zaručeně unikátní. Použil bych PRIMARY KEY na jméno abychom poskytli index, o který usilujeme:

ALTER TABLE mv_t SET (fillfactor=90);

ALTER TABLE mv_t
ADD CONSTRAINT mv_t_pkey PRIMARY KEY(name) WITH (fillfactor=90);

Poté váš INSERT může vypadat takto:

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 (...)

faktor plnění je užitečné pouze v případě, že váš stůl dostává mnoho aktualizací.

Aktualizujte řádky v materializovaném zobrazení v SPUŠTĚNÍ PO VLOŽENÍ NEBO AKTUALIZACI názvu, hodnoty NEBO DELETE aby to bylo aktuální. Náklady na dodatečné objekty je třeba porovnat se ziskem. Do značné míry závisí na vaší typické zátěži.




  1. Při pokusu o aktualizaci 640 000 řádků v mySQL dojde během dotazu ke ztrátě připojení k serveru MySQL

  2. Jak přidat podmíněný jedinečný index na PostgreSQL

  3. MySQL:jedinečné pole musí být index?

  4. Příkaz MYSQL IN