try 在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();

但对于第二个 case ,我面临的问题这个酷儿是错误的,我为这个写的

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,为了避免竞争条件,您可以将 struct 拆分成一个组合目录以供您的表引用,并在目录级别应用您的约束.要完全涵盖你的规则:(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允许多次引用相同的组合,从而允许您描述的有效重复.

available_combinations上的三个UNIQUE都是必需的:

  1. 仅保留UNIQUE(a,b,c)允许同一(a,b)对有多个不同的c,而同一c有不同的(a,b)对.
  2. 仅保留UNIQUE(c)UNIQUE(a,b,c)仍然允许同一(a,b)对有多个不同的c.
  3. 仅保留UNIQUE(a,b)UNIQUE(a,b,c)仍然允许对于相同的c有多个不同的(a,b)对.
  4. 实际上,只留下UNIQUE(a,b)UNIQUE(c)是可以的.它在逻辑上也意味着UNIQUE(a,b,c),但需要完整的约束才能强制 foreign 记录引用完整的组合.

您也可以让c(a,b)完全关闭test_table,这允许您将外键规范限制为其中之一并删除UNIQUE(a,b,c),但它也迫使您通过不可插入的视图获取它,或者每隔select取回join.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;

您可以考虑一个触发器,当您将一个触发器插入到test_table中时,它会创建一个新的available_option,而不是先创建它.

Sql相关问答推荐

Postgresql在加入时显示重复的行

在SQL:2003(PGQ)中,Cypher查询语言、GQL、PGQL和属性图查询的常见子集是什么?'

判断Pyspark生成的SQL查询

从自定义日期和时间开始,每月具有给定状态的公司数量

Ffltter&;Dart SQL Lite包:是否可以在一个查询中执行多条更新语句(每次执行不同的WHERE参数)

查询每周数据(周一至周日),避免年度日期重叠

SQL按日期分组字段和如果日期匹配则求和

最小非重复集的SQL查询

在Power Bi中将SQL代码转换为DAX

如何使用Clickhouse的COUNT聚合返回所有列,但GROUP BY是这些列的子集

如何使用jsonn_populate_record()插入到包含IDENTITY列的表中

为什么左联接结果在MS Access数据库中不匹配

STContains、STIntersections和STWithin返回错误的地理结果

不同计数的 Postgres PIVOT 表

用替代方案替换 SQL Cursor 以提高性能

我可以在 T-SQL (SQL Server) 的函数内使用 OPTION 子句吗?

SQL 根据前一天的最大值计算每天的值数

joins 组合多个重复数据删除策略

使用 PL/PGSQL 函数 Select 返回多条记录

在 AWS athena 的视图之上创建视图时,如何消除此错误:列别名列表有 1 个条目但t有 4 列可用?