Oracle: Modify primary/foreignkeys and constrains inside a TRIGGER - sql

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.

Related

How to temporarily disable all constraints of a schema in postgresql?

I need to temporarily disable all constraints and triggers of a schema in order to anonymize data in several tables. As there are dependencies between tables, I prefer to disable everything and once the anonymization treatment is over I can enable all constraints and triggers one more time.
I tried SET FOREIGN_KEY_CHECKS=0; and I got this error:
ERROR: unrecognized configuration parameter "foreign_key_checks"
SQL state: 42704
I've been reading a lot about that and some people say this is not possible.
Do you know a way to do that?
Thank you!
To disable foreign keys and deferrable unique and primary key constraints for a table, you can use
ALTER TABLE ... DISABLE TRIGGER ALL;
To disable all such constraints for the duration of a database session, you can
SET session_replication_role = replica;
Both tricks won't work for non-deferrable constraints and check constraints.
I found this solution,
I created a temporal table to keep all constraints definition:
CREATE TEMPORARY TABLE temp_constraints AS
SELECT conname constraintname, conrelid::regclass tablename, pg_get_constraintdef(oid) definition, contype
FROM pg_catalog.pg_constraint;
Then I drop all constraints:
DO $$
DECLARE constraint_name TEXT;
DECLARE constraint_table TEXT;
BEGIN
FOR constraint_name, constraint_table IN
SELECT constraintname , tablename FROM temp_constraints ORDER BY contype DESC
LOOP
EXECUTE 'ALTER TABLE ' || constraint_table || ' DROP CONSTRAINT IF EXISTS ' || constraint_name || ' CASCADE;';
END LOOP;
END $$;
And after anonymizing data I restore all constraints using the definitions in the temporal table and I drop the temporal table:
DO $$
DECLARE constraint_table TEXT;
DECLARE constraint_definition TEXT;
BEGIN
FOR constraint_table, constraint_definition IN
SELECT tablename, definition FROM temp_constraints ORDER BY contype DESC
LOOP
EXECUTE 'ALTER TABLE ' || constraint_table || ' ADD ' || constraint_definition || ';';
END LOOP;
DROP TABLE IF EXISTS temp_constraints;
END $$;
I hope this can help someone else. Thank you!

Redshift Alter table if not exists

I'm trying add a new field in a redshift table. But I want to add only if this field doesn't exists.
I tried wrapping it with IF NOT EXISTS. But I got following error:
Amazon](500310) Invalid operation: syntax error at or near "IF" Position: 5;
BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'schema_name' and table_name='table_name' and column_name='new_field') THEN
ALTER TABLE schema_name.table_name
ADD new_field INT;
END IF;
COMMIT;
I'm not sure if I'm correctly using "IF NOT EXISTS" statement inside the BEGIN block.
Can someone please help me?
Thanks in advance!
It could be better to handle it using EXCEPTION
BEGIN
ALTER TABLE
<table_name> ADD COLUMN <column_name> <column_type>;
EXCEPTION
WHEN duplicate_column
THEN RAISE NOTICE 'column <column_name> already exists in <table_name>.';
END;
The Answer by Yauheni Khvainitski is not completely wrong. But you do have to use a SP and the only option Redshit has (at this point is to have "EXCEPTION WHEN OTHER"). An example:
CREATE OR REPLACE PROCEDURE change_column_to_big_int_TABLE_NAME_X(column_name varchar(200)) AS
$$
DECLARE
new_column_name VARCHAR;
BEGIN
SELECT INTO new_column_name (table_name)||'_new';
-- RAISE INFO 'new_table_name = % table_name = %',new_column_name, table_name;
ALTER TABLE TABLE_NAME_X ADD COLUMN "(new_column_name)" bigint;
EXCEPTION WHEN OTHERS
THEN RAISE NOTICE 'column already exists on table';
END;
$$
LANGUAGE plpgsql;
CALL change_column_to_big_int_TABLE_NAME_X('COLUMN_Y');
Some links from AWS on:
trapping errors: https://docs.aws.amazon.com/redshift/latest/dg/stored-procedure-trapping-errors.html
CREATE PROCEDURE: https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_PROCEDURE.html
Also please notice that this is valid at this point in time. Redshift seems to be always evolving.
The issue I think is that AWS Redshift does not support the IF statement, but instead uses CASE statements. The CASE statements are very similar to IF the way they implement them. But I admit, I prefer the IF statements.

Drop table if it exists with DB2/400 SQL

My goal is pretty straightforward - if table has rows, drop it.
Despite the fact that currently there are several similar answers none of them worked for me.
DB2 Drop table if exists equivalent
Suggested solution:
IF EXISTS (SELECT name FROM sysibm.systables WHERE name = 'mylib.mytable') THEN
DROP TABLE mylib.mytable;END IF;
Result:
SQL State: 42601 Vendor Code: -199 Message: [SQL0199] Keyword IF not expected.
Valid tokens: ( CL END GET SET CALL DROP FREE HOLD LOCK OPEN WITH ALTER BEGIN
Drop DB2 table if exists
Suggested solution:
--#SET TERMINATOR #
begin
declare statement varchar(128);
declare continue handle for sqlstate '42710' BEGIN END;
SET STATEMENT = 'DROP TABLE MYLIB.MYTABLE';
EXECUTE IMMEDIATE STATEMENT;
end #
Result:
Message: [SQL0104] Token HANDLE was not invalid. Valid tokens: HANDLER or, if replace handle with handler:
Message: [SQL0199] Keyword STATEMENT not expected. Valid tokens: SQL PATH RESULT SCHEMA CURRENT CONNECTION DESCRIPTOR.
From answer about views
Suggested solution:
DROP TABLE MY_TABLE ONLY IF EXISTS source.
Result:
Message: [SQL0104] Token ONLY was not invalid. Valid tokens: RESTRICT CASCADE
So, I wonder if an alternate solution exists. CL solution is also interesting.
I'm assuming you may want to do this more than once, so a procedure might be in order.
CREATE or replace PROCEDURE DROP_LIVE_TABLE
(in #table varchar(10)
,in #library varchar(10)
)
BEGIN
declare #stmt varchar(100);
declare #cnt int;
IF exists( select * from systables
where sys_dname = #library
and sys_tname = #table
and table_type in ('P','T')
) THEN
SELECT int(sum(number_rows))
INTO #cnt
from SYSTABLESTAT
where sys_dname = #library
and sys_tname = #table
;
IF #cnt > 0 THEN
set #stmt = 'DROP TABLE '||#library||'.'||#table||' CASCADE';
execute immediate #stmt;
END IF;
END IF;
RETURN;
END;
The CASCADE keyword causes any dependent objects such as indexes, logical files, views, or such to be deleted as well.
Here is a CL answer to this question:
PGM PARM(&FILENAME)
DCL VAR(&FILENAME) TYPE(*CHAR) LEN(10)
DCL VAR(&NUMRECS) TYPE(*DEC) LEN(10 0)
RTVMBRD FILE(&FILENAME) NBRCURRCD(&NUMRECS)
IF COND(&NUMRECS > 0) THEN(DLTF +
FILE(&FILENAME))
OUT: ENDPGM
This solution would have trouble if the physical file has dependencies such as indexes or logical files. Those dependencies would have to be deleted first.
The solution by #danny117 on the other hand does not work in all environments. For example I was unable to coerce it to work in SQuirreL client. But it does work in i Navigator. It also works in RUNSQLSTM, but I was unable to determine how to make it work with unqualified table references. If the tables are unqualified, RUNSQLSTM uses the default collection from DFTRDBCOL. The CURRENT_SCHEMA special register does not return the value from DFTRDBCOL.
Here is the if table has rows drop it solution using a compound statement:
begin
if( exists(
select 1 from qsys2.systables
where table_schema = 'MYLIB'
and table_name = 'MYTABLE'
)) then
if( exists(
select 1 from mylib.mytable
)) then
drop table mylib.mytable;
end if;
end if;
end;
I am guessing at the reason you would want to do this, but if it is to allow creation of a new table, then best way may be with a CREATE OR REPLACE TABLE if you are at IBM i v7.2 or greater.
If all you want to do is make sure you have an empty table, TRUNCATE (v7.2+) or DELETE may be better options.
Drop table if exists using atomic statement.
BEGIN ATOMIC
IF( EXISTS(
SELECT 1 FROM TABLES
WHERE TABLE_SCHEMA = 'MYLIB'
AND TABLE_NAME = 'MYTABLE'
)) THEN
DROP TABLE MYLIB/MYTABLE;
END IF;
END;
try this:
BEGIN
IF EXISTS (SELECT NAME FROM QSYS2.SYSTABLES WHERE TABLE_SCHEMA = 'YOURLIBINUPPER' AND TABLE_NAME = 'YOURTABLEINUPPER') THEN
DROP TABLE YOURLIB.YOURTABLE;
END IF;
END ;

SQL - Trigger "not in"

I'm trying to make a time trigger in the dubbing database. I want to check that character can't be dubbed in the dubbing of the movie in which the character doesn't appear. Here's the PDM:
and CDM
I'm the begginer with SQL but I know that there should be some trigger in the table 'DUBBES'. I tried to make something like this, but i got a message that trigger is invalid:
CREATE OR REPLACE TRIGGER x_character
BEFORE INSERT OR UPDATE ON dubbes FOR EACH ROW
DECLARE
IF ( :NEW.CHAR_id_character NOT IN ( SELECT CHAR_id_CHARACTER
FROM APPEARS
WHERE APPEARS.MOV_id_movie = (SELECT dubbing.Mov_id_movie
FROM DUBBING
WHERE dubbing.id_dubbing = :NEW.dab_id_dubing)))
THEN
RAISE_APPLICATION_ERROR(-20000, 'Character is not in this movie.');
END IF;
END;
/
I would really appreciate any help.
Many thanks in advance!
I think that your code should be like this:
create or replace trigger X_character BEFORE INSERT OR UPDATE ON dubbes
FOR EACH ROW
DECLARE
haveit number;
idmovie number;
begin
select dubbing.Mov_id_movie into idmovie from DUBBING where dubbing.id_dubbing = :new.dab_id_dubing;
select count(*) into haveit from APPEARS
where
APPEARS.MOV_id_movie = idmovie and
APPEARS.CHAR_id_CHARACTER = :new.CHAR_id_character;
IF( haveit = 0 ) then
RAISE_APPLICATION_ERROR(-20000, 'Character is not in this movie.');
END IF;
END;
/
From your diagram, you don't need a trigger for this. If you redefine your FK_DUBBES_DUBBES3_CHARACTE foreign key to refer to APPEARS.CHAR_ID instead of directly against CHARACTER.ID_CHARACTER then the requirement will be enforced for you, without the additional overhead of a trigger.
(As an aside, you might find it easier to have more consistent column names, and simpler key names...)
Begin keyword is missing after DECLARE

DDL ON SCHEMA trigger

Can anyone please tell me whats going on here...
CREATE TRIGGER afr_alt_trig AFTER ALTER ON SCHEMA
cols ora_name_list_t;
BEGIN
IF (ora_dict_obj_type = 'TABLE') THEN
select column_name bulk collect into cols from user_tab_cols where table_name = ora_dict_obj_name;
FOR i IN cols.first .. cols.last LOOP
dbms_output.put_line(cols(i));
END LOOP;
END IF;
END;
The above trigger is expected to display the list of all the columns in a table after the ALTER TABLE command is executed.
It works just fine when I ALTER a table with column(s) without any constraints.
Ex: Alter Table tab_xxx ADD(nucol char);--FINE
However, if I add a new column with a constraint like
Ex: ALTER TALE tab_xxx ADD(n number NOT NULL);
then this new column is not immediately reflected in the USER_TAB_COLS DD view and the FOR loop above to display the names of all the columns DOESN'T show this NEW column added.
Thanks already.