如果约束已经存在,Postgres有没有办法说ALTER TABLE foo ADD CONSTRAINT bar ...
只会忽略命令,这样它就不会引发错误?
答案 0 :(得分:59)
一种可能的解决方案是在创建新约束之前简单地使用DROP IF EXISTS。
ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;
似乎比尝试查询information_schema或目录更容易,但在巨大的表上可能会很慢,因为它总是重新创建约束。
编辑2015-07-13: Kev在his answer中指出,当约束不存在且未被强制执行时,我的解决方案会创建一个短窗口。虽然这是事实,但是通过将两个语句包装在事务中,可以很容易地避免这样的窗口。
答案 1 :(得分:28)
这可能会有所帮助,虽然这可能有点肮脏:
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's answer建议:
ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;
这在您的开发数据库中可能很好,或者您知道可以在维护窗口中关闭依赖此数据库的应用程序。
但是,如果这是一个生动的任务关键的24x7生产环境,那么你真的不想在这样的情况下放弃限制。即使是几毫秒,也有一个短窗口,您不再强制执行约束,这可能允许错误的值滑过。这可能会产生意想不到的后果,导致在未来的某些时候出现相当大的商业成本。
答案 2 :(得分:17)
您可以在匿名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 :(得分:9)
您可以在pg_constraint
表上运行查询以查找是否存在约束。似乎:
SELECT 1 FROM pg_constraint WHERE conname = 'constraint_name'"
答案 4 :(得分:4)
在包含大量数据的表上创建约束可能是一项昂贵的操作,因此我建议不要仅删除约束,以便在之后立即再次创建它们 - 您只想创建一次。
我选择使用匿名代码块来解决这个问题,与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$$;
答案 5 :(得分:1)
在 psql 中,您可以使用元命令 \gexec 来运行生成的查询。
SELECT 'ALTER TABLE xx ADD CONSTRAINT abc' WHERE not EXISTS (SELECT True FROM pg_constraint WHERE conname = 'abc') \gexec
答案 6 :(得分:1)
使用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;
示例:
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);');
答案 7 :(得分:-1)
考虑到上面提到的所有答案,如果您只想检查要插入的表中是否存在约束,并在碰巧出现一个约束时发出通知,则以下方法会有所帮助
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
$$;
答案 8 :(得分:-3)
不知道为什么这么多行代码?
- SELECT&#34; Column1&#34;,&#34; Column2&#34;,&#34; Column3&#34; ,计数(明星)来自dbo。&#34; MyTable&#34; GROUP BY&#34; Column1&#34; ,&#34; Column2&#34; ,&#34; Column3&#34;有计数(*)&gt; 1;
改变表dbo。&#34; MyTable&#34; drop constraint如果存在&#34; MyConstraint_Name&#34; ;
ALTER TABLE dbo。&#34; MyTable&#34;添加约束&#34; MyConstraint_Name&#34; UNIQUE(&#34; Column1&#34;,&#34; Column3&#34;,&#34; Column2&#34;);