【发布时间】:2013-01-18 10:15:59
【问题描述】:
【问题讨论】:
标签: mysql database postgresql database-migration sqldatatypes
【问题讨论】:
标签: mysql database postgresql database-migration sqldatatypes
您在寻找枚举数据类型吗?
【讨论】:
通过阅读问题中引用的页面,似乎 SET 是一种在一列中存储多达 64 个命名布尔值的方法。 PostgreSQL 没有提供这样做的方法。您可以使用独立的布尔列,或某种大小的整数并直接旋转位。添加两个新表(一个用于有效名称,另一个用于将名称连接到详细信息行)可能有意义,尤其是在可能需要将任何其他数据与单个值关联时。
【讨论】:
前段时间我写了一个类似的扩展
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
【讨论】:
您可以使用以下解决方法:
您可以将最多 N 个元素的集合定义为简单的BIT(N)。
填充和检索有点尴尬 - 您必须使用位掩码作为集合成员。但是位串真的很适合set operations:交集就是&,联合就是|。
这种类型的存储效率非常高 - 每比特的长度开销很小。
此外,长度不受限制也很好(但您必须提前决定)。
HSTORE 类型是一个扩展,但是很容易安装。简单地执行
CREATE EXTENSION hstore
对于大多数安装 (9.1+) 将使其可用。有传言说 PostgreSQL 9.3 将有 HSTORE 作为标准类型。
它并不是真正的集合类型,而更像 Perl 哈希或 Python 字典:它保留任意的 key=>value 对集合。
这样,它不是很有效(当然不是 BIT 字符串有效),但它确实提供了集合所必需的功能:|| 用于联合,但交集有点尴尬:使用
slice(a,akeys(b)) || slice(b,akeys(a))
您可以阅读有关 HSTORE 的更多信息here。
【讨论】:
VARBIT 你将只花费 1000000/8 = 125KB(即使这也会被 Postgres TOAST 压缩,可能会压缩 5x-10x到 20KB)。如果您使用具有唯一约束的表,您将花费1000000*(32+8) = 40 MB(行开销为about 32 bytes + 4-8 字节用于您的唯一键和数据),并且不会被压缩。所以差异可能是 1000 倍或更多。
带有检查约束的数组呢:
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 个元素而变得混乱。
【讨论】:
基于上面 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 上。
【讨论】: