Oracle: dynamic SQL statement based on a mapping table - sql

Take the following statements in order to create the context in this page:
BEGIN TRANSACTION;
CREATE TABLE TABLEA (COLUMN_A VARCHAR2(30));
CREATE TABLE TABLEB (COLUMN_B VARCHAR2(30));
CREATE TABLE MAPPING_TABLE (TABLEA_COL VARCHAR2(30), TABLEB_COL VARCHAR2(30));
INSERT INTO MAPPING_TABLE (TABLEA_COL, TABLEB_COL) VALUES ('COLUMN_A', 'COLUMN_B');
COMMIT;
I want to make a select from TABLEB using the mapping value setted in the MAPPING_TABLE.
In other words, knowing the name of the column of the TABLEA (for example 'COLUMN_A'), I want to make a SELECT directly in the TABLEB.
Something like:
SELECT /*a statement to recover the COLUMN_B based on the 'COLUMN_A' value*/
FROM TABLEB
It is possible to do in SQL statement, or I'm forced to use PL/SQL context (with procedures, etc...)?

Try this,
declare
v_col varchar(30);
v_val varchar(30);
sql_stmt varchar(255);
begin
select tableb_col into v_col
from mapping_table
where tablea_col = 'COLUMN_A';
sql_stmt := 'select '||v_col||' from tableb';
dbms_output.put_line ( sql_stmt ) ;
execute immediate sql_stmt into l_val;
dbms_output.put_line ( v_val) ;
end;

Related

Procedure to insert data if not exist pl sql

I have a table with these columns (id,first_name,birth). I want to create a procedure that insert a new customer only if the id inserted doesn't exist in the table. If it already exist, then don't insert it. This is my code so far, but I got an error 'line 3 sql statement ignored'. Any idea? I need to use procedure and pl sql in oracle. Thanks!
CREATE OR REPLACE PROCEDURE add_emp(v_id IN int,
v_name IN varchar2,
v_bday IN date) IS
BEGIN
INSERT INTO Employees
(Id, First_name, Birth)
SELECT *
FROM (SELECT v_id, v_name, v_bday) AS tmp
WHERE NOT EXISTS (SELECT Id FROM Employees WHERE Id = v_id);
END;
/
DECLARE
m_id int := 3;
m_name varchar2 := 'John';
m_bday date := '16-Dec-1990';
BEGIN
add_cust(m_id, m_name, m_bday);
END;
/
Your procedure has some syntax issue which is fixed in following code:
CREATE OR REPLACE PROCEDURE ADD_EMP (
V_ID IN INT,
V_NAME IN VARCHAR2,
V_BDAY IN DATE
) IS
BEGIN
INSERT INTO EMPLOYEES (
ID,
FIRST_NAME,
BIRTH
)
SELECT V_ID,
V_NAME,
V_BDAY
FROM DUAL -- FROM clause was missing
WHERE NOT EXISTS (
SELECT ID
FROM EMPLOYEES
WHERE ID = V_ID
);
END;
/
Also, Your calling PL/SQL block has some issues which are corrected in the following code:
DECLARE
M_ID INT := 3;
M_NAME VARCHAR2(10) := 'John'; -- varchar2 must be declared with size
M_BDAY DATE := DATE '1990-12-16'; -- added date literal to convert string to date
BEGIN
ADD_CUST(M_ID, M_NAME, M_BDAY);
END;
/
In Oracle SELECT does not work without a FROM clause (other DBMS products are different). So you need to provide a table; you can use DUAL, which is a dummy table provided by Oracle which is guaranteed to return one row.
INSERT INTO Employees(Id,First_name,Birth)
SELECT v_id, v_name, v_bday
from dual
WHERE NOT EXISTS (
SELECT Id FROM Employees WHERE Id = v_id
);
Your INSERT statement would work with a slight change
CREATE OR REPLACE PROCEDURE add_emp(v_id Employees.Id%type,
v_name Employees.First_name%type,
v_bday Employees.Birth%type) IS
BEGIN
INSERT INTO Employees
(Id, First_name, Birth)
SELECT v_id, v_name, v_bday
FROM dual
WHERE NOT EXISTS (SELECT * FROM Employees WHERE Id = v_id);
END;
/
You can replace INSERT statement with MERGE as an alternative DML such as
MERGE INTO Employees e1
USING
(SELECT v_id AS id, v_name AS First_name, v_bday AS birth
FROM dual) e2
ON ( e1.id = e2.id )
WHEN MATCHED THEN UPDATE SET e1.First_name = e2.First_name,
e1.Birth = e2.Birth -- If you need to change for the matching case
WHEN NOT MATCHED THEN INSERT( e1.id, e1.First_name, e1.birth )
VALUES( e2.id, e2.First_name, e2.birth );

How to execute SQL query stored in a table

I have a table having one of the columns that stores SQL query.
create table test1
(
name varchar(20),
stmt varchar(500)
);
insert into test1 (name, stmt)
values ('first', 'select id from data where id = 1;')
Data table is like:
create table data
(
id number,
subject varchar(500)
);
insert into data (id, subject) values (1, 'test subject1');
insert into data (id, subject) values (2, 'test subject2');
insert into data (id, subject) values (3, 'test subject2');
Now every time on insert in test1, I need to execute the query that gets inserted in stmt column of test1 and insert queried data to result table:
create table result
(
id number,
subject varchar(500)
);
For that I am writing a trigger that gets executed on every insert in test1 like as follows:
create or replace TRIGGER "TEST_AFTER_INSERT"
BEFORE INSERT or UPDATE ON test1
FOR EACH ROW
DECLARE
sql_stmt VARCHAR2(500);
BEGIN
select stmt into sql_stmt from data where name = :NEW.name;
insert into result(id, subject)
select id,subject from data where id in ('stmt');
END;
Could you please let me know how to achieve this, above trigger is throwing error that I am not able to understand.
You can use a dynamic query in your trigger as follows:
CREATE OR REPLACE TRIGGER "TEST_AFTER_INSERT" AFTER -- CHANGED IT TO AFTER AS NAME SUGGESTS
INSERT OR UPDATE ON TEST1
FOR EACH ROW -- REMOVED DECLARE SECTION
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO result
SELECT ID, SUBJECT FROM DATA WHERE ID IN ('
|| RTRIM(:NEW.STMT, ';')
|| ')';
-- SINGLE QUERY TO INSERT THE DATA
-- USED RTRIM AS STMT HAS ; AT THE END
END;
Cheers!!
Consider direct insertion :
CREATE OR REPLACE TRIGGER "TEST_AFTER_INSERT"
BEFORE INSERT or UPDATE ON test1
FOR EACH ROW
DECLARE
BEGIN
insert into result(id, subject)
select id, subject from data where name = :NEW.name;
END;

After insert Trigger

I'm trying to do a trigger which insert some into column after insert statement. For example I have table with column which looks:
Column1 Column2 Column3
And I'm inserting data into Column 1, Insert into Table(Column1) values ('256234','234234').
Now I would like automatically insert into COlumn2 TImestamp and into Column3 Value "Y", So output should looks:
Column1 Column2 Column3
256234 2015-10-28 08:48 Y
234234 2015-10-28 08:48 Y
Guys, could you help me with that? I tried to use cursor
Finally I got something like that:
create or replace trigger name
after insert on table
declare
c1 sys_refcursor;
idx varchar2(200);
begin
open c1 for select Column1 from table ;
loop
fetch c1 into idx;
exit when c1%NOTFOUND;
update table a1 set a1.Column2 = (select to_char(sysdate,'YYYYMMDDHHMISS') from dual) where Column1=idx;
update table a1 set a1.Column3 = (select 'Y' from dual) where Column1=idx;
end loop;
close c1;
end;
It works fine, but I'm wondering if there is some other better solution than that?
No need for a cursor or even an update:
create or replace trigger name
before insert on table_x
begin
:new.column2 := sysdate;
:new.column3 := 'Y';
end;
/
But you need a before trigger for this, because an after trigger cannot modify the newly inserted row.
But why don't you just define a default value for those columns, then you don't need a trigger at all:
create table table_x
(
column_1 integer,
column_2 date default sysdate,
column_3 varchar(1) default 'Y'
);
You can create trigger as similar :
CREATE OR REPLACE TRIGGER "TRG_NAME"
BEFORE INSERT ON "TABLE_NAME"
FOR EACH ROW
DECLARE
BEGIN
:NEW.Column2 := to_char(sysdate,'YYYYMMDDHHMISS');
:NEW.Column3 := 'Y';
END TRG_NAME ;
/
ALTER TRIGGER "TRG_NAME" ENABLE;
/
Hope this PL/SQL will help you..

How to Create Stored procedure in DB2

Can you please help me to create a below Oracle procedure in DB2? Same table name with columns are available in DB2 also but below script is not working
CREATE OR REPLACE PROCEDURE sample_proc (ACCT_NO in CHAR,p_cursor out SYS_REFCURSOR)
is
BEGIN
OPEN p_cursor FOR
select sampl1,sample2,sample3
from
table_test b
where
rec_id='A'
and sample3=ACCT_NO ;
END;
If you want a return better use function here are some example to get collection
CREATE TABLE table_test
(
sample1 VARCHAR2 (1000),
sample2 VARCHAR2 (1000),
sample3 VARCHAR2 (1000)
);
insert into table_test ( sample1,sample2 ,sample3)
values ('daftest1','dsdtest1','sstsest3');
insert into table_test ( sample1,sample2 ,sample3)
values ('FAStest1','fstest1','sstsest3');
insert into table_test ( sample1,sample2 ,sample3)
values ('sdtest1','asdtest1','fstest3');
insert into table_test ( sample1,sample2 ,sample3)
values ('test2','test2','test123');
CREATE OR REPLACE TYPE TEST_REC
AS OBJECT (
sample1 VARCHAR2(1000)
,sample2 VARCHAR2(1000)
,sample3 VARCHAR2(1000)
);
CREATE OR REPLACE TYPE TEST_REPORT_TABLE
AS TABLE OF TEST_REC;
CREATE OR REPLACE FUNCTION testing (p_acct_no IN varchar2)
RETURN test_report_table
IS
v_rec test_rec;
v_test_report_table test_report_table := test_report_table();
BEGIN
FOR i IN (SELECT sample1,sample2,sample3
FROM table_test b
--where rec_id='A'
where sample3=p_acct_no)
LOOP
v_rec:=test_rec(NULL,NULL,NULL);
dbms_output.put_line(i.sample1);
v_rec.sample1:=i.sample1;
v_rec.sample2:=i.sample2;
v_rec.sample3:=i.sample3;
v_test_report_table.EXTEND;
v_test_report_table(v_test_report_table.COUNT) :=v_rec;
END LOOP;
RETURN v_test_report_table;
END;
select * from table(testing(p_acct_no=>'sstsest3'))
Or better use bulk collect if you don't need to add rows dynamically
CREATE OR REPLACE FUNCTION JSTRAUTI.testing (p_acct_no IN varchar2)
RETURN test_report_table
IS
v_test_report_table test_report_table := test_report_table();
BEGIN
SELECT test_rec(sample1,sample2,sample3)
bulk collect into v_test_report_table
FROM table_test b
--where rec_id='A'
where sample3=p_acct_no;
RETURN v_test_report_table;
END;
result the same.

Insert/Update in PL/SQL

I have made a procedure in PL/SQL which inserts data from one table to another on basis of primary key. My procedure is working fine but i can't figure out how will i update column CODE_NUMBER of my table MAIN if primary key already exists.
Actually i want rows of MAIN table to get UPDATED when its has primary key and insert data from REGIONS when primary key does not exists.
DECLARE
variable number;
id number;
description varchar2 (100);
CURSOR C1 IS
select regions.REGION_ID variable
from regions;
BEGIN
FOR R_C1 IN C1 LOOP
BEGIN
select regions.REGION_ID,regions.REGION_NAME
into id,description
from regions
where regions.REGION_ID = R_C1.variable;
----If exists then update otherwise insert
INSERT INTO MAIN( ID, CODE_NUMBER) VALUES( id,description);
dbms_output.put_line( id ||' '|| 'Already Exists');
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
dbms_output.put_line( R_C1.variable);
END;
END LOOP;
END;
There's no need to do this with PL/SQL and cursors. What you really want to do is something like this:
MERGE INTO MAIN dst
USING (
SELECT regions.REGION_ID id,
regions.REGION_NAME description
FROM regions
) src
ON src.id = dst.id
WHEN MATCHED THEN UPDATE
SET dst.code_number = src.description
WHEN NOT MATCHED THEN INSERT (id, code_number)
VALUES (src.id, src.description)
Read more about the SQL MERGE statement in the documentation
I can not really see a point in doing a cursor in this case. Why can't you just do it like this:
--Update the rows
UPDATE MAIN
SET ID=regions.REGION_ID,
CODE_NUMBER=regions.[description]
FROM MAIN
JOIN regions
ON MAIN.ID=regions.REGION_ID;
--Insert the new ones
INSERT INTO MAIN(ID,CODE_NUMBER)
SELECT
regions.REGION_ID,
regions.[description]
FROM
regions
WHERE NOT EXISTS
(
SELECT
NULL
FROM
MAIN.ID=regions.REGION_ID
)