Fal*_*rri 5 postgresql indexing bit-manipulation constraints postgresql-9.1
所以我只是在阅读PostgreSQL中的排除约束,我似乎无法找到在位串上使用按位运算符的方法,我想知道它是否可行.
我的用例是我有一个name: text列和一value: bit(8)列.我想创建一个基本上这样说的约束:
ADD CONSTRAINT route_method_overlap
EXCLUDE USING gist(name WITH =, value WITH &)
Run Code Online (Sandbox Code Playgroud)
但这从此起作用
operator&(bit,bit)不是运算符族"gist_bit_ops"的成员
我假设这是因为bit_ops&运算符不返回布尔值.但有没有办法做我想做的事情?有没有办法强制operator &将其返回值强制转换为布尔值?
忘了版本号.这是在9.1.4上安装了"btree_gist"扩展,全部来自Ubuntu 12.04 repos.但版本并不重要.如果上游有修复/更新,我可以从repos安装.我还处于设计阶段.
正如您的编辑所阐明的那样,您安装了扩展btree_gist. 没有它,该示例将在 处失败name WITH =。
CREATE EXTENSION btree_gist;
Run Code Online (Sandbox Code Playgroud)
安装的操作符类btree_gist涵盖了许多操作符。不幸的是,&运营商不在其中。显然是因为它没有返回boolean操作员应该符合条件的 a 。
我会使用 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.
Run Code Online (Sandbox Code Playgroud)
该扩展btree_gist是不是在这种情况下需要。
我将触发器限制为相关列的 INSERT 或 UPDATE以提高效率。
一个检查约束是行不通的。我引用手册CREATE TABLE:
目前,
CHECK表达式不能包含子查询,也不能引用当前行列以外的变量。
大胆强调我的:
应该表现得很好,实际上比排除约束要好,因为维护 b-tree 索引比 GiST 索引便宜。并且使用基本=运算符进行查找应该比使用运算符进行假设查找更快&。
此解决方案不如排除约束安全,因为可以更轻松地绕过触发器 - 例如,在同一事件的后续触发器中,或者如果触发器被暂时禁用。如果这些条件适用,请准备好对整个表运行额外的检查。
示例触发器仅捕获 的反转value。正如您在评论中澄清的那样,您实际上需要这样的条件:
IF EXISTS (
SELECT 1 FROM t
WHERE name = NEW.name
AND value & NEW.value <> B'00000000'::bit(8)
) THEN
Run Code Online (Sandbox Code Playgroud)
这种情况稍微贵一些,但仍然可以使用索引。上面的多列索引可以工作——如果你有需要的话。或者,稍微更有效一点,名称上的简单索引:
CREATE INDEX t_name_idx ON t (name);
Run Code Online (Sandbox Code Playgroud)
正如您所评论的,每个最多只能有 8 个不同的行name,实际上更少。所以这应该仍然很快。
如果INSERT性能是最重要的,特别是如果许多尝试的 INSERT 都失败了,你可以做更多的事情:创建一个预先聚合valueper的物化视图name:
CREATE TABLE mv_t AS
SELECT name, bit_or(value) AS value
FROM t
GROUP BY 1
ORDER BY 1;
Run Code Online (Sandbox Code Playgroud)
name保证在这里是独一无二的。我会使用一个PRIMARY KEYonname来提供我们所追求的索引:
ALTER TABLE mv_t SET (fillfactor=90);
ALTER TABLE mv_t
ADD CONSTRAINT mv_t_pkey PRIMARY KEY(name) WITH (fillfactor=90);
Run Code Online (Sandbox Code Playgroud)
那么你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 (...)
Run Code Online (Sandbox Code Playgroud)
的fillfactor,如果你的表得到了很多的更新才是有用的。
在 a 中更新物化视图中的行TRIGGER AFTER INSERT OR UPDATE OF name, value OR DELETE以使其保持最新状态。必须权衡额外对象的成本与收益。很大程度上取决于您的典型负载。