sea*_*avi 4 sql postgresql set
我有一个表,包含两个单独的列中的单词对.单词的顺序通常很重要,但有时候我只想基于这两个单词进行聚合,而不管顺序如何.是否有一种简单的方法来处理具有相同单词但具有不同顺序(一行与另一行相反)的两行作为相同的"集合"?换句话说,对待:
apple orange orange apple
如:
(apple,orange) (apple,orange)
目前还没有内置方式.
如果在保存时始终将它们标准化,则可以将数组视为集合,始终将它们存储为已排序和重复数据删除.如果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可以重复密钥.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扩展提供了有用的快速函数,用于将数组作为集处理.它们只适用于整数数组,但它们确实非常有用.