Generate SQL to update primary key - sql

I want to change a primary key and all table rows which reference to this value.
# table master
master_id|name
===============
foo|bar
# table detail
detail_id|master_id|name
========================
1234|foo|blu
If I give a script or function
table=master, value-old=foo, value-new=abc
I want to create a SQL snippet that executes updates on all tables which refere to table "master":
update detail set master_id=value-new where master_id=value-new;
.....
With the help of introspection, this should be possible.
I use postgres.
Update
The problem is, that there are many tables which have a foreign-key to the table "master". I want a way to automatically update all tables which have a foreign-key to master table.

The easiest way to deal with primary key changes - by far - is to ALTER your referring foreign key constraints to be ON UPDATE CASCADE.
You are then free to update the primary key values, and the changes will cascade to child tables. It can be a very slow process due to all the random I/O, but it will work.
You do need to watch out not to violate uniqueness constraints on the primary key column during the process.
A fiddlier but faster way is to add a new UNIQUE column for the new PK, populate it, add new columns to all the referring tables that point to the new PK, drop the old FK constraints and columns, then finally drop the old PK.

If you need to change PK you could use DEFFERED CONSTRAINTS:
SET CONSTRAINTS sets the behavior of constraint checking within the current transaction. IMMEDIATE constraints are checked at the end of each statement. DEFERRED constraints are not checked until transaction commit. Each constraint has its own IMMEDIATE or DEFERRED mode.
Data preparation:
CREATE TABLE master(master_id VARCHAR(10) PRIMARY KEY, name VARCHAR(10));
INSERT INTO master(master_id, name) VALUES ('foo', 'bar');
CREATE TABLE detail(detail_id INT PRIMARY KEY, master_id VARCHAR(10)
,name VARCHAR(10)
,CONSTRAINT fk_det_mas FOREIGN KEY (master_id) REFERENCES master(master_id));
INSERT INTO detail(detail_id, master_id, name) VALUES (1234,'foo','blu');
In normal situtation if you try to change master detail you will end up with error:
update detail set master_id='foo2' where master_id='foo';
-- ERROR: insert or update on table "detail" violates foreign key
-- constraint "fk_det_mas"
-- DETAIL: Key (master_id)=(foo2) is not present in table "master"
update master set master_id='foo2' where master_id='foo';
-- ERROR: update or delete on table "master" violates foreign key
-- constraint "fk_det_mas" on table "detail"
-- DETAIL: Key (master_id)=(foo) is still referenced from table "detail".
But if you change FK resolution to deffered, there is no problem:
ALTER TABLE detail DROP CONSTRAINT fk_det_mas ;
ALTER TABLE detail ADD CONSTRAINT fk_det_mas FOREIGN KEY (master_id)
REFERENCES master(master_id) DEFERRABLE;
BEGIN TRANSACTION;
SET CONSTRAINTS ALL DEFERRED;
UPDATE master set master_id='foo2' where master_id = 'foo';
UPDATE detail set master_id='foo2' where master_id = 'foo';
COMMIT;
DBFiddle Demo
Please note that you could do many things inside transaction, but during COMMIT all referential integrity checks have to hold.
EDIT
If you want to automate this process you could use dynamic SQL and metadata tables. Here Proof of Concept for one FK column:
CREATE TABLE master(master_id VARCHAR(10) PRIMARY KEY, name VARCHAR(10));
INSERT INTO master(master_id, name)
VALUES ('foo', 'bar');
CREATE TABLE detail(detail_id INT PRIMARY KEY, master_id VARCHAR(10),
name VARCHAR(10)
,CONSTRAINT fk_det_mas FOREIGN KEY (master_id)
REFERENCES master(master_id)DEFERRABLE ) ;
INSERT INTO detail(detail_id, master_id, name) VALUES (1234,'foo','blu');
CREATE TABLE detail_second(detail_id INT PRIMARY KEY, name VARCHAR(10),
master_id_second_name VARCHAR(10)
,CONSTRAINT fk_det_mas_2 FOREIGN KEY (master_id_second_name)
REFERENCES master(master_id)DEFERRABLE ) ;
INSERT INTO detail_second(detail_id, master_id_second_name, name)
VALUES (1234,'foo','blu');
And code:
BEGIN TRANSACTION;
SET CONSTRAINTS ALL DEFERRED;
DO $$
DECLARE
old_pk TEXT = 'foo';
new_pk TEXT = 'foo2';
table_name TEXT = 'master';
BEGIN
-- update childs
EXECUTE (select
string_agg(FORMAT('UPDATE %s SET %s = ''%s'' WHERE %s =''%s'' ;'
,c.relname,pa.attname, new_pk,pa.attname, old_pk),CHR(13)) AS sql
from pg_constraint pc
join pg_class c on pc.conrelid = c.oid
join pg_attribute pa ON pc.conkey[1] = pa.attnum
and pa.attrelid = pc.conrelid
join pg_attribute pa2 ON pc.confkey[1] = pa2.attnum
and pa2.attrelid = table_name::regclass
where pc.contype = 'f');
-- update parent
EXECUTE ( SELECT FORMAT('UPDATE %s SET %s = ''%s'' WHERE %s =''%s'';'
,c.relname,pa.attname, new_pk,pa.attname, old_pk)
FROM pg_constraint pc
join pg_class c on pc.conrelid = c.oid
join pg_attribute pa ON pc.conkey[1] = pa.attnum
and pa.attrelid = pc.conrelid
WHERE pc.contype IN ('p','u')
AND conrelid = table_name::regclass
);
END
$$;
COMMIT;
DBFiddle Demo 2
EDIT 2:
I tried it, but it does not work. It would be nice, if the script could show the SQL. This is enough. After looking at the generated SQL I can execute it if psql -f
have you tried it? It did not work for me.
Yes, I have tried it. Just check above live demo links.
I prepare the same demo with more debug info:
values before
executed SQL
values after
Please make sure that FKs are defined as DEFFERED.
DBFiddle 2 with debug info
LAST EDIT
Then I wanted to see the sql instead of executing it. I removed "perform" from your fiddle, but then I get an error. See: http://dbfiddle.uk/?rdbms=postgres_10&fiddle=b9431c8608e54b4c42b5dbd145aa1458
If you only want to get SQL code you could create function:
CREATE FUNCTION generate_update_sql(table_name VARCHAR(100), old_pk VARCHAR(100), new_pk VARCHAR(100))
RETURNS TEXT
AS
$$
BEGIN
RETURN
-- update childs
(SELECT
string_agg(FORMAT('UPDATE %s SET %s = ''%s'' WHERE %s =''%s'' ;', c.relname,pa.attname, new_pk,pa.attname, old_pk),CHR(13)) AS sql
FROM pg_constraint pc
JOIN pg_class c on pc.conrelid = c.oid
JOIN pg_attribute pa ON pc.conkey[1] = pa.attnum and pa.attrelid = pc.conrelid
JOIN pg_attribute pa2 ON pc.confkey[1] = pa2.attnum and pa2.attrelid = table_name::regclass
WHERE pc.contype = 'f') || CHR(13) ||
-- update parent
(SELECT FORMAT('UPDATE %s SET %s = ''%s'' WHERE %s =''%s'';', c.relname,pa.attname, new_pk,pa.attname, old_pk)
FROM pg_constraint pc
JOIN pg_class c on pc.conrelid = c.oid
JOIN pg_attribute pa ON pc.conkey[1] = pa.attnum and pa.attrelid = pc.conrelid
WHERE pc.contype IN ('p','u')
AND conrelid = table_name::regclass)
;
END
$$ LANGUAGE plpgsql;
And execution:
SELECT generate_update_sql('master', 'foo', 'foo');
UPDATE detail SET master_id = 'foo' WHERE master_id ='foo' ;
UPDATE detail_second SET master_id_second_name = 'foo'
WHERE master_id_second_name ='foo' ;
UPDATE master SET master_id = 'foo' WHERE master_id ='foo';
DBFiddle Function Demo
Of course there is a place for improvement for example handling identifiers like "table with space in name" and so on.

I found a dirty solution: in psql the command \d master_table show the relevant information. With some text magic, it is possible to extract the needed information:
echo "UPDATE master_table SET id='NEW' WHERE id='OLD';" > tmp/foreign-keys.txt
psql -c '\d master_table' | grep -P 'TABLE.*CONSTRAINT.*FOREIGN KEY' \
>> tmp/foreign-keys.txt
reprec '.*TABLE ("[^"]*") CONSTRAINT[^(]*\(([^)]*)\).*' \
"UPDATE \1 set \2='NEW' WHERE \2='OLD';" \
tmp/foreign-keys.txt
psql -1 -f tmp/foreign-keys.txt
Result:
UPDATE "master_table" SET id='NEW' WHERE id='OLD';
UPDATE "other_table" SET master_id='NEW' WHERE master_id='OLD';
...
But better solutions are welcome.

I dont think you can update the Primary key. One possible work around is that you can remove the primary key constraint from the table column. Then update the column value.
Updating the primary key can lead you to some serious problems. But if you still want to do it.
Please refer this Thread.(kevchadders has given a solution.)

Related

Key is not present in table

table description
# \d invites;
Table "public.invites"
Column | Type | Modifiers
-----------------------+-----------------------------+---------------------
id | integer | not null default
email | character varying |
key | character varying |
sender_user_id | integer | not null
receiver_user_id | integer |
Indexes:
"invites_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"fk_invites_receiver_user_id"
FOREIGN KEY (receiver_user_id) REFERENCES users(id)
"fk_invites_sender_user_id"
FOREIGN KEY (sender_user_id) REFERENCES users(id)
you can see Foreighn Key "fk_invites_receiver_user_id" FOREIGN KEY (receiver_user_id) REFERENCES users(id).
But the records for user with pk is absent in the parent table, where in the reference table fk is exists.
# select id from users where id = 958;
id
----
(0 rows)
select count(*) from invites where receiver_user_id = 958;
count
-------
1
(1 row)
the question is how it can be, simple way to fix the conflicts is delete wrong records, but want to exclude such situation in the future, and when i try to restore the data there is an error:
DETAIL: Key (receiver_user_id)=(958) is not present in table "users".
Command was: ALTER TABLE ONLY invites
ADD CONSTRAINT fk_invites_receiver_user_id
FOREIGN KEY (receiver_user_id) REFERENCES users(id);
P.S.
database=# select version();
version
------------------------------------------------------------------------
PostgreSQL 9.4.14 on x86_64-unknown-linux-gnu,
compiled by gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4, 64-bit
Create fail
-- tmp schema
-- \i tmp.sql
-- tables
CREATE TABLE users
(id serial NOT NULL PRIMARY KEY
, name text
);
CREATE TABLE refs
( user_from INTEGER NOT NULL
, user_to INTEGER NOT NULL
, msg text
);
-- data
INSERT INTO users(name) VALUES ('Alice'), ('Bob');
INSERT INTO refs VALUES (1,2), (1,3);
-- FK constraints
ALTER TABLE refs
ADD CONSTRAINT bad_user_from
FOREIGN KEY (user_from) references users(id);
ALTER TABLE refs
ADD CONSTRAINT bad_user_to
FOREIGN KEY (user_to) references users(id)
NOT VALID ; -- <<--HERE
-- This should fail; user=3 does not exist
INSERT INTO refs VALUES (3,2), (2,3);
Repair
-- "repair" the broken refs (by introducing a dummy row)
INSERT INTO users(id,name) VALUES (0, 'NoName');
-- Make the bad FKs point to the dummy row
UPDATE refs r
SET user_to =0
WHERE NOT EXISTS(
SELECT *
FROM users u
WHERE u.id= r.user_to
);
UPDATE refs r
SET user_from =0
WHERE NOT EXISTS(
SELECT *
FROM users u
WHERE u.id= r.user_from
);
SELECT * FROM refs r
JOIN users u0 ON u0.id= r.user_from
JOIN users u1 ON u1.id= r.user_to
;
INSERT INTO users(name) VALUES ('NewName');
-- see what we'v got
SELECT * FROM users;
SELECT* FROM refs r
JOIN users u0 ON u0.id= r.user_from
JOIN users u1 ON u1.id= r.user_to;
Validate constraint
\d refs
\d users
-- enforce the constraint
ALTER TABLE refs
VALIDATE CONSTRAINT bad_user_to; -- <<-- HERE
-- check if valid
\d refs
\d users
-- This should not fail; user=3 does exist now
INSERT INTO refs VALUES (3,2), (2,3);
The answer is unfortunately “data corruption”.
You should delete the offending row, dump the database with pg_dumpall and restore it into a fresh cluster created with initdb.
You should also try to find out what caused the data corruption. Did you have any crashes recently? Could it be that you have unreliable storage or hardware problems? Anything weird in the database log?
To exclude such problems as much as possible, always use the latest fixpack for your PostgreSQL version and use reliable hardware.

How to delete/update records inside trigger based on the updated/deleted row?

I have a table with the following format
id | name | supervisor_id
I made a "BEFORE INSERT" trigger that checks if the supervisor_id exists in the id column and if not, then assign a null value to the supervisor_id.
I am trying to write two more triggers. One that checks if the supervisor_id exists in the id column before each update of the supervisor_id, and one that sets the supervisor_id to NULL for each employee if his supervisor is deleted.
This is my code, of course it's not working, help please.
CREATE OR REPLACE TRIGGER EAP_users_TRG3
AFTER DELETE
ON EAP_users
FOR EACH ROW
DECLARE
d NUMBER;
BEGIN
SELECT id INTO d FROM EAP_users WHERE id = :OLD.id;
UPDATE EAP_users SET supervisor = NULL WHERE supervisor = d;
END;
/
This is the "working" trigger:
CREATE OR REPLACE TRIGGER EAP_users_TRG1
BEFORE INSERT
ON EAP_users
FOR EACH ROW
DECLARE
supervisor EAP_users.supervisor%TYPE;
CURSOR supervisor_CUR IS SELECT idFROM EAP_users;
b BOOLEAN := FALSE;
BEGIN
IF ( :NEW.supervisor IS NOT NULL ) THEN
FOR s IN supervisor_CUR LOOP
IF ( :NEW.supervisor = s.id ) THEN
b := TRUE;
END IF;
END LOOP;
IF (b = FALSE) THEN
:NEW.supervisor := NULL;
END IF;
END IF;
END;
/
According to the definition of your problem, you are trying to enforce referential integrity of your data. In that case, a trigger is probably not the right tool. To quote Oracle's documentation:
You can use both triggers and integrity constraints to define and enforce any type of integrity rule. However, Oracle strongly recommends that you use triggers to constrain data input only in the following situations:
[...]
When a required referential integrity rule cannot be enforced using the following integrity constraints:
NOT NULL, UNIQUE
PRIMARY KEY
FOREIGN KEY
CHECK
DELETE CASCADE
DELETE SET NULL
In that particular case you should use FOREIGN KEY constraint using the DELETE SET NULL modifier. Assuming you have an index on id, all you need is:
ALTER TABLE EAP_users
ADD CONSTRAINT EAP_users_supervisor_cst
FOREIGN KEY (supervisor_id)
REFERENCES EAP_users(id)
ON DELETE SET NULL;
This simple referential integrity constraint will perform probably better the same things as your 3 triggers -- namely:
prevent insert/update with a non existing (non-NULL) supervisor_id
set all supervisor_id to NULL when you delete the supervisor
See http://sqlfiddle.com/#!4/1f8fb/1 for a live example.

Drop a Constraint doesn’t work

I am getting following error when deleting a column with constraint in SQL Server 2005.
The object 'DF__PlantRecon__Test' is dependent on column 'Test'.
The column is not part of any key. But it has a default constraint and the constraint has a pre-defined name.
Though I have written code to delete constraint first, it is not working.
Why doesn't it work?
What need to be done to make it working?
Note: I need to check whether the constraint exist first.
REFERENCES
Named CONSTRAINT benefits
How to drop column with constraint?
How to drop SQL default constraint without knowing its name?
CODE
IF OBJECT_ID('DF__PlantRecon__Test', 'C') IS NOT NULL
BEGIN
SELECT 'EXIST'
ALTER TABLE [dbo].[PlantReconciliationOptions] drop constraint DF__PlantRecon__Test
END
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS WHERE CONSTRAINT_NAME ='DF__PlantRecon__Test')
BEGIN
SELECT 'EXIST'
--drop constraint
ALTER TABLE [dbo].[PlantReconciliationOptions] drop constraint DF__PlantRecon__Test
END
IF EXISTS ( SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'[dbo].[PlantReconciliationOptions]') AND name = 'Test')
BEGIN
--drop column
ALTER TABLE [dbo].[PlantReconciliationOptions] DROP COLUMN Test
END
ALTER TABLE PlantReconciliationOptions
ADD Test INT NOT NULL
CONSTRAINT DF__PlantRecon__Test DEFAULT 30
try
IF OBJECT_ID('DF__PlantRecon__Test') IS NOT NULL
BEGIN
SELECT 'EXIST'
ALTER TABLE [dbo].[PlantReconciliationOptions] drop constraint DF__PlantRecon__Test
END
In your example, you were looking for a 'C' Check Constraint, which the DEFAULT was not. You could have changed the 'C' for a 'D' or omit the parameter all together.
If you are consistent with your naming, as it appears you are, (e.g. DF__xxx) then dropping the second parameter is an acceptable choice to be sure that the hard coded constraint name is dropped.
Here is a list of the OBJECT_ID() Object Types you can pass:
AF = Aggregate function (CLR)
C = CHECK constraint
D = DEFAULT (constraint or stand-alone)
F = FOREIGN KEY constraint
PK = PRIMARY KEY constraint
P = SQL stored procedure
PC = Assembly (CLR) stored procedure
FN = SQL scalar function
FS = Assembly (CLR) scalar function
FT = Assembly (CLR) table-valued function
R = Rule (old-style, stand-alone)
RF = Replication-filter-procedure
S = System base table
SN = Synonym
SQ = Service queue
TA = Assembly (CLR) DML trigger
TR = SQL DML trigger
IF = SQL inline table-valued function
TF = SQL table-valued-function
U = Table (user-defined)
UQ = UNIQUE constraint
V = View
X = Extended stored procedure
IT = Internal table
(This list was found on Beyond Relational: Using TSQL Function: OBJECT_ID())
Steps to solve your problem.
Open SSMS.
Edit the table to remove the default. Do not press save
Script the output to "new window".
Read the script.

Updating foreign keys while inserting into new table

I have table A(id).
I need to
create table B(id)
add a foreign key to table A that references to B.id
for every row in A, insert a row in B and update A.b_id with the newly inserted row in B
Is it possible to do it without adding a temporary column in B that refers to A? The below does work, but I'd rather not have to make a temporary column.
alter table B add column ref_id integer references(A.id);
insert into B (ref_id) select id from A;
update A set b_id = B.id from B where B.ref_id = A.id;
alter table B drop column ref_id;
Assuming that:
1) you're using postgresql 9.1
2) B.id is a serial (so actually an int with a default value of nextval('b_id_seq')
3) when inserting to B, you actually add other fields from A otherwise the insert is useless
...I think something like this would work:
with n as (select nextval('b_id_seq') as newbid,a.id as a_id from a),
l as (insert into b(id) select newbid from n returning id as b_id)
update a set b_id=l.b_id from l,n where a.id=n.a_id and l.b_id=n.newbid;
Add the future foreign key column, but without the constraint itself:
ALTER TABLE A ADD b_id integer;
Fill the new column with values:
WITH cte AS (
SELECT
id
ROW_NUMBER() OVER (ORDER BY id) AS b_ref
FROM A
)
UPDATE A
SET b_id = cte.b_ref
FROM cte
WHERE A.id = cte.id;
Create the other table:
CREATE TABLE B (
id integer CONSTRAINT PK_B PRIMARY KEY
);
Add rows to the new table using the referencing column of the existing one:
INSERT INTO B (id)
SELECT b_id
FROM A;
Add the FOREIGN KEY constraint:
ALTER TABLE A
ADD CONSTRAINT FK_A_B FOREIGN KEY (b_id) REFERENCES B (id);
PostgeSQL dialect.
You might use an anonymous code block like this
do $$
declare
category_cursor cursor for select id from schema1.categories;
r_category bigint;
setting_id bigint;
begin
open category_cursor;
loop fetch category_cursor into r_category;
exit when not found;
insert into schema2.setting(field)
values ('field_value') returning id into setting_id;
update schema1.categories set category_setting_id = setting_id
where category_id = r_category;
end loop;
end; $$
Let assume we have two tables first - categories, second - settings which must be applied to these categories.
First step - declare cursor(collect ids from categories), and variabels where we store temporary data
Loop cursor inserting values 'field_value' into settings
Store id in variable setting_id
Update table categories with setting_id

Postgres: Add constraint if it doesn't already exist

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");