PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Batasan pengecualian pada kolom bitstring dengan operator AND bitwise

Saat pengeditan Anda diklarifikasi, Anda memasang ekstensi btree_gist . Tanpa itu, contoh akan gagal di name WITH = .

CREATE EXTENSION btree_gist;

Kelas operator diinstal oleh btree_gist mencakup banyak operator. Sayangnya, & operator tidak ada di antara mereka. Jelas karena tidak mengembalikan boolean yang diharapkan dari operator untuk memenuhi syarat.

Solusi alternatif

Saya akan menggunakan kombinasi b-tree indeks multi-kolom (untuk kecepatan) dan pemicu alih-alih. Pertimbangkan demo ini, diuji pada 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.

Harus berkinerja sangat baik, sebenarnya lebih baik daripada batasan pengecualian, karena pemeliharaan indeks b-tree lebih murah daripada indeks GiST. Dan pencarian dengan = dasar operator harus lebih cepat daripada pencarian hipotetis dengan & operator.

Solusi ini tidak seaman batasan pengecualian, karena pemicu dapat lebih mudah dielakkan - dalam pemicu berikutnya pada peristiwa yang sama misalnya, atau jika pemicu dinonaktifkan untuk sementara. Bersiaplah untuk menjalankan pemeriksaan ekstra di seluruh tabel jika kondisi tersebut berlaku.

Kondisi lebih kompleks

Pemicu contoh hanya menangkap inversi value . Seperti yang Anda klarifikasi dalam komentar Anda, Anda sebenarnya membutuhkan kondisi seperti ini sebagai gantinya:

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

Kondisi ini sedikit lebih mahal, tetapi masih bisa menggunakan indeks. Indeks multi-kolom dari atas akan berfungsi - jika Anda tetap membutuhkannya. Atau, sedikit lebih efisien, indeks sederhana pada nama:

CREATE INDEX t_name_idx ON t (name);

Saat Anda berkomentar, hanya boleh ada maksimal 8 baris berbeda per name , lebih sedikit dalam praktiknya. Jadi ini masih harus cepat.

Kinerja INSERT terbaik

Jika INSERT kinerja adalah yang terpenting, terutama jika banyak INSERT yang dicoba gagal memenuhi syarat, Anda dapat melakukan lebih banyak:membuat tampilan terwujud yang telah digabungkan value per name :

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

name dijamin unik disini. Saya akan menggunakan PRIMARY KEY di name untuk memberikan indeks yang kita cari:

ALTER TABLE mv_t SET (fillfactor=90);

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

Kemudian INSERT bisa terlihat seperti ini:

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 hanya berguna jika tabel Anda mendapat banyak pembaruan.

Perbarui baris dalam tampilan terwujud dalam TRIGGER AFTER INSERT OR UPDATE OF name, value OR DELETE untuk menjaganya tetap terkini. Biaya objek tambahan harus ditimbang terhadap keuntungan. Sangat tergantung pada beban biasa Anda.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Memasukkan LOOP ke dalam SQL

  2. Bagaimana cara mengambil data dari beberapa tabel terkait di Postgres?

  3. Gabungkan baris postgres dan ganti nilai dengan yang terbaru jika bukan nol

  4. Gabungkan baris JSON

  5. Apa yang dijelaskan PostgreSQL kepada saya dengan tepat?