Ignore constraint if already present on Postgres? [duplicate] - postgresql

Does Postgres have any way to say ALTER TABLE foo ADD CONSTRAINT bar ... which will just ignore the command if the constraint already exists, so that it doesn't raise an error?

A possible solution is to simply use DROP IF EXISTS before creating the new constraint.
ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;
Seems easier than trying to query information_schema or catalogs, but might be slow on huge tables since it always recreates the constraint.
Edit 2015-07-13:
Kev pointed out in his answer that my solution creates a short window when the constraint doesn't exist and is not being enforced. While this is true, you can avoid such a window quite easily by wrapping both statements in a transaction.

This might help, although it may be a bit of a dirty hack:
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'
Then call with:
SELECT create_constraint_if_not_exists(
'foo',
'bar',
'ALTER TABLE foo ADD CONSTRAINT bar CHECK (foobies < 100);')
Updated:
As per Webmut's answer below suggesting:
ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;
That's probably fine in your development database, or where you know you can shut out the apps that depend on this database for a maintenance window.
But if this is a lively mission critical 24x7 production environment you don't really want to be dropping constraints willy nilly like this. Even for a few milliseconds there's a short window where you're no longer enforcing your constraint which may allow errant values to slip through. That may have unintended consequences leading to considerable business costs at some point down the road.

You can use an exception handler inside an anonymous DO block to catch the duplicate object error.
DO $$
BEGIN
BEGIN
ALTER TABLE foo ADD CONSTRAINT bar ... ;
EXCEPTION
WHEN duplicate_table THEN -- postgres raises duplicate_table at surprising times. Ex.: for UNIQUE constraints.
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

you can run query over pg_constraint table to find constraint exists or not.like:
SELECT 1 FROM pg_constraint WHERE conname = 'constraint_name'"

Creating constraints can be an expensive operation on a table containing lots of data so I recommend not dropping constraints only to immediately create them again immediately after - you only want to create that thing once.
I chose to solve this using an anonymous code block, very similar to Mike Stankavich, however unlike Mike (who catches an error) I first check to see if the constraint exists:
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$$;

Using information_schema.constraint_column_usage to check for the constraint doesn't work for foreign keys. I use pg_constraint to check for primary keys, foreign keys or unique constraints:
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;
Examples:
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);');

Take advantage of regclass to reduce verbosity, increase performance, and avoid errors related to table naming clashes between schemas:
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 $$;
This will also work for tables in other schemas, e.g.:
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 $$;

In psql You can use metacommand \gexec for run generated query.
SELECT 'ALTER TABLE xx ADD CONSTRAINT abc' WHERE not EXISTS (SELECT True FROM pg_constraint WHERE conname = 'abc') \gexec

For me those solutions didn't work because the constraint was a primary key.
This one worked for me:
ALTER TABLE <table.name> DROP CONSTRAINT IF EXISTS <constraint.name> CASCADE;

Considering all the above mentioned answers , the below approach help if you just want to check if a constraint exist in the table in which you are trying to insert and raise a notice if there happens to be one
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
$$;

Don't know why so many lines of code ?
-- SELECT "Column1", "Column2", "Column3" , count(star) FROM dbo."MyTable" GROUP BY "Column1" , "Column2" , "Column3" HAVING count(*) > 1;
alter table dbo."MyTable" drop constraint if exists "MyConstraint_Name" ;
ALTER TABLE dbo."MyTable" ADD CONSTRAINT "MyConstraint_Name" UNIQUE("Column1", "Column3", "Column2");

Related

Delete selected tables from Postgres sql

I need to remove selected tables from the Postgres SQL. It would better to use like or where clause.
Like I have
TABLE_A
TABLE_B
TABLE_C
-
-
-
TABLE_N
I need to delete
TABLE_A to TABLE_X
Can be done with a single command, which is faster - in case this is a recurring task.
Add IF EXISTS if the existence of any tables is uncertain. This way we save an extra trip to the system catalogs (information_schema.tables or pg_catalog.pg_tables).
And you may want to add CASCADE:
DO
$do$
BEGIN
-- child safety device: quote RAISE instead of EXECUTE to prime the bomb
-- EXECUTE (
RAISE NOTICE '%', (
SELECT 'DROP TABLE IF EXISTS'
|| string_agg('table_' || chr(ascii('a') + g) , ', ')
|| ' CASCADE;'
FROM generate_series(0,13) g
);
END
$do$;
Generates a command of the form:
DROP TABLE IF EXISTS table_a, table_b, ... , table_n CASCADE;
Using generate_series() to generate requested table names. More here:
How to drop many (but not all) tables in one fell swoop?
How to check if a table exists in a given schema
DO
$$
DECLARE
r RECORD;
BEGIN
FOR r IN SELECT oid::REGCLASS table_name
FROM pg_class
WHERE relname <= 'table_x'
AND relkind = 'r'
LOOP
EXECUTE 'DROP TABLE' || r.table_name;
END LOOP;
END;
$$ LANGUAGE plpgsql;

Using PL/pgSQL conditionals without creating a function

I would like to create a Postgres table and alter it, but only if it does not exist in the first place. I don't think this should be a function, as this is ran only once during the app startup. I tried to execute this code from pg-promise nodejs lib using client.none(sql, params), but it gave me an error syntax error at or near "IF", so I'm guessing it has to be some other execution method, or my SQL is fundamentally flawed. Thanks!
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = $1 AND table_name = $2
) THEN
CREATE TABLE $2~ (zoom smallint, idx bigint, tile bytea, PRIMARY KEY (zoom, idx));
ALTER TABLE $2~ ALTER COLUMN tile SET STORAGE EXTERNAL;
END IF;
END;
Your code is almost there
do $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = $1 AND table_name = $2
) THEN
CREATE TABLE $2~ (zoom smallint, idx bigint, tile bytea, PRIMARY KEY (zoom, idx));
ALTER TABLE $2~ ALTER COLUMN tile SET STORAGE EXTERNAL;
END IF;
END $$;

How can i Alter TABLE all column from not null to null by one query of PostgreSQL 9.1

I migrate my data from MySQL Database to PostgreSQL Database and by mistaken i have all my column set not-null in PostgreSQL database.
After this i am facing issue for inserting data and have to uncheck not null manually, so is there any way to do for all column in table ( except id(PRIMARY KEY) ).
i have this query also for single column but its also time consuming,
ALTER TABLE <table name> ALTER COLUMN <column name> DROP NOT NULL;
I don't think there is built in functionality for this. But it's easy to write a function to do this. For example:
CREATE OR REPLACE FUNCTION set_nullable(relation TEXT)
RETURNS VOID AS
$$
DECLARE
rec RECORD;
BEGIN
FOR rec IN (SELECT * FROM pg_attribute
WHERE attnotnull = TRUE AND attrelid=relation::regclass::oid AND attnum > 0 AND attname != 'id')
LOOP
EXECUTE format('ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL', relation, rec.attname);
RAISE NOTICE 'Set %.% nullable', relation, rec.attname;
END LOOP;
END
$$
LANGUAGE plpgsql;
Use it like this:
SELECT set_nullable('my_table');
Just perform a new CREATE TABLE noNULL using the same structure as your current table and modify the NULLable field you need.
Then insert from old table
INSERT INTO noNULL
SELECT *
FROM oldTable
then delete oldTable and rename noNull -> oldTable

Alter table across all postgresql schemas

If I have several schemas that contain the same table, is there a way for me to make an update to all the tables at once? For example, if I have 3 schemas that each have a user table with columns first_name, last_name, email and I want to add a column for phone_num for each user table in all 3 schemas, is there a way i can do it? I could not find a way in the postgresql docs...
Thanks in advance!
I think you want to alter table and not to update the table. If yes then below code will work for you,
-- Function: alter_table()
-- DROP FUNCTION alter_table();
CREATE OR REPLACE FUNCTION alter_table()
RETURNS integer AS
$BODY$
DECLARE
v_schema text;
BEGIN
FOR v_schema IN
SELECT quote_ident(nspname)
FROM pg_namespace n
WHERE nspname !~~ 'pg_%'
AND nspname <> 'information_schema'
LOOP
EXECUTE 'SET LOCAL search_path = ' || v_schema;
ALTER TABLE "user" ADD COLUMN show_price boolean NOT NULL DEFAULT TRUE;
END LOOP;
return 1;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION alter_table()
OWNER TO postgres;

PostgreSQL drop constraint with unknown name

I have an SQL script that needs to drop several constraints and restore them at the end, but the constraint names are auto-generated and will be different each time the script is run.
I know how to get the constraint name from the table names, but it doesn't seem possible to use this information in the drop statement.
select conname from pg_constraint where
conrelid = (select oid from pg_class where relname='table name')
and confrelid = (select oid from pg_class where relname='reference table');
alter table something drop constraint (some subquery) is a syntax error.
Ideally I would like to get the constraint name and store it in a variable, but it doesn't seem that Postgres supports that and I can't make it work with psql \set.
Is this even possible?
To dynamically drop & recreate a foreign key constraint, you could wrap it all in a function or use the DO command:
DO
$body$
DECLARE
_con text := (
SELECT quote_ident(conname)
FROM pg_constraint
WHERE conrelid = 'myschema.mytable'::regclass
AND confrelid = 'myschema.myreftable'::regclass
LIMIT 1 -- there could be multiple fk constraints. Deal with it ...
);
BEGIN
EXECUTE '
ALTER TABLE wuchtel12.bet DROP CONSTRAINT ' || _con;
-- do stuff here
EXECUTE '
ALTER TABLE myschema.mytable
ADD CONSTRAINT ' || _con || ' FOREIGN KEY (col)
REFERENCES myschema.myreftable (col)';
END
$body$
You must own the table to use ALTER TABLE.
Else you can create a function with LANGUAGE plpgsql SECURITY DEFINER (using the same body) and
ALTER FUNCTION foo() OWNER TO postgres;
postgres being a superuser here - or the owner of the table.
But be sure to know what the manual has to say about security.
The manual also has more on dynamic commands.
You can use stored procedure also.
CREATE OR REPLACE PROCEDURE public.p_costraint()
LANGUAGE plpgsql
AS $procedure$
DECLARE _constrint text;
begin
-- for dynamic change the constraint.
_constrint := (
SELECT quote_ident(conname)
FROM pg_constraint
WHERE conrelid = 'test.contacts'::regclass
AND confrelid = 'test.customers'::regclass
LIMIT 1 -- there could be multiple fk constraints. Deal with it ...
);
_constrint := _constrint || 'test';
EXECUTE '
ALTER TABLE test.contacts
ADD CONSTRAINT ' || _constrint || ' FOREIGN KEY (customer_id)
REFERENCES test.customers (customer_id)';
RAISE NOTICE 'hello, world!';
end
$procedure$;
In here. constraint name is used as a text variable.
You can just call it: call public.p_costraint();
It will return :
NOTICE: hello, world!
CALL