How to use on conflict on constraint in postgres with update query - sql

I want to update "name" column in my database, that has more than 300000 records. The name field has a unique constraint on it
While bulk updating I want to skip the records that are violating the constraint
update "profiles" set name = left(name, -1)
where ---------
ON CONFLICT ON CONSTRAINT profiles_name_key
DO NOTHING
The above query is throwing an error on "ON"
edit
It is not necessary to use ON CONFLICT. Any query that can update the records that doesn't conflict with constraint will work

You cannot get what you are looking for with update ... on conflict - that structure does not exist. But you can get it with a transaction block. While you will get an exception you can intercept it and continue processing. (see demo)
do $$
begin
begin -- nested block
update <table>
set <column> = '<existing value'
where <column> = '<other existing value>';
exception
when others then null;
end; -- nested block
-- other code here will still execute
end ;
$$;

Related

How to substitute a variable when creating a check constraint?

I need to add a required field for newly added rows. However, it is undesirable to set the default value for old rows due to the large size of the table. I need to provide an automated script that will do this.
I tried this, but it does not work:
do $$
declare
max_id int8;
begin
select max(id) into max_id from transactions;
alter table transactions add constraint check_process_is_assigned check (id <= max_id or process_id is not null);
end $$;
Utility commands like ALTER TABLE do not accept parameters. Only the basic DML commands SELECT, INSERT, UPDATE, DELETE do.
See:
set "VALID UNTIL" value with a calculated timestamp
“ERROR: there is no parameter $1” in “EXECUTE .. USING ..;” statement in plpgsql
Creating user with password from variables in anonymous block
You need dynamic SQL like:
DO
$do$
BEGIN
EXECUTE format(
'ALTER TABLE transactions
ADD CONSTRAINT check_process_is_assigned CHECK (id <= %s OR process_id IS NOT NULL)'
, (SELECT max(id) FROM transactions)
);
END
$do$;
db<>fiddle here
This creates a CHECK constraint based on the current maximum id.
Maybe a NOT VALID constraint would serve better? That is not checked against existing rows:
ALTER TABLE transactions
ADD CONSTRAINT check_process_is_assigned CHECK (process_id IS NOT NULL) NOT VALID;
But you do have to "fix" old rows that get updated in this case. (I.e. assign a value to process_id if it was NULL so far.) See:
Enforce NOT NULL for set of columns with a CHECK constraint only for new rows
Best way to populate a new column in a large table?

Executing a PostgreSQL query with an EXCEPTION results in two different ERROR messages

I have an PostgreSQL query that includes a transaction and an exception if a column is duplicated:
BEGIN;
ALTER TABLE "public"."cars"
ADD COLUMN "top_speed" text;
EXCEPTION WHEN duplicate_column THEN NOTHING;
ROLLBACK;
In this query I am trying to add a column that already exists (playing a little bit with exceptions) and if it does then the query shall just ignore it. At the moment I am not really sure if the exception-code I am using is the right (couldn't find a site where they are described; only found this)
My Problem is if I execute this query I get the error-message:
ERROR: column "top_speed" of relation "cars" already exists
And if I execute it a second time the error-message changes to:
ERROR: current transaction is aborted, commands ignored until end of transaction block
Try an anonymous code block. As Laurenz mentioned, you were mixing PL/pgSQL and SQL commands.
Sample table
CREATE TABLE t (f1 TEXT);
Anonymous code block
DO $$
BEGIN
IF (SELECT count(column_name) FROM information_schema.columns
WHERE table_schema = 'public' AND
table_name = 't' AND
column_name = 'f2') = 0 THEN
ALTER TABLE public.t ADD COLUMN "f2" text;
END IF;
END$$;
After execution you have your new column. If the column already exists, it will do nothing.
SELECT * FROM t;
f1 | f2
----+----
0 Zeilen
In PostgreSQL 9.6+ you can use IF NOT EXISTS to check if a given column already exists in the table before creating it:
ALTER TABLE t ADD COLUMN IF NOT EXISTS f2 TEXT;
Code at db<>fiddle

ORA-04091 Error on "after update" trigger, how to get around?

I have a trigger set to fire after an update of a specific column. I want to update the value of a specific column after a specific value from another column changes to another specific value.
I'm getting the errors:
ORA-04091: table tableName is mutating, trigger/function may not see it
ORA-06512: at "triggerName", line 14
ORA-04088: error during execution of trigger 'triggerName'
I've rewritten this as a before update and after update, as well as tried storing the logic in a function, and using "pragma autonomous_transaction", but the error still gets thrown.
BEFORE update of columnName1 ON tableName
FOR EACH ROW
BEGIN
if :new.columnName1 = 3 AND :old.columnName1 = 1 then
update tablename
set columnName2= MOD(sequenceName.NextVal, 5) + 1
where tableName.columnName2 = :old.columnName2;
end if;
END;
/
I don't understand why the entire table is labelled as "mutating" when I am not updating the column affected by the update the trigger is responding to. Surely, you should be able to update the value of a column in a table if another value in the table changes, or am I crazy here?
Note: Only one entry would be affected at a time. In my application logic, I update the status of some person in the database. I want the database to do some logic only on a specific status change, and I want to avoid using API calls for the logic here, as you can see, it is simply one line of logic in PLSQL.
Thanks
Perhaps you don't really want to update the table but just change a value in the specific row being updated.
If so, eschew the update and just set the value:
BEFORE update of columnName1 ON tableName
FOR EACH ROW
BEGIN
if :new.columnName1 = 3 AND :old.columnName1 = 1 then
:new.columnName2 := MOD(sequenceName.NextVal, 5) + 1 ;
end if;
END;

SQL Transactions Error: Can't update table 'todo' in stored function/trigger because it is already used

Yii::app()->db->createCommand("DROP TRIGGER IF EXISTS update_todo;")->execute();
Yii::app()->db->createCommand("CREATE TRIGGER update_todo AFTER DELETE ON user_todo_send FOR EACH ROW BEGIN "
. " UPDATE todo SET status = 1 WHERE id = OLD.id_todo; END;")->execute();
In response, I receive an error :
Can't update table 'todo' in stored function/trigger because it is
already used by statement which invoked this stored function/trigger..
I am going to guess that you are really using MySQL. Use an after delete trigger. Using your syntax, that would look like this (and I assume you have the right delimiter statements:
DROP TRIGGER IF EXISTS `update_todo`;
CREATE TRIGGER `update_todo` AFTER DELETE ON `user_todo_send` FOR EACH ROW
BEGIN
UPDATE `todo`
SET status = 1
WHERE id IN (SELECT OLD.id_todo
FROM user_todo_send
WHERE OLD.id_todo = todo.id
);
END;
This is really simpler to write as:
DROP TRIGGER IF EXISTS `update_todo`;
CREATE TRIGGER `update_todo` AFTER DELETE ON `user_todo_send` FOR EACH ROW
BEGIN
UPDATE `todo`
SET status = 1
WHERE id = OLD.id_todo
END;
Or, forget triggers altogether and add a cascading delete foreign key reference.

Oracle: Modify primary/foreignkeys and constrains inside a TRIGGER

we have an issue with the folowing trigger where we have to disable certain constraints to add the primary key of a table:
create or replace trigger TRG_NAMENSAENDERUNG_MA
after update of vname, nname on mitarbeiter
referencing new as new old as old
for each row
declare
initialien char(2);
benutzernr int;
benutzername_neu char(5);
benutzername_alt char(5);
begin
/*...
Code sets corect values to all variables.
...*/
/* the following is suposed to disable the two constraints*/
for i in (select fk_session_log_ben_name, SESSION_LOGGING FROM USER_CONSTRAINTS) loop
execute immediate 'ALTER TABLE'||i.session_logging||' DISABLE CONSTRAINT '||i.fk_session_log_ben_name||'';
end loop;
for i in (select fk_geraetekto_ben_name, GERAETEKONTO FROM USER_CONSTRAINTS) loop
execute immediate 'ALTER TABLE'||i.geraetekonto||' DISABLE CONSTRAINT '||i.fk_geraetekto_ben_name||'';
end loop;
/*Update statements which can only work without the constraints!!: */
UPDATE BENUTZERKONTO SET BENUTZERNAME = benutzername_neu WHERE BENUTZERNAME = benutzername_alt;
UPDATE SESSION_LOGGING SET BENUTZERNAME = benutzername_neu WHERE BENUTZERNAME = benutzername_alt;
UPDATE GERAETEKONTO SET BENUTZERNAME = benutzername_neu WHERE BENUTZERNAME = benutzername_alt;
/*Supposed to re-enable the constraints. */
for i in (select fk_session_log_ben_name, SESSION_LOGGING FROM USER_CONSTRAINTS) loop
execute immediate 'ALTER TABLE'||i.session_logging||' ENABLE CONSTRAINT '||i.fk_session_log_ben_name||'';
end loop;
for i in (select fk_geraetekto_ben_name, GERAETEKONTO FROM USER_CONSTRAINTS) loop
execute immediate 'ALTER TABLE'||i.geraetekonto||' ENABLE CONSTRAINT '||i.fk_geraetekto_ben_name||'';
end loop;
end TRG_NAMENSAENDERUNG_MA;
It throws the error that SESSION_LOGGING would be an "invalid identifier". It is typed right though and we copied the syntax from the example from the Oracale page.
What's the easiest way to achieve what we want?
Thanks in advance!
Here's the invalid identifier, these columns do not exist in USER_CONSTRAINTS. The full error message should have included the line number and given a hint at the meaning.
select fk_session_log_ben_name, SESSION_LOGGING FROM USER_CONSTRAINTS
The code should probably be this:
select constraint_name fk_session_log_ben_name, table_name SESSION_LOGGING
FROM USER_CONSTRAINTS;
My first suggestion is to redesign the schema so that it's no need to change the PK values (especially by users). PK must be surrogate. There must be serious reasons for a decision like yours. Hope you don't need such reasons :) The redesign mentioned, methinks, would be less expensive than struggling with the problem you cast. With changing PK values you'll probably face such problems again and again as the application is evolved.