sql - postgres 中的条件索引和触发器

标签 sql postgresql indexing psql b-tree

尝试在 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;

最佳答案

您可以在触发器中处理您描述的两种情况,而不受约束: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) 对被允许。


suggested by @Laurenz Albe ,为了避免竞争条件,您可以将结构拆分为由表引用的组合目录,并在目录级别应用约束。要完全涵盖您的规则:( demo )

CREATE TABLE available_combinations (
   a integer NOT NULL,
   b integer NOT NULL,
   c integer NOT NULL UNIQUE,
   UNIQUE(a,b),
   UNIQUE(a,b,c)--this is a natural consequence of the previous two UNIQUEs but
                --it's required for foreign keys to link complete combinations
);

CREATE TABLE test_table (
   pkey integer generated by default as identity PRIMARY KEY,
   a integer NOT NULL,
   b integer NOT NULL,
   c integer NOT NULL,
   FOREIGN KEY (a, b, c) REFERENCES available_combinations (a, b, c)
);

test_table允许对同一组合进行多次引用,从而允许您所描述的有效重复。

全部三个UNIQUE available_combinations 上需要 s :

  1. 仅留下 UNIQUE(a,b,c)允许多个不同的c同样(a,b)配对和不同(a,b)配对相同 c .
  2. 仅留下 UNIQUE(c)UNIQUE(a,b,c)仍然允许多个不同的 c同样(a,b)一对。
  3. 仅留下 UNIQUE(a,b)UNIQUE(a,b,c)仍然允许多个不同的不同(a,b)配对相同 c .
  4. 其实只留下UNIQUE(a,b)也可以和UNIQUE(c) 。它已经在逻辑上暗示了 UNIQUE(a,b,c)同样,但需要完整的约束来强制外部记录引用完整的组合。

您也可以留下 c(a,b)完全关闭test_table它允许您将外键规范限制为其中之一并删除 UNIQUE(a,b,c) ,但它也迫使您通过不可插入的 View 或 join 来抓取它每隔 select 就会回来。 Demo :

CREATE TABLE available_combinations (
   a integer NOT NULL,
   b integer NOT NULL,
   c integer NOT NULL UNIQUE,
   UNIQUE(a,b)                      );

CREATE TABLE test_table (
   pkey integer generated by default as identity PRIMARY KEY,
   c integer REFERENCES available_combinations (c) NOT NULL  );

CREATE VIEW v_test_table AS SELECT pkey,a,b,c
FROM test_table t NATURAL JOIN available_combinations a;

您可以考虑使用一个触发器来创建新的 available_option当您将一个插入test_table时无需先创建它。

关于sql - postgres 中的条件索引和触发器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77257389/

相关文章:

mysql - 共享租户对象

php - 使用 OR Case 选择数据,但仍然得到错误的结果

mysql - 奇怪的MySQL命令,它的目的是什么?

sql - Postgresql 插入如果不存在

mysql - 表数据的非线性三向求和

postgresql - pg_dump 转储具有紧凑约束的表模式

sql - 如何将 UPDATE + 数学函数执行到空列中?

php - 有没有办法在 php 的一次调用中发送多个 postgresql sql 准备语句?

r - 合并 R 中的索引列表

python-3.x - 获取每个 id 的所有列,其中该列等于某个值