【问题标题】:How to create a unique index with conditional and subquery in PostgreSQL?如何在 PostgreSQL 中使用条件和子查询创建唯一索引?
【发布时间】:2012-08-01 19:22:04
【问题描述】:

我使用 PGSQL 并尝试在下面添加索引。

CREATE UNIQUE INDEX fk_client ON user_client (fk_client) WHERE fk_client NOT IN(SELECT fk_client FROM legal_entity);

但是...这是不可能的,因为在创建索引时允许运行子查询。

我收到以下错误:

ERROR:  cannot use subquery in index predicate

有没有办法解决这个问题?

上面的模型代表了case的情况。

  • 客户可以是普通人,也可以是公司
  • 如果是普通人,“legal_entity”表中不会有FK。
  • 如果是普通人,她应该在“user_client”表中只有一条记录。

with index 没有,但是有什么办法可以解决这个问题吗?...

脚本生成表格:

-- user is a special word, then renamed to users
CREATE TABLE users (
    id_user INT,
    name VARCHAR(50) NOT NULL,
    CONSTRAINT user_pkey PRIMARY KEY (id_user)
);

CREATE TABLE client (
    id_client INT,
    CONSTRAINT client_pkey PRIMARY KEY (id_client)
);

CREATE TABLE legal_entity (
    fk_client INT,
    federal_id VARCHAR(14) NOT NULL,
    CONSTRAINT legal_entity_pkey PRIMARY KEY (fk_client),
    CONSTRAINT legal_entity_fkey FOREIGN KEY (fk_client) REFERENCES client (id_client)
);

CREATE TABLE user_client (
    fk_client INT,
    fk_user INT,
    CONSTRAINT user_client_pkey PRIMARY KEY (fk_client, fk_user),
    CONSTRAINT user_client_fkey_1 FOREIGN KEY (fk_client) REFERENCES client (id_client),
    CONSTRAINT user_client_fkey_2 FOREIGN KEY (fk_user) REFERENCES users (id_user)
);

【问题讨论】:

  • 请给出实际的表定义而不是文字描述。
  • #Done - 希望对您有所帮助。
  • 对不起,我不看图。我读过 SQL。
  • 太棒了!现在编辑问题并添加脚本来创建表格。
  • 你的答案也可能被编辑除了模型是一样的,不同的是现在有一个上下文。

标签: sql database postgresql constraints


【解决方案1】:

使用规则的缺点是规则只是在解析查询后重写查询,因此如果通过触发器添加数据,则不会触发。添加一个使用您的逻辑调用函数的 CHECK 约束会更安全。如果我正确地遵循了你的逻辑,它应该是这样的:

CREATE OR REPLACE FUNCTION check_user_client(fkc int) 
  RETURNS boolean AS
$$
DECLARE
  i int;
BEGIN
  SELECT count(*) INTO i FROM legal_entity WHERE fk_client = fkc;
  IF (i > 0) THEN
    RETURN true;
  END IF;

  SELECT count(*) INTO i FROM user_client WHERE fk_client = fkc;
  IF (i = 0) THEN
    RETURN true;
  END IF;

  RETURN false;  
END
$$ LANGUAGE plpgsql;

ALTER TABLE user_client ADD CONSTRAINT unique_user CHECK (check_user_client(fk_client));

【讨论】:

  • 谢谢!有时间我会测试的。
  • 谢谢!该功能完美运行,抱歉延迟提供反馈
  • 这里如何处理并发事务?它们可能都满足条件,然后将提交,使表处于不一致状态。
【解决方案2】:

我通过添加规则表解决了我的问题:

CREATE OR REPLACE RULE rule_test AS ON INSERT
    TO user_client WHERE (
        (SELECT fk_client FROM legal_entity WHERE fk_client = new.fk_client) IS NULL) AND (
        (SELECT fk_client FROM user_client WHERE fk_client = new.fk_client) IS NOT NULL)
    DO INSTEAD NOTHING;

【讨论】:

    【解决方案3】:
    DROP SCHEMA tmp CASCADE;
    CREATE SCHEMA tmp ;
    SET search_path=tmp;
    
    CREATE TABLE foo
            ( id INTEGER NOT NULL PRIMARY KEY
            );
    CREATE TABLE tbl_relation
            ( id INTEGER NOT NULL PRIMARY KEY
            , foo_id INTEGER REFERENCES foo(id)
            , fk_1 INTEGER
            );
    
    CREATE UNIQUE INDEX fk_1 ON tbl_relation (fk_1)
            WHERE foo_id IS NULL;
    

    UPDATE:这是修改数据模型后的新情况。 关键是:legal_entity 和客户端似乎共享一个关键域(看起来不对)给 legal_entity 自己的关键域似乎更合适。

    DROP SCHEMA tmp CASCADE;
    CREATE SCHEMA tmp ;
    SET search_path=tmp;
    
    -- user is a special word, then renamed to users
    CREATE TABLE users
            ( id INTEGER PRIMARY KEY
            -- name is a special word
            , zname VARCHAR(50) NOT NULL
            );
    
    CREATE TABLE client
            (id INTEGER PRIMARY KEY
            );
    
    CREATE TABLE legal_entity
            ( id INTEGER PRIMARY KEY
            , client_id INTEGER REFERENCES client(id)
            , federal_id VARCHAR(14) NOT NULL
            );
    
    CREATE TABLE user_client
            ( client_id INTEGER REFERENCES client (id)
            , user_id INTEGER REFERENCES users (id)
            , legal_id INTEGER REFERENCES legal_entity(id)
            , CONSTRAINT user_client_pkey PRIMARY KEY (client_id, user_id) 
            );
    
    CREATE INDEX tres_stupide ON user_client (client_id) WHERE legal_id IS NULL;
    

    【讨论】:

    • foo_id 不能为空 fk_1 和 fk_2 PK 是 tbl_relation
    • Foo_id 是外键,可以为空。 fk_1 以(foo_id 不为 NULL)为条件。我不明白你句子的其余部分。也许是关于 OP 调用列 fk_1 的混淆,这是一个非常糟糕的列名称。
    • 我编辑了这个问题,我相信现在最好理解。
    • 法人实体中的client_id是PK,没有意义在user_client中插入重复值。
    • 您的数据模型似乎不正确。 user_client 表允许用户和客户端之间的 N::M 关系。在文本中,您解释说客户是“自然”人或法人实体。 N::M 关系将允许客户端成为人员的集合,并且 s 属于这些集合中的多个。 IMO,client_id 作为 PK 在多个表中的出现也是可疑的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-16
    • 2014-09-01
    • 2021-07-29
    • 2013-09-11
    相关资源
    最近更新 更多