I am using a few temp tables in Oracle to store some data, which is needed later in the function. To determine the data I need to store in the table, I am using a dynamic sql statement (using parameters as column names of other tables). I tried to different approaches.
The first time, I tried something like this:
INSERT INTO temp_tableA
EXECUTE IMMEDIATE dynamic_sql
USING IN OUT temp_tableB
And the other approach was like:
EXECUTE IMMEDIATE INTO temp_tableA
USING IN OUT temp_tableB
I already used some other temp table (temp_tableB) to store some data it seems, that it worked fine with the first approach, but there I did not have to use a dynamic sql statement. If I am trying to do it the same way with dynamic sql (so like the first approach) it tells me, that the keyword VALUES is missing.
The second try returns the error message, that temp_tableA can't be used as an INTO-target of a SELECT/FETCH-Statement.
What am I missing? I have to say, that I am quite new to Oracle and it's starting to drive me crazy :D
Say you have tables like the following:
create table someTab(colA number, colB number, colC number);
create table someOtherTab(col varchar2(10), val number);
insert into someTab values (1, 10, 100);
insert into someTab values (2, 20, 200);
You could define a procedure like this:
create or replace procedure copyProc(colName IN varchar2) is
vSQL varchar2(1000);
begin
vSQL := 'insert into someOtherTab(col, val) select ''' || colName || ''', ' || colName || ' from someTab';
--
dbms_output.put_line(vSQL);
execute immediate vSQL;
end;
It you run this procedure, this is what you get:
SQL> exec copyProc('colA');
insert into someOtherTab(col, val) select 'colA', colA from someTab
PL/SQL procedure successfully completed.
SQL> select * from someOtherTab;
COL VAL
---------- ----------
colA 1
colA 2
SQL> exec copyProc('colC');
insert into someOtherTab(col, val) select 'colC', colC from someTab
PL/SQL procedure successfully completed.
SQL> select * from someOtherTab;
COL VAL
---------- ----------
colA 1
colA 2
colC 100
colC 200
Related
i have four tables as below : I want to insert the variable value v_col into the table test.This variable name is stored in another table test_lkp.when i try to run the code i am getting an error. How to achieve this.
DROP TABLE TEST ;
DROP TABLE TEST_LKP;
DROP TABLE TEST_REF;
DROP TABLE TEST_Q;
CREATE TABLE TEST (COL VARCHAR2(10));
CREATE TABLE TEST_LKP (COL VARCHAR2(10));
CREATE TABLE TEST_REF (VAL VARCHAR2(10));
CREATE TABLE TEST_Q (ENAME VARCHAR2(10));
INSERT INTO TEST_LKP VALUES ('v_col');
INSERT INTO TEST_REF VALUES ('ENAME');
INSERT INTO TEST_Q VALUES ('TOM');
INSERT INTO TEST_Q VALUES ('JIM');
INSERT INTO TEST_Q VALUES ('MARK');
COMMIT;
Actual code :
SET SERVEROUTPUT ON;
declare
v_col VARCHAR2(30);
BEGIN
SELECT VAL INTO v_col FROM TEST_REF;
dbms_output.put_line(v_col);
EXECUTE IMMEDIATE
' INSERT INTO TEST SELECT '|| V_COL ||' FROM TEST_Q ' ;
END;
/
expectation is to build the insert statement dynamically through the look up table so that if any change in the columns can be handled through the look up table rather than modifying the script.
To achieve i have created the look up table TEST_LKP with the same structure as TEST and inserting values as V_COL.
This is what i tried
SET SERVEROUTPUT ON;
declare
v_col VARCHAR2(30);
q VARCHAR2(1000);
CURSOR c1 IS
SELECT
'INSERT INTO TEST '
||
'SELECT ' || COL || ' FROM TEST_Q ' DMLS
FROM TEST_LKP;
BEGIN
SELECT val INTO v_col FROM TEST_REF;
dbms_output.put_line(v_col);
FOR i IN c1
loop
dbms_output.put_line(i.DMLS);
execute immediate i.dmls;
end loop;
END;
/
erorr :
Error report -
ORA-00904: "V_COL": invalid identifier
ORA-06512: at line 17
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
ENAME
INSERT INTO TEST SELECT v_col FROM TEST_Q
Expected :
i want v_col to be replaced with ENAME as
INSERT INTO TEST SELECT ENAME FROM TEST_Q
any solution for this ?
Oracle stored procedure: I want a stored procedure that inserts table and column names into another table (target: add_definitions). I want to be able to run this procedure at any time to pick up new tables/columns that may have been added to the database. Also, I only want to add items that belong to a specific owner (Oracle term - aka "PUBUSER"), and try to avoid "system" tables. The solution must be a stored procedure.
Current stored proc that does not work
create or replace procedure test_adding as
sqlcmd varchar2(2000);
begin
sqlcmd := 'insert into add_definitions (table_name, column_name) ' ||
'select aa.table_name, aa.column_name ' ||
'from all_tab_columns aa ' ||
'where (instr(aa.table_name, ''$'') = 0 and aa.owner = ''PUBUSER'') and ' ||
'(not exists (select item_id from add_definitions cc where cc.table_name = aa.table_name and cc.column_name = aa.column_name))';
execute immediate sqlcmd;
end test_adding;
I checked the above by first manually populating the add_definitions table (with all the proper values). Then I deleted 2 of the rows. Then when I run the above, those 2 rows are not added back. I checked the SELECT part of the above INSERT and it does return the 2 items (table_name and column_name) that I deleted.
Note: I simplified the code, I need to add other values during the insert which get calculated from other code snippets (I plan on doing the "USING" part of the EXECUTE IMMEDIATE... to substitute those values into the sql command). But I cannot get this very basic step to work first. So please do not offer suggestions to "not do" a stored procedure, etc, etc. After 30 hours on this, I'm about to shoot myself.
Update: As per one of the comments, I tried to get this to run without EXECUTE IMMEDIATE - just did a direct "INSERT INTO... SELECT ..." It still failed to recover the deleted values (I retested the above steps over again, several times, using the direct INSERT approach). This is quite frustrating as one of the other responses says it works fine for them....??? (I'm using Oracle 11g Express Edition - but I sure hope that would not make a difference).
There seems no problem with your code, except not including and not populating item_id column in the insert statement :
SQL> create table add_definitions (item_id int, table_name varchar2(100), column_name varchar2(100));
SQL> create sequence seq_test start with 1;
SQL> create or replace procedure test_adding as
sqlcmd varchar2(2000);
begin
sqlcmd := 'insert into add_definitions (item_id,table_name, column_name) ' ||
'select seq_test.nextval,aa.table_name, aa.column_name ' ||
'from all_tab_columns aa ' ||
'where (instr(aa.table_name, ''$'') = 0 and aa.owner = ''PUBUSER'') and ' ||
'(not exists (select item_id from add_definitions cc where cc.table_name = aa.table_name and cc.column_name = aa.column_name))';
--dbms_output.put_line(sqlcmd);
execute immediate sqlcmd;
end test_adding;
SQL> exec test_adding; -- Assume 100 rows inserted
SQL> select count(1) from add_definitions;
COUNT(1)
----------
100
SQL> delete add_definitions where item_id in (99,100);
SQL> select count(1) from add_definitions;
COUNT(1)
----------
98
SQL> exec test_adding;
SQL> select count(1) from add_definitions;
COUNT(1)
----------
100
SQL> commit;
I have a procedure which receives as parameter a where clause (i.e. where col1 = 1). I am using this clause to search in some tables using an EXECUTE IMMEDIATE statement and the result to be inserted into a nested table, and than be displayed.
The procedure works fine if any data is found but in case no data is found, then the above error is thrown.
Can someone explain what cause this error, please?
Here is the procedure:
create or replace procedure prc_checks(pi_where varchar2) as
cursor c_tables is
select object_name,
case object_name
when 'XP_IMPORT_MW' THEN 99999999
when 'XP_IMPORT_MW_ARCH' THEN 99999998
else TO_NUMBER(SUBSTR(object_name, -8, 8))
end to_order
from dba_objects
where object_type = 'TABLE'
and object_name IN ('XP_IMPORT_MW', 'XP_IMPORT_MW_ARCH')
or REGEXP_LIKE (object_name, 'XP_IMPORT_MW_ARCH_201(5|6|7)[0-9]{4}') order by 2 desc;
type t_result is table of xp_import_mw%rowtype;
v_result t_result;
v_sql varchar2(300);
BEGIN
for i in c_tables
loop
v_sql := 'select * from ' || i.object_name || ' ' || pi_where;
execute immediate v_sql bulk collect into v_result;
if v_result.count > 0
then
for j in v_result.first .. v_result.last
loop
dbms_output.put_line(v_result(j).art_nr);
end loop;
dbms_output.put_line('... the required information was found on table name ' || upper(i.object_name));
exit;
end if;
end loop;
END prc_checks;
You'll get this is one of the tables being found by the cursor has fewer columns than xp_import_mw. For example:
create table xp_import_mw (col1 number, art_nr number, dummy number);
create table xp_import_mw_arch_20160102 (col1 number, art_nr number, dummy number);
create table xp_import_mw_arch_20160101 (col1 number, art_nr number);
insert into xp_import_mw_arch_20160101 values (1, 42);
So the main xp_import_mw table has three columns but no matching data. One of the old archive tables has one fewer columns.
I added a dbms_output.put_line(v_sql) to the procedure to see which table it fails against, then ran it:
set serveroutput on
exec prc_checks('where col1 = 1');
which got output:
select * from XP_IMPORT_MW where col1 = 1
select * from XP_IMPORT_MW_ARCH_20160102 where col1 = 1
select * from XP_IMPORT_MW_ARCH_20160101 where col1 = 1
Error starting at line : 49 in command -
BEGIN prc_checks('where col1 = 1'); END;
Error report -
ORA-01007: variable not in select list
ORA-06512: at "MY_SCHEMA.PRC_CHECKS", line 25
ORA-06512: at line 1
01007. 00000 - "variable not in select list"
*Cause:
*Action:
So the problem isn't that there is no data found; the problem is that there is matching data in a table which has the wrong structure.
You could construct the select list based on the xp_import_mw table's structure, instead of using *; that won't stop it failing, but would at least give you a slightly more helpful error message - in this case ORA-00904: "DUMMY": invalid identifier instead of ORA-01007.
You could do a quick and crude check for discrepancies with something like:
select table_name, count(column_id) as column_count,
listagg(column_name, ',') within group (order by column_id) as columns
from dba_tab_columns
where table_name IN ('XP_IMPORT_MW', 'XP_IMPORT_MW_ARCH')
or REGEXP_LIKE (table_name, 'XP_IMPORT_MW_ARCH_201(5|6|7)[0-9]{4}')
group by table_name
having count(column_id) != (
select count(column_id) from dba_tab_columns where table_name = 'XP_IMPORT_MW'
);
... although if you're using dba_* or all_* view you should really be including the owner, here and in your procedure.
I need to create a table if it does not exist, and when it is created add a single row to it.
I'm new to oracle and PL/SQL so I basically need an equivalent of the following T-SQL:
IF OBJECT_ID('my_table', 'U') IS NULL
BEGIN
CREATE TABLE my_table(id numeric(38,0), date datetime)
INSERT INTO my_table
VALUES (NULL, 0)
END
if you want to check table creation
DECLARE count NUMBER;
BEGIN
count := 0;
SELECT COUNT(1) INTO count from user_tables WHERE table_name= 'MY_TABLE';
IF COL_COUNT = 0 THEN
EXECUTE IMMEDIATE 'create table ....';
END IF;
END;
/
A checking for DML .please note you have to sepcify your pk columns and values.
DECLARE count NUMBER;
BEGIN
count := 0;
SELECT COUNT(1) INTO count from MY_TABLE WHERE id= 0 and name='Something';
IF COL_COUNT = 0 THEN
EXECUTE IMMEDIATE 'insert into MY_TABLE (id,name) values(0,''something'') ';
END IF;
END;
/
also note I recomand to specify columns when you insert into a table
Another approach is to use exception logic. I changed field names and types according to Oracle rules
declare
eAlreadyExists exception;
pragma exception_init(eAlreadyExists, -00955);
begin
execute immediate 'CREATE TABLE my_table(id number, dateof date)';
execute immediate 'INSERT INTO my_table VALUES (NULL, sysdate)';
exception when eAlreadyExists then
null;
end;
but may be it is not a good idea to create tables dynamically
In my opinion, you should not be creating objects on the fly. You should think about your design before implementing it.
Anyway, if you really want to do it this way, then you need to do it programmatically in PL/SQL (ab)using EXECUTE IMMEDIATE.
However, I would prefer the CTAS i.e. create table as select if you want to create a table ta once with a single row. For example,
SQL> CREATE TABLE t AS SELECT 1 id, SYSDATE dt FROM DUAL;
Table created.
SQL> SELECT * FROM t;
ID DT
---------- ---------
1 29-MAY-15
SQL>
The table is created permanently.
If you are looking for a temporary table, which you could use to store session specific data , then look at creating Global temporary table.
From documentation,
Use the CREATE GLOBAL TEMPORARY TABLE statement to create a temporary
table. The ON COMMIT clause indicates if the data in the table is
transaction-specific (the default) or session-specific
You can use NOT EXISTS with select statement:
IF NOT EXISTS(SELECT 1 FROM my_table) THEN
CREATE TABLE my_table(id NUMBER, date date);
COMMIT;
INSERT INTO my_table(id, date) values (NULL, O);
COMMIT;
END IF;
UPDATE
According to the comment, I cannot use Exist directly in PL/SQL. So this is another way to do it:
begin
select case
when exists(select 1
from my_table)
then 1
else 0
end into l_exists
from dual;
if (l_exists = 1)
then
-- anything
else
EXECUTE IMMEDIATE 'CREATE TABLE my_table(id NUMBER, date date)';
EXECUTE IMMEDIATE 'INSERT INTO my_table(id, date) values (NULL, O)';
end if;
end;
I need to write a stored procedure that will provide the data from two different tables. Say table1 and table2. These two tables doesn't have any relationship.
Now in SQL Server i can simply create a stored procedures like:
create procedure abc
as
begin
select * from table1;
select * from table2:
end;
Now in oracle, I usually create a SYS_REFCURSOR and do something like:
Open SYS_REFCURSOR_VAR For Select * from table1;
but I don't know how to provide the two result sets from two different tables table1 and table2. I tried to create two different SYS_REFCURSOR one for each table. But when I executed the procedure I got the data from first table only. The second SYS_REFCURSOR doesn't seems to be working.
Anyone have any idea, how to accomplish this?
Try this
create or replace procedure tst
(p_refcursor1 out sys_refcursor,p_refcursor2 out sys_refcursor)
is
begin
open p_refcursor1 for
select * from dual;
open p_refcursor2 for
select * from dual;
end;
So I assume the records you want to pull from each table are effectively the same. e.g. id, name, price. In that case just write your query like
SELECT t1.id AS id, t1.name AS name, t1.unit_price AS price FROM t1
UNION
SELECT t2.id AS id, t2.description AS name, t2.price AS price FROM t2
Not sure if its required, but always good form to have a union return data sets with the same "column" names. So I used the AS in teh example to demonstrate this
You need to give more details about your problem.. specifically
1) the code that you have tried so far
2) How are you accessing your ref cursor to see the results? (SQLPLUS or Java or VB.net)?
Based on your SQL Server code, I am assuming you are trying to get the UNION of the rows from the two tables. Here is the code in Oracle that lets you do that. As you can see, my client tool here is SQLPLUS and I am able to see the values from both the tables.
create table t1(
id number,
name varchar2(10)
);
create table t2(
id number,
name varchar2(10)
);
create or replace procedure get_t1_and_t2(
o_cur out sys_refcursor) is
begin
open o_cur for
select id from t1
union all
select id from t2;
end;
/
Procedure created.
SQL> var rc refcursor;
SQL> exec get_t1_and_t2( :rc);
PL/SQL procedure successfully completed.
SQL> print rc;
ID
----------
1
2
This script shows #Maxim Shevtsov answer working. Run it in SQL*Plus
SET serveroutput ON size 100000
DECLARE
c1 SYS_REFCURSOR;
c2 SYS_REFCURSOR;
v1 VARCHAR2(10);
v2 VARCHAR2(10);
n2 NUMBER;
PROCEDURE tst ( p_refcursor1 OUT SYS_REFCURSOR
, p_refcursor2 OUT SYS_REFCURSOR)
IS
BEGIN
OPEN p_refcursor1 FOR
SELECT 'val1' FROM DUAL;
OPEN p_refcursor2 FOR
SELECT 'val2', 42 FROM DUAL;
END tst;
BEGIN
tst( c1, c2 );
LOOP
FETCH c1 INTO v1;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.put_line( 'CURSOR1: ' || v1 );
END LOOP;
LOOP
FETCH c2 INTO v2, n2;
EXIT WHEN c2%NOTFOUND;
DBMS_OUTPUT.put_line( 'CURSOR2: ' || v2 || ' ' || n2 );
END LOOP;
END;
/