Postgres:添加约束(如果它尚不存在)

Pau*_*rth 58 sql postgresql constraints

ALTER TABLE foo ADD CONSTRAINT bar ...如果约束已经存在,Postgres有没有办法说哪个会忽略命令,这样它就不会引发错误?

Web*_*mut 65

一种可能的解决方案是在创建新约束之前简单地使用DROP IF EXISTS.

ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;
Run Code Online (Sandbox Code Playgroud)

似乎比尝试查询information_schema或目录更容易,但在巨大的表上可能会很慢,因为它总是重新创建约束.

编辑2015-07-13:Kev在他的回答中指出,当约束不存在且未被强制执行时,我的解决方案会创建一个短窗口.虽然这是事实,但是通过将两个语句包装在事务中,可以非常容易地避免这样的窗口.

  • 我知道这个线程是旧的,但也许值得指出,事务通常适用于DML,而alter table命令是DDL.通常无法回滚DDL命令(取决于数据库).话虽如此,Postgres*确实*支持回滚DDL,而Oracle和MySQL则不支持.有关特定于数据库的详细信息,请参阅此主题:/sf/ask/328488331/ ?utm_medium =有机&utm_source = google_rich_qa&utm_campaign = google_rich_qa (4认同)
  • 我建议不要编写只为了重新创建约束而删除约束的代码,因为 Webmut 指出这在大型表上可能会很慢。不要冒险,而是首先检查约束是否存在,我在这个问题的另一个答案中展示了如何。 (2认同)

Kev*_*Kev 33

这可能有所帮助,虽然它可能有点肮脏的黑客:

create or replace function create_constraint_if_not_exists (
    t_name text, c_name text, constraint_sql text
) 
returns void AS
$$
begin
    -- Look for our constraint
    if not exists (select constraint_name 
                   from information_schema.constraint_column_usage 
                   where table_name = t_name  and constraint_name = c_name) then
        execute constraint_sql;
    end if;
end;
$$ language 'plpgsql'
Run Code Online (Sandbox Code Playgroud)

然后打电话:

SELECT create_constraint_if_not_exists(
        'foo',
        'bar',
        'ALTER TABLE foo ADD CONSTRAINT bar CHECK (foobies < 100);')
Run Code Online (Sandbox Code Playgroud)

更新:

根据Webmut的回答,以下建议:

ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;
Run Code Online (Sandbox Code Playgroud)

这在您的开发数据库中可能很好,或者您知道可以将依赖于此数据库的应用程序关闭到维护窗口.

但如果这是一个生动的关键任务关键24x7生产环境,你真的不想像这样毫无疑问地放弃限制.即使是几毫秒,也有一个短窗口,您不再强制执行约束,这可能允许错误的值滑过.这可能会产生意想不到的后果,导致在某些时候出现相当大的商业成本.

  • 通过使用事务删除短窗口。 (3认同)
  • 我会进一步修改这个答案,以便execute语句是`execute'ALTER TABLE'|| t_name || '添加约束'|| c_name || ''|| constraint_sql;`并且调用该函数看起来像`SELECT create_constraint_if_not_exists('foo','bar','CHECK(foobies <100);');`.这可以确保您不会弄乱约束SQL中的参数,因为它们基于原始参数. (2认同)

小智 20

您可以在匿名DO块内使用异常处理程序来捕获重复的对象错误.

DO $$
BEGIN

  BEGIN
    ALTER TABLE foo ADD CONSTRAINT bar ... ;
  EXCEPTION
    WHEN duplicate_object THEN RAISE NOTICE 'Table constraint foo.bar already exists';
  END;

END $$;
Run Code Online (Sandbox Code Playgroud)

http://www.postgresql.org/docs/9.4/static/sql-do.html http://www.postgresql.org/docs/9.4/static/plpgsql-control-structures.html http:// www. postgresql.org/docs/9.4/static/errcodes-appendix.html

  • 不得不将`duplicate_object`改为`duplicate_table`(代码42P07).Postgres 9.6 (3认同)
  • `FOREIGN KEY`的`duplicate_object`。用于“唯一”约束的“ duplicate_table”。PostgreSQL 9.6.8 (2认同)

Beh*_*nam 9

您可以在pg_constraint表上运行查询以查找是否存在约束.

SELECT 1 FROM pg_constraint WHERE conname = 'constraint_name'"
Run Code Online (Sandbox Code Playgroud)

  • "DROP CONSTRAINT"有一个"IF EXISTS"选项,但AFAIK没有"ADD CONSTRAINT". (3认同)

jam*_*iet 7

在包含大量数据的表上创建约束可能是一项昂贵的操作,因此,我建议不要删除约束只是为了立即之后立即再次创建约束-您只想创建一次约束。

我选择使用与Mike Stankavich非常相似的匿名代码块来解决此问题,但是与Mike(捕获错误)不同,我首先检查约束是否存在:

DO $$
BEGIN
    IF NOT EXISTS ( SELECT  constraint_schema
                ,       constraint_name 
                FROM    information_schema.check_constraints 
                WHERE   constraint_schema = 'myschema'
                  AND   constraint_name = 'myconstraintname'
              )
    THEN
        ALTER TABLE myschema.mytable ADD CONSTRAINT myconstraintname CHECK (column <= 100);
    END IF;
END$$; 
Run Code Online (Sandbox Code Playgroud)

  • 我必须使用“information_schema.constraint_column_usage”来代替,这让我可以控制我可以读取的名称以及表名称。 (2认同)

Han*_*nde 7

利用它regclass可以减少冗长、提高性能并避免与模式之间的表命名冲突相关的错误:

DO $$ BEGIN
    IF NOT EXISTS (SELECT FROM pg_constraint 
                   WHERE conrelid = 'foo'::regclass AND conname = 'bar') THEN 
        ALTER TABLE foo ADD CONSTRAINT bar...;
    END IF;
END $$;
Run Code Online (Sandbox Code Playgroud)

这也适用于其他模式中的表,例如:

DO $$ BEGIN
    IF NOT EXISTS (SELECT FROM pg_constraint 
                   WHERE conrelid = 's.foo'::regclass AND conname = 'bar') THEN 
        ALTER TABLE s.foo ADD CONSTRAINT bar...;
    END IF;
END $$;
Run Code Online (Sandbox Code Playgroud)


Ped*_*ros 6

使用information_schema.constraint_column_usage检查约束不适用于外键。我使用pg_constraint来检查主键外键唯一约束:

CREATE OR REPLACE FUNCTION add_constraint(t_name text, c_name text, constraint_sql text)
RETURNS void
AS $$
BEGIN
    IF NOT EXISTS(
            SELECT c.conname
            FROM pg_constraint AS c
            INNER JOIN pg_class AS t ON c.conrelid = t."oid"
            WHERE t.relname = t_name AND c.conname = c_name
    ) THEN

        EXECUTE 'ALTER TABLE ' || t_name || ' ADD CONSTRAINT ' || c_name || ' ' || constraint_sql;

    END IF;
END;
$$
LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

例子:

SELECT add_constraint('client_grant_system_scopes', 'client_grant_system_scopes_pk', 'PRIMARY KEY (client_grants_id, tenant, "scope");');

SELECT add_constraint('client_grant_system_scopes', 'client_grant_system_scopes_fk', 'FOREIGN KEY (tenant,"scope") REFERENCES system_scope(tenant,"scope") ON DELETE CASCADE;');

SELECT add_constraint('jwt_assertion_issuers', 'jwt_assertion_issuers_issuer_key', 'UNIQUE (issuer);');
Run Code Online (Sandbox Code Playgroud)