在PostgreSQL中是否有处理无序数组(集合)的标准方法?

sea*_*avi 4 sql postgresql set

我有一个表,包含两个单独的列中的单词对.单词的顺序通常很重要,但有时候我只想基于这两个单词进行聚合,而不管顺序如何.是否有一种简单的方法来处理具有相同单词但具有不同顺序(一行与另一行相反)的两行作为相同的"集合"?换句话说,对待:

apple orange
orange apple

如:

(apple,orange)
(apple,orange)

Cra*_*ger 8

目前还没有内置方式.

作为数组

如果在保存时始终将它们标准化,则可以将数组视为集合,始终将它们存储为已排序和重复数据删除.如果PostgreSQL有一个内置的C函数来做这件事会很棒,但事实并非如此.我看了一下写了一个,但C数组API很可怕,所以即使我写了一堆扩展,我还是小心翼翼地远离这个.

如果你不介意适度的性能,你可以在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;
Run Code Online (Sandbox Code Playgroud)

然后将所有保存包装在调用中array_uniq_sort或使用触发器强制执行.然后,您可以比较您的数组是否相等.array_uniq_sort如果您改为在应用程序端执行排序/唯一,则可以避免从应用程序调用数据.

如果您这样做,请将您的"集合"存储为数组列,例如text[],不是以逗号或空格分隔的文本.出于某些原因,请参阅此问题.

你需要注意一些事情,比如数组之间的强制转换比它们的基类型之间的强制转换更严格.例如:

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)
Run Code Online (Sandbox Code Playgroud)

对于像array-contains或array-overlaps这样的操作,这些列是GiST-indexable; 请参阅有关数组索引的PostgreSQL文档.

作为规范化的行

另一种选择是使用合适的密钥存储规范化的行.我仍然会用它array_agg来排序和比较它们,因为SQL设置操作可能很笨拙(特别是考虑到缺少XOR /双面设置差异操作).

这通常称为EAV(实体属性值).我自己不是粉丝,但偶尔会有它的位置.除非您在没有value组件的情况下使用它.

你创建一个表:

CREATE TABLE item_attributes (
    item_id integer references items(id),
    attribute_name text,
    primary key(item_id, attribute_name)
);
Run Code Online (Sandbox Code Playgroud)

并为每个项目的每个集合条目插入一行,而不是让每个项目都有一个数组值列.主键强制执行的唯一约束确保没有项可能具有给定属性的重复项.属性排序无关紧要/未定义.

可以使用SQL集合运算符进行比较EXCEPT,或者使用它array_agg(attribute_name ORDER BY attribute_name)来形成一致排序的数组以进行比较.

索引仅限于确定给定项目是否具有给定属性.

就个人而言,我会在这种方法中使用数组.

hstore

您还可以使用具有空值的hstore来存储集,因为hstore可以重复密钥.9.4 jsonb也适用于此.

regress=# create extension hstore;
CREATE EXTENSION
regress=# SELECT hstore('a => 1, b => 1') = hstore('b => 1, a => 1, b => 1');
 ?column? 
----------
 t
(1 row)
Run Code Online (Sandbox Code Playgroud)

但它只对文本类型非常有用.例如:

regress=# SELECT hstore('"1.0" => 1, "2.0" => 1') = hstore('"1.00" => 1, "1.000" => 1, "2.0" => 1');
 ?column? 
----------
 f
(1 row)
Run Code Online (Sandbox Code Playgroud)

而且我认为这很难看.所以,我再次赞成阵列.

仅适用于整数数组

intarray扩展提供了有用的快速函数,用于将数组作为集处理.它们只适用于整数数组,但它们确实非常有用.