PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Ograniczenie wykluczenia kolumny ciągu bitowego z bitowym operatorem AND

Jak wyjaśniono w Twojej edycji, zainstalowałeś rozszerzenie btree_gist . Bez tego przykład nie powiódłby się już w name WITH = .

CREATE EXTENSION btree_gist;

Klasy operatorów instalowane przez btree_gist obejmują wielu operatorów. Niestety, & operator nie jest wśród nich. Oczywiście, ponieważ nie zwraca boolean czego oczekuje się od operatora, aby się zakwalifikować.

Alternatywne rozwiązanie

Użyłbym kombinacji b-drzewa indeksu wielokolumnowego (dla szybkości) i wyzwalacz zamiast. Rozważ to demo, przetestowane 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.

Powinien działać bardzo dobrze, właściwie lepiej niż ograniczenie wykluczenia, ponieważ utrzymanie indeksu b-drzewa jest tańsze niż indeksu GiST. I wyszukiwanie z podstawowym = Operatory powinny być szybsze niż hipotetyczne wyszukiwania za pomocą & operatora.

To rozwiązanie nie jest tak bezpieczne jak ograniczenie wykluczenia, ponieważ wyzwalacze można łatwiej obejść - na przykład w kolejnym wyzwalaczu tego samego zdarzenia lub jeśli wyzwalacz jest tymczasowo wyłączony. Przygotuj się na przeprowadzenie dodatkowych kontroli całego stołu, jeśli takie warunki mają zastosowanie.

Bardziej złożony warunek

Przykładowy wyzwalacz przechwytuje tylko inwersję wartości . Jak wyjaśniłeś w swoim komentarzu, w rzeczywistości potrzebujesz takiego warunku:

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

Ten warunek jest nieco droższy, ale nadal można używać indeksu. Powyższy indeks wielokolumnowy zadziała - jeśli i tak będziesz go potrzebował. Lub, nieco bardziej wydajny, prosty indeks nazwy:

CREATE INDEX t_name_idx ON t (name);

Jak skomentowałeś, może być maksymalnie 8 odrębnych wierszy na nazwa , mniej w praktyce. Więc to powinno być szybkie.

Najwyższa wydajność INSERT

Jeśli WSTAW wydajność jest najważniejsza, zwłaszcza jeśli wiele prób INSERT nie spełnia warunku, możesz zrobić więcej:utworzyć zmaterializowany widok, który wstępnie zagregował wartość na nazwę :

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

nazwa gwarantuje, że będzie tutaj wyjątkowy. Użyję KLUCZ PODSTAWOWY na nazwa aby zapewnić indeks, którego szukamy:

ALTER TABLE mv_t SET (fillfactor=90);

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

Następnie Twój INSERT może wyglądać tak:

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

fillfactor przydaje się tylko wtedy, gdy Twoja tabela otrzymuje wiele aktualizacji.

Zaktualizuj wiersze w widoku zmaterializowanym w WYZWOLENIU PO WSTAWIENIU LUB AKTUALIZACJI nazwy, wartości LUB USUNIĘCIA aby była aktualna. Koszt dodatkowych obiektów należy porównać z zyskiem. W dużej mierze zależy od typowego obciążenia.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak policzyć wszystkie połączone węzły (wiersze) na wykresie w Postgresie?

  2. Zapętlaj wymiar tablicy w plpgsql

  3. Czy możliwe jest zliczanie według różnych warunków w jednym zapytaniu?

  4. Nie można określić typu polimorficznego, ponieważ dane wejściowe mają nieznany typ

  5. Usunięcie indeksu za pomocą psycopg2 działa przed lub po zatwierdzeniu?