Below trigger code(converted from MSSQL) in oracle is not working.
The two columns should not have duplicate row in the table. I'm creating a trigger for accomplishing this.
Can anyone help in updating/correcting the above code to be used in my trigger?
/*
**Unique Constraint for TestOracle - TestTinyInt.
*/
if (Update(UpdOperation) or Update(TestTinyInt)) THEN
IF Exists(
SELECT * FROM inserted i INNER LOOP JOIN TestOracle x ON
(i.TestTinyInt=x.TestTinyInt)
WHERE i.updoperation IN (0, 1) AND x.updoperation IN (0, 1) GROUP BY x.TestTinyInt
HAVING COUNT(*) > 1)
BEGIN
RAISERROR( 'Invalid attempt to enter duplicate TestTinyInt in TestOracle', 16, -1 )
ROLLBACK TRAN
RETURN
END
END
The best way is to create 2 unique index on each of columns. By doing this you are eliminating duplication in particual column(like #a_horse_with_no_name mentioned).
For other case you don't need to use triger, you need only simple where condition
where Column_A not in (select Column_B from table) and Column_B not in (Select Column_A in table).
EDIT:
It if have to be done in trigger THEN :
create or replace trigger ... instead of insert or update on ...
Declare
dummy number;
Begin
select 1 into dummy from dual where :new.Column_A in (select Column_B from table) or new:.Column_B in (Select Column_A in table);
if dummy <> 1 THEN
INSERT
END IF;
END;
EDIT2: IF you don't want unique index and tirgger here is solution :
create or replace trigger ... instead of insert or update on ...
Declare
dummy number;
Begin
select count(*) into dummy from(
SELECT COL1 FROM (
(select :new.Column_A col1 from dual
UNION
select :new.Column_B from dual))
INTERSECT
SELECT COL2 FROM (
( SELECT COLUMN_A COL2 from table
UNION
SELECT COLUMN_B from table));
if dummy = 0 THEN
INSERT
END IF;
END;
Related
I have a requirement to create or replace a table only if certain variable value is 1
SET variable = (select statement) -- $variable will be 1 or 0 depending on value returned from select statment
create or replace table2 as select * from table1 (only if $variable =1)
Is there any way to do it? If the variable value is 0, the create statement should be skipped
Using Snowflake Scripting block:
-- CREATE TABLE table1 AS SELECT 1 AS col;
SET variable = (SELECT 1);
BEGIN
IF ($variable = 1) THEN
create or replace table table2 as select * from table1;
END IF;
END;
Variable could be defined also at Snowflake Scripting block level:
DECLARE
variable INTEGER := (SELECT 1);
BEGIN
IF (:variable = 1) THEN
create or replace table table2 as select * from table1;
END IF;
END;
Our IT team loads couple of tables every month. The new load should have more records than the previous load, with at least 2% more records.
It's a truncate and load process, I'm collecting the num of records from each table before the truncate, and I'm checking the difference in excel every month to make sure the data load is correct.
Is there anyway to automate this in Oracle.
eg:
Table_name Before_cnt After_cnt
XX_TEST1 4,606,619,326 4,983,759,822
XX_TEST2 121,973,005 123,161,581
You can apply the steps just like below :
SQL> create table XX_TEST1( id int primary key );
SQL> insert into XX_TEST1 select level from dual connect by level <= 100;
SQL> begin -- if table exists, then drop it!
for c in (select table_name from cat where table_name = 'XX_TEST1_OLD' )
loop
execute immediate 'drop table '||c.table_name;
end loop;
end;
/
SQL> create table XX_TEST1_old as select count(*) as cnt from XX_TEST1;
SQL> begin
execute immediate 'truncate table XX_TEST1';
end;
/
SQL> insert into XX_TEST1 select level from dual connect by level <= 103;
SQL> with xt1_new(cnt_new) as
(
select count(id) from XX_TEST1
)
select case when sign( (100 * ( cnt_new - cnt) / cnt)-2 ) = 1 then 1
else 0 end as "Rate Satisfaction"
from XX_TEST1_old
cross join xt1_new;
If this SELECT statement retuns 1, then we're successful to reach the target, else returns 0 and means we're unsuccessful.
Demo
I have a table (let's name it source_tab) where I store list of all database tables that meet some criteria.
tab_name: description:
table1 some_desc1
table2 some_desc2
Now I need to execute a select statement on each of these tables and return a result as a table (I created custom TYPE). However I have a problem - when using bulk collect, only the last select statement is returned. The same issue was with open cursor. Is there any possibility to achieve this goal, another then concatenating all select statements using union all and executing it as one statement? And because I'm the begginer in sql, my second question is, is it ok to use this dynamic sql in terms of sql injection issues? Below is simplified version of my code:
CREATE OR REPLACE FUNCTION my_function RETURN newly_created_table_type IS
ret_tab_type newly_created_table_type;
BEGIN
for r in (select * from source_tab)
loop
execute immediate 'select value1, value2,''' || r.tab_name || ''' from ' || r.tab_name bulk collect into ret_tab_type;
end loop;
return ret_tab_type;
END;
I'm using Oracle 11.
In your case you are trying to populate a collection dynamically and wanted result in a single collection. In your case its not possible to do that in a single loop. Also as mentioned by #OldProgrammer, piperow would be a better solution from performance point. See below demo:
--Tables and Values:
CREATE TABLE SOURCE_TAB(TAB_NAME VARCHAR2(100), DESCRIPTION VARCHAR2(100));
/
SELECT * FROM SOURCE_TAB;
/
INSERT INTO SOURCE_TAB VALUES('table1','some_desc1');
INSERT INTO SOURCE_TAB VALUES('table2','some_desc2');
/
CREATE TABLE TABLE1(COL1 NUMBER, COL2 NUMBER);
/
INSERT INTO TABLE1 VALUES(1,2);
INSERT INTO TABLE1 VALUES(3,4);
INSERT INTO TABLE1 VALUES(5,6);
/
Select * from TABLE1;
/
CREATE TABLE TABLE2(COL1 NUMBER, COL2 NUMBER);
/
INSERT INTO TABLE2 VALUES(7,8);
INSERT INTO TABLE2 VALUES(9,10);
INSERT INTO TABLE2 VALUES(11,12);
/
Select * from TABLE2;
/
--Object Created
--UDT
CREATE OR REPLACE TYPE NEWLY_CREATED_TABLE_TYPE IS OBJECT (
VALUE1 NUMBER,
VALUE2 NUMBER
);
/
--Type of UDT
CREATE OR TYPE NEWLY_CRTD_TYP AS TABLE OF NEWLY_CREATED_TABLE_TYPE;
/
--Function:
--Function
CREATE OR REPLACE FUNCTION MY_FUNCTION
RETURN NEWLY_CRTD_TYP PIPELINED
AS
CURSOR CUR_TAB
IS
SELECT *
FROM SOURCE_TAB;
RET_TAB_TYPE NEWLY_CRTD_TYP;
BEGIN
FOR I IN CUR_TAB
LOOP
--Here i made sure that all the tables have col1 & col2 columns since you are using dynamic sql.
EXECUTE IMMEDIATE 'select NEWLY_CREATED_TABLE_TYPE(COL1, COL2) from '|| I.TAB_NAME
BULK COLLECT INTO RET_TAB_TYPE;
EXIT WHEN CUR_TAB%NOTFOUND;
FOR REC IN 1 .. RET_TAB_TYPE.COUNT
LOOP
PIPE ROW (RET_TAB_TYPE (REC) );
END LOOP;
END LOOP;
RETURN;
END;
/
Output:
SQL> Select * from table(MY_FUNCTION);
VALUE1 VALUE2
---------- ----------
1 2
3 4
5 6
7 8
9 10
11 12
6 rows selected.
May be you can combine all the queries into one using UNION ALL before execution, if the number and type of columns to be retrieved from all the tables are identical.
CREATE OR REPLACE FUNCTION my_function
RETURN newly_created_table_type
IS
ret_tab_type newly_created_table_type;
v_query VARCHAR2 (4000);
BEGIN
SELECT LISTAGG (' select VALUE1,VALUE2 FROM ' || tab_name, ' UNION ALL ')
WITHIN GROUP (ORDER BY tab_name)
INTO v_query
FROM source_tab;
EXECUTE IMMEDIATE v_query BULK COLLECT INTO ret_tab_type;
RETURN ret_tab_type;
END;
You could then use a single select statement to get all the values.
select * FROM TABLE ( my_function );
Can I update the result of query easily?
Assume I have big query which returns salary column and I need update salaries based on this query results.
ID- is primary key for my table
Now I,m doing it like this:
STEP 1
select id from mytable ...... where something
STEP 2
update mytable set salary=1000 where id in (select id from mytable ...... where something)
Is there exists alternative to do that easily?
Try for update and current of. You said that you are looking for something like "updating data on grid"
create table my_table( id number, a varchar2(10), b varchar2(10));
insert into my_table select level, 'a', 'b' from dual connect by level <=10;
select * from my_table;
declare
rec my_table%rowtype;
cursor c_cursor is select * from my_table for update;
begin
open c_cursor;
loop
fetch c_cursor into rec;
exit when c_cursor%notfound;
if rec.id in (1,3,5) then
rec.a := rec.a||'x';
rec.b := rec.b||'+';
update my_table set row = rec where current of c_cursor;
else
delete from my_table where current of c_cursor;
end if;
end loop;
commit;
end;
select * from my_table;
Yes , you can directly update the result easily.
Here is example :
update
(
select salary from mytable ...... where something
) set salary=1000
I am stuck at a place.
There is a procedure that checks for something and inserts into an table type upon successful determination of that condition.
But i can insert only once in the table type. Is there a way to insert again and again into the table type.
PROCEDURE "hello"."helloWorld.db::sampleException" (OUT TRACE_RECORD "hello"."LogTrace" )
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE i int;
select count(*) into i from "hello"."REGION";
IF :i > 1 then
TRACE_RECORD = SELECT '1' AS "LogID", '1' AS "TraceID" FROM DUMMY;
end if;
IF :i > 2 then
TRACE_RECORD = SELECT '2' AS "LogID", '2' AS "TraceID" FROM DUMMY;
end if;
END;
What i get on executing the procedure is only the last record "2,2".
How can i insert both the records 1,1 and 2,2.
Note: I do not want to use Temporary Tables.
Any help on this..
Thanks.!
Editing the Question a bit:
-I have to use Table TYPE (till the time there is no optimal way better than it)
-I have to insert more than 20-30 records in the table type.
Do you have to write this as a procedure? A table-valued function seems more suitable:
CREATE FUNCTION f_tables4 (in_id INTEGER)
RETURNS TABLE (
"LogID" VARCHAR(400),
"TraceID" VARCHAR(400)
)
LANGUAGE SQLSCRIPT
AS
BEGIN
RETURN
SELECT t."LogID", t."TraceID"
FROM (
SELECT 1 AS i, '1' AS "LogID", '1' AS "TraceID" FROM DUMMY
UNION ALL
SELECT 2 AS i, '2' AS "LogID", '2' AS "TraceID" FROM DUMMY
) t
JOIN (SELECT count(*) AS cnt FROM "hello"."REGION") c
ON c.cnt > t.i
END