3

我正在尝试使用MySQL SET类型PostgreSQL,但我发现只有数组,它具有非常相似的功能但不符合要求。

是否PostgreSQL有类似的数据类型?

4

6 回答 6

2

您可以使用以下解决方法:

1. BIT字符串

您可以简单地定义一组最大 N 个元素BIT(N)。填充和检索有点尴尬 - 您必须使用位掩码作为集合成员。但是位串真的很适合集合操作:交集很简单&,并集很简单|

这种类型的存储非常有效 - 每位每位的长度开销很小。

此外,长度不受限制也很好(但您必须提前决定)。

2. HSTORE

HSTOREtype 是一个扩展,但是很容易安装。简单地执行

CREATE EXTENSION hstore

对于大多数安装 (9.1+) 将使其可用。有传言说 PostgreSQL 9.3 将HSTORE作为标准类型。

它不是真正的集合类型,而是更像 Perl 哈希或 Python 字典:它保留任意的key=>value对集合。

这样,它不是很有效(当然不是 BIT 字符串有效),但它确实提供了集合所必需的函数:||对于并集,但交集有点尴尬:使用

slice(a,akeys(b)) || slice(b,akeys(a))

您可以在此处阅读有关 HSTORE的更多信息。

于 2013-02-04T08:22:12.363 回答
2

基于上面 a_horse_with_no_name 的答案,我建议稍微复杂一点:

 CREATE FUNCTION set_check(in_value anyarray, in_check anyarray)
 RETURNS BOOL LANGUAGE SQL IMMUTABLE AS
 $$
    WITH basic_check AS (
         select bool_and(v = any($2)) as condition, count(*) as ct 
              FROM unnest($1) v
          GROUP BY v
    ), length_check AS (
         SELECT count(*) = 0 as test FROM unnest($1)
    )
    SELECT bool_and(condition AND ct = 1)
      FROM basic_check
     UNION
    SELECT test from length_check where test;

 $$;

然后你应该能够做类似的事情:

 CREATE TABLE set_test (
       my_set text[] CHECK (set_check(my_set, array['one'::text,'two']))
 );

这有效:

postgres=# insert into set_test values ('{}');
INSERT 0 1
postgres=# insert into set_test values ('{one}');
INSERT 0 1
postgres=# insert into set_test values ('{one,two}');
INSERT 0 1
postgres=# insert into set_test values ('{one,three}');
ERROR:  new row for relation "set_test" violates check constraint "set_test_my_set_check"
postgres=# insert into set_test values ('{one,one}');
ERROR:  new row for relation "set_test" violates check constraint "set_test_my_set_check"

请注意,这假设对于您的集合,每个值都必须是唯一的(我们在这里讨论集合)。该功能应该执行得非常好并且应该满足您的需求。然而,这具有处理任何尺寸集的优势。

存储方面它与 MySQL 的实现完全不同。它将占用更多磁盘空间,但应该处理具有尽可能多的成员的集合,前提是您没有遇到存储限制......所以与 MySQL 的实现相比,它应该具有超集的功能。但是,一个显着的区别是这不会数组折叠成不同的值。它只是禁止他们。如果您也需要,请查看触发器。

此解决方案还保持输入数据的序数不变,因此 '{one,two}' 与 '{two,one}' 不同,因此如果您需要确保行为已更改,您可能需要查看 PostgreSQL 9.2 上的排除约束.

于 2013-03-01T02:08:17.647 回答
2

带有检查约束的数组怎么样:

create table foobar 
(
  myset text[] not null,
  constraint check_set 
      check ( array_length(myset,1) <= 2 
             and (myset = array[''] or 'one'= ANY(myset) or 'two' = ANY(myset))
            )
);

这将匹配SET('one', 'two')MySQL 手册中解释的定义。

唯一不会做的就是“标准化”数组。所以

insert into foobar values (array['one', 'two']);

insert into foobar values (array['two', 'one']);

显示方式与 MySQL 不同(两者都显示为'one','two'

但是,检查约束会因超过 3 或 4 个元素而变得混乱。

于 2013-02-04T08:47:21.877 回答
1

您在寻找枚举数据类型吗?

PostgreSQL 9.1 枚举类型

于 2013-02-03T16:29:08.097 回答
0

从阅读问题中引用的页面来看,似乎 SET 是一种在一列中存储多达 64 个命名布尔值的方法。PostgreSQL 没有提供这样做的方法。您可以使用独立的布尔列,或某种大小的整数并直接旋转位。添加两个新表(一个用于有效名称,另一个用于将名称连接到详细信息行)可能是有意义的,尤其是在可能需要将任何其他数据与单个值相关联的情况下。

于 2013-02-03T17:38:10.543 回答
0

前段时间我写了一个类似的扩展

https://github.com/okbob/Enumset

但它并不完整

pltoolkit 的一些更完整和更接近 mysql 的功能

http://okbob.blogspot.cz/2010/12/bitmapset-for-plpgsql.html http://pgfoundry.org/frs/download.php/3203/pltoolbox-1.0.2.tar.gz http:// postgres.cz/wiki/PL_toolbox_%28en%29

函数 find_in_set 可以通过数组来模拟

http://okbob.blogspot.cz/2009/08/mysql-functions-for-postgresql.html

于 2013-02-03T20:19:55.430 回答