Tidak ada cara bawaan saat ini.
Sebagai array
Jika Anda secara konsisten menormalkannya saat disimpan, Anda dapat memperlakukan array sebagai set, dengan selalu menyimpannya diurutkan dan dihilangkan duplikatnya. Akan sangat bagus jika PostgreSQL memiliki fungsi C bawaan untuk melakukan ini, tetapi tidak. Saya melihat untuk menulis satu tetapi C array API mengerikan , jadi meskipun saya telah menulis banyak ekstensi, saya mundur dengan hati-hati dari ekstensi ini.
Jika Anda tidak keberatan dengan kinerja yang cukup menjijikkan, Anda dapat melakukannya di SQL:
CREATE OR REPLACE FUNCTION array_uniq_sort(anyarray) RETURNS anyarray AS $$
SELECT array_agg(DISTINCT f ORDER BY f) FROM unnest($1) f;
$$ LANGUAGE sql IMMUTABLE;
lalu bungkus semua simpanan dalam panggilan ke array_uniq_sort
atau memaksakannya dengan pemicu. Anda kemudian dapat membandingkan array Anda untuk kesetaraan. Anda dapat menghindari array_uniq_sort
memanggil data dari aplikasi jika Anda hanya melakukan pengurutan/unik di sisi aplikasi.
Jika Anda melakukan ini tolong simpan "set" Anda sebagai kolom array, seperti text[]
, bukan teks yang dibatasi koma atau spasi. Lihat pertanyaan ini
untuk beberapa alasan.
Anda perlu berhati-hati untuk beberapa hal, seperti fakta bahwa gips antar array lebih ketat daripada gips antara tipe dasarnya. Mis.:
regress=> SELECT 'a' = 'a'::varchar, 'b' = 'b'::varchar;
?column? | ?column?
----------+----------
t | t
(1 row)
regress=> SELECT ARRAY['a','b'] = ARRAY['a','b']::varchar[];
ERROR: operator does not exist: text[] = character varying[]
LINE 1: SELECT ARRAY['a','b'] = ARRAY['a','b']::varchar[];
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
regress=> SELECT ARRAY['a','b']::varchar[] = ARRAY['a','b']::varchar[];
?column?
----------
t
(1 row)
Kolom tersebut dapat diindeks oleh GiST untuk operasi seperti array-contains atau array-overlaps; lihat dokumentasi PostgreSQL tentang pengindeksan array.
Sebagai baris yang dinormalisasi
Opsi lainnya adalah hanya menyimpan baris yang dinormalisasi dengan kunci yang sesuai. Saya masih menggunakan array_agg
untuk menyortir dan membandingkannya, karena operasi set SQL dapat menjadi kikuk untuk digunakan untuk ini (terutama mengingat kurangnya operasi perbedaan set XOR / dua sisi).
Ini umumnya dikenal sebagai EAV (entity-attribute-value). Saya sendiri bukan penggemar, tetapi kadang-kadang memang ada tempatnya. Kecuali Anda akan menggunakannya tanpa value
komponen.
Anda membuat tabel:
CREATE TABLE item_attributes (
item_id integer references items(id),
attribute_name text,
primary key(item_id, attribute_name)
);
dan sisipkan baris untuk setiap entri set untuk setiap item, alih-alih membuat setiap item memiliki kolom bernilai array. Batasan unik yang diberlakukan oleh kunci utama memastikan bahwa tidak ada item yang mungkin memiliki duplikat dari atribut yang diberikan. Urutan atribut tidak relevan/tidak ditentukan.
Perbandingan dapat dilakukan dengan operator set SQL seperti EXCEPT
, atau menggunakan array_agg(attribute_name ORDER BY attribute_name)
untuk membentuk array yang diurutkan secara konsisten untuk perbandingan.
Pengindeksan terbatas untuk menentukan apakah item tertentu memiliki/tidak memiliki atribut tertentu.
Secara pribadi saya akan menggunakan array melalui pendekatan ini.
hstore
Anda juga dapat menggunakan hstores dengan nilai kosong untuk menyimpan set, karena hstore menghapus duplikat kunci. jsonb
9.4 juga akan bekerja untuk ini.
regress=# create extension hstore;
CREATE EXTENSION
regress=# SELECT hstore('a => 1, b => 1') = hstore('b => 1, a => 1, b => 1');
?column?
----------
t
(1 row)
Ini hanya sangat berguna untuk jenis teks. misalnya:
regress=# SELECT hstore('"1.0" => 1, "2.0" => 1') = hstore('"1.00" => 1, "1.000" => 1, "2.0" => 1');
?column?
----------
f
(1 row)
dan menurut saya itu jelek. Jadi sekali lagi, saya lebih suka array.
Hanya untuk array integer
intarray
extension menyediakan fungsi yang berguna dan cepat untuk memperlakukan array sebagai set. Mereka hanya tersedia untuk array integer tetapi sangat berguna.