Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Postgresql 具有按位AND运算符的位字符串列上的排除约束_Postgresql_Indexing_Constraints_Bit Manipulation_Postgresql 9.1 - Fatal编程技术网

Postgresql 具有按位AND运算符的位字符串列上的排除约束

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”的成员 我假设这是因为位运

所以我只是在阅读PostgreSQL,我似乎找不到一种在位字符串上使用位运算符的方法,我想知道这是否可能

我的用例是我有一个
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
    表达式不能包含子查询,也不能引用 当前行的列以外的变量

    粗体强调:

应该表现得很好,实际上比排除约束更好,因为b树索引的维护比GiST索引便宜。使用基本
=
运算符的查找应该比使用
&
运算符的假设查找更快

此解决方案不如排除约束安全,因为触发器更容易规避—例如,在同一事件的后续触发器中,或者如果触发器被临时禁用。如果这些条件适用,准备对整个表进行额外检查


更复杂的条件 示例触发器仅捕获
值的反转。正如您在评论中阐明的,您实际上需要这样的条件:

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 (...)