Postgresql 具有按位AND运算符的位字符串列上的排除约束
所以我只是在阅读PostgreSQL,我似乎找不到一种在位字符串上使用位运算符的方法,我想知道这是否可能 我的用例是我有一个Postgresql 具有按位AND运算符的位字符串列上的排除约束,postgresql,indexing,constraints,bit-manipulation,postgresql-9.1,Postgresql,Indexing,Constraints,Bit Manipulation,Postgresql 9.1,所以我只是在阅读PostgreSQL,我似乎找不到一种在位字符串上使用位运算符的方法,我想知道这是否可能 我的用例是我有一个name:text列和一个value:bit(8)列。我想创建一个约束,基本上是这样的: ADD CONSTRAINT route_method_overlap EXCLUDE USING gist(name WITH =, value WITH &) 但从那以后就没用了 运算符&(位,位)不是运算符族“gist\u bit\u ops”的成员 我假设这是因为位运
name:text
列和一个value:bit(8)
列。我想创建一个约束,基本上是这样的:
ADD CONSTRAINT route_method_overlap
EXCLUDE USING gist(name WITH =, value WITH &)
但从那以后就没用了
运算符&(位,位)不是运算符族“gist\u bit\u ops”的成员
我假设这是因为位运算运算符不返回布尔值。但有没有办法做到我想做的事?有没有办法强制运算符&
将其返回值转换为布尔值
编辑
忘记了版本号。这是在9.1.4版本上安装的“btree_gist”扩展,所有这些都来自Ubuntu12.04 repos。但版本并不重要。如果上游有修复/更新,我可以从repos安装。我仍处于设计阶段。正如您的编辑所阐明的,您安装了扩展。如果没有它,示例将在
name WITH=
处失败
CREATE EXTENSION btree_gist;
btree\u gist
安装的操作员类别涵盖了许多操作员。不幸的是,&
运算符不在其中。显然,这是因为它不会返回一个布尔值
,这是期望运算符能够限定的
替代解决方案 我将使用b-树多列索引(表示速度)和触发器的组合。考虑这个演示,在PostgreSQL上测试<强> 9.1 < /强>:
CREATE TABLE t (
name text
,value bit(8)
);
INSERT INTO t VALUES ('a', B'10101010');
CREATE INDEX t_name_value_idx ON t (name, value);
CREATE OR REPLACE FUNCTION trg_t_name_value_inversion_prohibited()
RETURNS trigger AS
$func$
BEGIN
IF EXISTS (
SELECT 1 FROM t
WHERE (name, value) = (NEW.name, ~ NEW.value) -- example: exclude inversion
) THEN
RAISE EXCEPTION 'Your text here!';
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER insup_bef_t_name_value_inversion_prohibited
BEFORE INSERT OR UPDATE OF name, value -- only involved columns relevant!
ON t
FOR EACH ROW
EXECUTE PROCEDURE trg_t_name_value_inversion_prohibited();
INSERT INTO t VALUES ('a', ~ B'10101010'); -- fails with your error msg.
- 此场景中不需要扩展名
btree\u gist
- 为了提高效率,我限制了价格
- 检查约束不起作用。我引述:
当前,
表达式不能包含子查询,也不能引用 当前行的列以外的变量 粗体强调:CHECK
=
运算符的查找应该比使用&
运算符的假设查找更快
此解决方案不如排除约束安全,因为触发器更容易规避—例如,在同一事件的后续触发器中,或者如果触发器被临时禁用。如果这些条件适用,准备对整个表进行额外检查
更复杂的条件 示例触发器仅捕获
值的反转。正如您在评论中阐明的,您实际上需要这样的条件:
IF EXISTS (
SELECT 1 FROM t
WHERE name = NEW.name
AND value & NEW.value <> B'00000000'::bit(8)
) THEN
正如您所评论的,每个名称最多只能有8行不同的行,实际上更少。所以这应该还是很快的
最终插入性能
如果INSERT
性能至关重要,特别是如果许多尝试的插入失败,那么您可以做更多的事情:创建一个物化视图,按照name
预先聚合值
:
CREATE TABLE mv_t AS
SELECT name, bit_or(value) AS value
FROM t
GROUP BY 1
ORDER BY 1;
name
在此处保证是唯一的。我会在名称
上使用主键
,以提供我们要查找的索引:
ALTER TABLE mv_t SET (fillfactor=90);
ALTER TABLE mv_t
ADD CONSTRAINT mv_t_pkey PRIMARY KEY(name) WITH (fillfactor=90);
然后您的INSERT
可以如下所示:
WITH i(n,v) AS (SELECT 'a'::text, B'10101010'::bit(8))
INSERT INTO t (name, value)
SELECT n, v
FROM i
LEFT JOIN mv_t m ON m.name = i.n
AND m.value & i.v <> B'00000000'::bit(8)
WHERE m.n IS NULL; -- alternative syntax for EXISTS (...)
i(n,v)为的(选择'a':文本,B'10101010':位(8))
插入到t(名称、值)
选择n,v
从我
m.name=i.n上的左连接mv\u t m
和m.value&i.v B'00000000':位(8)
其中m.n为空;——EXISTS(…)的替代语法
只有当您的表得到大量更新时,才有用
在插入或更新名称、值或删除后,在触发器中更新物化视图中的行,以保持其最新状态。额外物品的成本必须与收益进行权衡。很大程度上取决于您的典型负载。必须为您的版本号提供像这样的高级问题。嗯,很有趣。这里使用触发器是为了提高速度吗?而不是说支票约束?我想要防止的是位重叠。因此,对于具有相等name
字段的任何两列,val1和val2必须=B'00000000'
。这就是为什么我认为排除约束可以在这里工作。所以我不认为在~值上建立索引是有建设性的。此外,在这种情况下,请记住,只有8列具有匹配的名称
字段。@Falmari:我用您的澄清修改了我的答案,解决了检查约束,并添加了更多内容。非常详尽的答案,谢谢。我认为更复杂的触发器可以很好地工作,因为插入应该比选择“相对”少。
WITH i(n,v) AS (SELECT 'a'::text, B'10101010'::bit(8))
INSERT INTO t (name, value)
SELECT n, v
FROM i
LEFT JOIN mv_t m ON m.name = i.n
AND m.value & i.v <> B'00000000'::bit(8)
WHERE m.n IS NULL; -- alternative syntax for EXISTS (...)