【问题标题】:How to create foreign key only if it doesn't exists already?仅当外键不存在时如何创建外键?
【发布时间】:2012-10-12 09:03:50
【问题描述】:

我使用的是 PostgreSQL 9.1。

我有一个表common.client_contact,我在其中使用以下代码创建了外键:

ALTER TABLE common.client_contact 
ADD FOREIGN KEY (contact_id) REFERENCES common.contact_item(id);

如果我执行这段代码,我会得到几个不同名字的外键(比如client_contact_contact_id_fkey1client_contact_contact_id_fkey2client_contact_contact_id_fkey3等等)。

所以,在创建新约束之前,我需要检查它是否存在。

我检查pg_constraint表中是否存在这个约束:

SELECT * FROM pg_constraint WHERE conname = 'client_contact_contact_id_fkey'

现在我需要将它们组合在一起。类似的东西

IF NOT EXISTS
    (SELECT * FROM pg_constraint WHERE conname = 'client_contact_contact_id_fkey')
ALTER TABLE common.client_contact
    ADD CONSTRAINT client_contact_contact_id_fkey
    FOREIGN KEY (contact_id) REFERENCES common.contact_item(id)

或者只是

ALTER TABLE common.client_contact 
ADD FOREIGN KEY IF NOT EXISTS (contact_id) REFERENCES common.contact_item(id)

但是这两个查询都会产生语法错误。那么,我该如何在 PostgreSQL 中做到这一点呢?

【问题讨论】:

    标签: postgresql postgresql-9.1


    【解决方案1】:

    使用DO 块在PL/PgSQL 中执行它。

    DO $$
    BEGIN
        IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'client_contact_contact_id_fkey') THEN
            ALTER TABLE common.client_contact
                ADD CONSTRAINT client_contact_contact_id_fkey
                FOREIGN KEY (contact_id) REFERENCES common.contact_item(id);
        END IF;
    END;
    $$;
    

    您似乎依赖于默认约束名称生成,这并不理想。使用information_schema 检查是否存在链接两列的约束可能更安全。

    以下查询在不依赖生成的约束名称的情况下检查两个表之间的外键:

    SELECT 1
    FROM information_schema.table_constraints tc 
    INNER JOIN information_schema.constraint_column_usage ccu 
      USING (constraint_catalog, constraint_schema, constraint_name) 
    INNER JOIN information_schema.key_column_usage kcu 
      USING (constraint_catalog, constraint_schema, constraint_name) 
    WHERE constraint_type = 'FOREIGN KEY' 
      AND ccu.table_name = 'contact_item' 
      AND ccu.table_schema = 'common'  
      AND ccu.column_name = 'contact_id' 
      AND tc.table_schema = 'common' 
      AND tc.table_name = 'client_contact'
      AND kcu.column_name = 'id';
    

    【讨论】:

    • 顺便说一句,我怀疑上面的查询需要改进以确保它不匹配 include contact_idid 的多列约束,因此如果需要,请进一步测试使用它。
    • 在代码sn-p的第一部分,不应该是“IF NOT EXISTS”而不是“IF EXISTS”吗?
    • @KumarVaibhav 是的,应该的。修复。
    • 您也可以使用异常处理程序而不是 if not exists。请参阅我对stackoverflow.com/questions/6801919/…的回复
    【解决方案2】:

    解决您遇到的问题的一种方法是在创建约束之前删除它

    ALTER TABLE common.client_contact DROP CONSTRAINT IF EXISTS  client_contact_contact_id_fkey; 
    
    ALTER TABLE common.client_contact
        ADD CONSTRAINT client_contact_contact_id_fkey
        FOREIGN KEY (contact_id) REFERENCES common.contact_item(id)
    

    命名约束的添加将通过。

    【讨论】:

      【解决方案3】:

      我不确定这是否可行,但你可以试试。

      在执行查询之前触发

      SET FOREIGN_KEY_CHECKS=0
      

      然后您进行查询并

      SET FOREIGN_KEY_CHECKS=1
      

      【讨论】:

      • PostgreSQL 甚至没有 FOREIGN_KEY_CHECKS GUC,所以我不知道你从哪里得到的。其次,这不是暂时违反外键(无论如何都是一个糟糕的想法),而是仅在一个外键不存在时才定义一个新的外键约束。
      • 那是我说的,“我不确定这是否可行,但你可以试试。”
      猜你喜欢
      • 1970-01-01
      • 2013-06-14
      • 1970-01-01
      • 1970-01-01
      • 2013-02-10
      • 2011-04-22
      • 2018-01-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多