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


Answers:


49

这可能有所帮助,尽管可能有点脏:

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'

然后致电:

SELECT create_constraint_if_not_exists(
        'foo',
        'bar',
        'ALTER TABLE foo ADD CONSTRAINT bar CHECK (foobies < 100);')

更新:

根据Webmut的以下建议:

ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;

在您的开发数据库中,或者在您知道可以关闭依赖该数据库的应用程序作为维护时段的情况下,这可能很好。

但是,如果这是一个关键任务的24x7全天候生产环境,那么您真的不想像这样随意放弃约束。即使是几毫秒,也有一个很短的窗口,您不再需要执行约束,这可能会使错误的值漏掉。这可能会带来意想不到的后果,从而在将来的某些时候导致可观的业务成本。


我认为'myconstraint'应该'bar'在您的最后一个示例中。
Denis de Bernardy 2011年

2
我将进一步修改此答案,以便execute语句为,execute 'ALTER TABLE ' || t_name || ' ADD CONSTRAINT ' || c_name || ' ' || constraint_sql;然后调用该函数将类似于SELECT create_constraint_if_not_exists('foo', 'bar', 'CHECK (foobies < 100);');。这可以确保您不会弄乱约束SQL中的参数,因为它们基于原始参数。
Tim Mattison 2014年

我完全同意@TimMattison的观点,但请进一步加上“ ||” ';'`在execute句子的末尾(在之前;),这样我就可以消除;调用函数时自变量的结尾。
Masa Sakano,

4
通过使用事务删除短窗口。
OrangeDog

这不会找到任何外键约束,但是可以从中选择information_schema.table_constraints
iElectric

91

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

ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;

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

编辑2015-07-13:Kev在回答中指出,当约束不存在且未强制执行时,我的解决方案会创建一个短窗口。确实如此,但是您可以通过将两个语句包装在事务中来轻松地避免这样的窗口。


5
我知道这个线程很旧,但是也许值得指出的是,事务通常适用于DML,而alter table命令是DDL。DDL命令通常无法回滚(取决于数据库)。话虽如此,Postgres确实支持回滚DDL,而Oracle和MySQL支持。有关特定于DB的详细信息
请参见此

2
我建议不要编写只删除约束以重新创建约束的代码,因为Webmut指出在大型表上这样做可能会很慢。不要冒险,而是先检查约束是否存在,然后在另一个回答中说明该问题。
jamiet

如果该语句需要可重复,则会遇到问题。例如,您首先像这样创建约束A,然后创建约束B,而约束B某种程度上取决于约束A。之后,您必须重新运行包含约束A创建的语句-它将引发错误:由于B不能删除A取决于它。
cis

24

您可以在匿名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 $$;

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


3
必须更改duplicate_objectduplicate_table(代码42P07)。Postgres 9.6
volvpavl

Postgres 10.1已将其还原到duplicate_object
Tanktalus

3
duplicate_objectFOREIGN KEYduplicate_table对于UNIQUE限制。Postgres 9.6.8
dfritch,

11

您可以在pg_constraint表上运行查询以查找约束是否存在,例如:

SELECT 1 FROM pg_constraint WHERE conname = 'constraint_name'"

4
IF EXISTS对于DROP CONSTRAINTAFAIK,有一个选项,但对没有ADD CONSTRAINT
亩太短了

1
约束名称是否在表本地?如果有两个约束名为的表会constraint_name怎样?
guettli 2014年

是的,如果另一个表具有相同的约束名称,则可能会产生误报。但是,如果您可以100%掌控自己的命名,那么这仍然是一个不错的解决方案。
乔M

10

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

我选择使用与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$$; 

@All-以我为例,约束是从JPA创建的,并且这些名称不存在constraint_schema
Pra_A

2
我不得不information_schema.constraint_column_usage改用它,这使我可以控制可以读取的名称以及表名称。
CTS_AE

-2

考虑到上面提到的所有答案,如果您只想检查要插入的表中是否存在约束,并且如果碰巧遇到一个约束,则以下方法会有所帮助

DO 
$$ BEGIN
IF NOT EXISTS (select constraint_name 
               from information_schema.table_constraints 
               where table_schema='schame_name' and upper(table_name) = 
upper('table_name')  and upper(constraint_name) = upper('constraint_name'))

THEN

   ALTER TABLE TABLE_NAME ADD CONSTRAINT CONTRAINT_NAME..... ;

ELSE raise NOTICE 'Constraint CONTRAINT_NAME already exists in Table TABLE_NAME';   

END IF;
END
$$;

请问拒绝投票的原因是什么?
charle819

1
我认为它之所以遭到否决,是因为它看起来完全像Jamiet的回答。它不会为现有答案增加任何有价值的东西。
Sorrel Vesper

-4

不知道为什么会有如此多的代码行?

-从dbo中选择“ Column1”,“ Column2”,“ Column3”,count(star)。“ MyTable” GROUP BY“ Column1”,“ Column2”,“ Column3”具有count(*)> 1;

更改表dbo。如果存在“ MyConstraint_Name”,则“ MyTable”放置约束;

ALTER TABLE dbo。“ MyTable”添加约束“ MyConstraint_Name” UNIQUE(“ Column1”,“ Column3”,“ Column2”);

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.