PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Ограничение за изключване на колона от битов низ с побитов оператор И

Както изясни редакцията ви, вие сте инсталирали разширението btree_gist . Без него примерът вече би се провалил при name WITH = .

CREATE EXTENSION btree_gist;

Класовете оператори, инсталирани от btree_gist обхваща много оператори. За съжаление, & оператор не е сред тях. Очевидно, защото не връща булева стойност което би се очаквало от оператора да отговаря на изискванията.

Алтернативно решение

Бих използвал комбинация от b-дърво многоколонен индекс (за скорост) и спусък вместо. Помислете за тази демонстрация, тествана на 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.

Трябва да работи много добре, всъщност по-добре от ограничението за изключване, тъй като поддръжката на индекс на b-дърво е по-евтина от GiST индекс. И търсенето с основен = операторите трябва да са по-бързи от хипотетичните търсения с & оператор.

Това решение не е толкова безопасно, колкото ограничението за изключване, тъй като задействанията могат по-лесно да бъдат заобиколени - например при следващо задействане при същото събитие или ако задействането е временно деактивирано. Бъдете готови да извършите допълнителни проверки на цялата таблица, ако са приложими такива условия.

По-сложно условие

Примерният тригер улавя само инверсията на value . Както изяснихте в коментара си, вместо това всъщност се нуждаете от условие като това:

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

Това условие е малко по-скъпо, но все още може да използва индекс. Индексът с много колони от по-горе ще работи - ако все пак имате нужда от него. Или, малко по-ефективно, прост индекс на име:

CREATE INDEX t_name_idx ON t (name);

Както коментирахте, може да има максимум 8 отделни реда на име , по-малко на практика. Така че това все още трябва да е бързо.

Върховна производителност на INSERT

Ако INSERT производителността е от първостепенно значение, особено ако много опити за INSERT не изпълнят условието, бихте могли да направите повече:създайте материализиран изглед, който предварително агрегира стойност на име :

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

име е гарантирано уникален тук. Бих използвал PRIMARY KEY на име за да предоставим индекса, който търсим:

ALTER TABLE mv_t SET (fillfactor=90);

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

След това вашият INSERT може да изглежда така:

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 е полезно само ако вашата таблица получава много актуализации.

Актуализирайте редовете в материализирания изглед в ТРИМЕР СЛЕД ВМЪКВАНЕ ИЛИ АКТУАЛИЗИРАНЕ НА име, стойност ИЛИ ИЗТРИВАНЕ за да го поддържате актуален. Цената на допълнителните обекти трябва да се претегли спрямо печалбата. До голяма степен зависи от вашето типично натоварване.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да конвертирате времето в дата в местната часова зона по време на заявка

  2. org.postgresql.util.PSQLException:Опитът за свързване е неуспешен

  3. Postgresql - как да получите записи в таблица, които нямат съвпадение в друга таблица

  4. докосва неуспешна миграция от sqlite към postgres rails4, ruby ​​1.9.3

  5. Грешка при използване на R за получаване на идентификационни данни от Windows Cred Vault