postgres 中的条件索引和触发器

问题描述 投票:0回答:2

尝试在 postgresql 中创建条件唯一索引但无法这样做并收到此错误

Query 1 ERROR: ERROR:  cannot use subquery in index predicate
LINE 3: WHERE (
              ^

这就是我的查询的样子

CREATE UNIQUE INDEX conditional_unique_index
ON test_table (a, b)
WHERE (
    SELECT COUNT(*)
    FROM test_table t2
    WHERE t2.a = test_table.a AND t2.b = test_table.b
) = 1;

我的要求是这样的:

CREATE TABLE test_table (
    a integer,
    b integer,
    c integer
);
INSERT INTO test_table (a, b, c) VALUES
(1, 2, 22),
(1, 2, 22),
(1, 2, 22),
(1, 3, 34),
(2, 3, 26),
(2, 3, 26);

条件是如果列 (a, b) 中有多个行具有相同的值,则列 c 中的值必须相同,如下所示 (1, 2, 22),(1, 2, 22),(1 , 2, 22);这是不允许的 (1, 2, 22),(1, 2, 23),(1, 2, 22);这里的 c 必须相同 (1, 2, 23)

现在第二个条件是如果列 (a, b) 是唯一的,那么 c 也应该是唯一的 像这样 (1, 3, 34),(2, 3, 26);这是不允许的 (1, 3, 34),(2, 3, 34);在这种情况下不允许 c 具有相同的值

到目前为止,对于第一种情况,我已经通过使用触发器来管理它

CREATE OR REPLACE FUNCTION check_conditional_unique() RETURNS TRIGGER AS $$
BEGIN
    IF EXISTS (
        SELECT 1
        FROM test_table t2
        WHERE t2.a = NEW.a AND t2.b = NEW.b AND t2.c <> NEW.c
    ) THEN
        RAISE EXCEPTION 'Duplicate values in c for the same a and b combination.';
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_check_conditional_unique
BEFORE INSERT ON test_table
FOR EACH ROW
EXECUTE FUNCTION check_conditional_unique();

但是对于第二种情况,我面临的问题是这个查询是错误的,我为此写了

CREATE UNIQUE INDEX conditional_unique_index
ON test_table (a, b)
WHERE (
    SELECT COUNT(*)
    FROM test_table t2
    WHERE t2.a = test_table.a AND t2.b = test_table.b
) = 1;
sql postgresql indexing psql b-tree
2个回答
0
投票

您可以在触发器中处理您描述的两种情况,而不受约束:demo

CREATE OR REPLACE FUNCTION check_conditional_unique() RETURNS TRIGGER AS $f$
BEGIN
    IF EXISTS (--reject same (a,b) for different c
        SELECT FROM test_table t2
        WHERE (t2.a,t2.b) = (NEW.a,NEW.b) 
        AND    t2.c       <> NEW.c
    ) THEN
        RAISE EXCEPTION 'New values in c for the same a and b combination.';
    END IF;
    IF EXISTS (--reject same c for different (a,b)
        SELECT FROM test_table t2
        WHERE (t2.a,t2.b) <> (NEW.a,NEW.b) 
        AND    t2.c       =   NEW.c
    ) THEN
        RAISE EXCEPTION 'Duplicate values in c for different a and b combination.';
    END IF;
    RETURN NEW;
END;
$f$ LANGUAGE plpgsql;

您给出的示例与

unique
或部分唯一约束的用途不匹配;只要它们共享一个共同的
c
,您就需要允许重复的
(a,b)
对。


0
投票

对此的经典解决方案是不通过将数据模型拆分为多个表来标准化数据模型。观察到

c
在功能上依赖于
(a, b)
。所以你可以设计一张这样的表:

CREATE TABLE tab2 (
   a integer NOT NULL,
   b integer NOT NULL,
   c integer NOT NULL,
   PRIMARY KEY (a, b)
);

由于

a
b
可能在您的设置中多次出现,因此您将拥有另一个如下表:

CREATE TABLE tab1 (
   pkey integer PRIMARY KEY,
   a integer NOT NULL,
   b integer NOT NULL,
   FOREIGN KEY (a, b) REFERENCES tab2 (a, b)
);

添加主键是因为每个表都应该/必须有一个。

你的桌子就变成了一个视图:

CREATE VIEW test_table AS
SELECT tab1.a, tab1.b, tab2.c
FROM tab1 JOIN tab2 USING (a, b);
© www.soinside.com 2019 - 2024. All rights reserved.