I am using oracle 10g . I want execute dynamic insert query into that fields are select from another query in procedure .
sql_stmt := 'INSERT INTO tt_causalvarien VALUES(:breakdate, :batch, :procode, :rpcaption, :rvminmaxs, :rpvalue)';
EXECUTE IMMEDIATE sql_stmt USING select brkngdte,batch,prdtcode,'RP1','S',getminmax('qtyadded','vttrcwfdetatdryer','S','prdcode','PRC002','vttrcwfaddatdryer') Test_Sample
from skybluem.VMtrcwfhdailyplansheet;
Can you please help ? And can you give me tips how to write this type of queries.
Not sure if i got your example right by what is static and what is dynamic, but in general for that purpose you don't need execute immediate, you can just insert into a table with a select from:
INSERT INTO tt_causalvarien
SELECT brkngdte,batch,prdtcode,'RP1','S',getminmax('qtyadded','vttrcwfdetatdryer','S','prdcode','PRC002','vttrcwfaddatdryer')
FROM skybluem.VMtrcwfhdailyplansheet;
Dynamic queries are used to build SQL statement at runtime. As you have access to the whole statement as text, you have much more flexibility than by using SQL statements that are known at compilation time. See Oracle's documentation.
Broadly speaking, you need dynamic SQL if you need to change something else than a value in your SQL query.
Given your example, you have 3 options:
As explained by #evilive in her own answer, given your example, you can rewrite your statement as a simple INSERT ... SELECT. Please note this does not necessarily insert one row. Depending your data/exact query, this can trigger the insert of several rows. Or of no rows at all.
You can use PL/SQL SELECT .. INTO and data substitution (
don't forget to declare as needed the variables:
breakdate, batch, procode,
rpcaption, rvminmaxs,
rpvalue)
select "brkngdte", "batch", "prdtcode", 'RP1', 'S',
getminmax('qtyadded', 'vttrcwfdetatdryer', 'S', 'prdcode', 'PRC002', 'vttrcwfaddatdryer') Test_Sample
into breakdate, batch, procode, rpcaption, rvminmaxs,
rpvalue
from skybluem.VMtrcwfhdailyplansheet;
INSERT INTO tt_causalvarien VALUES(breakdate, batch, procode, rpcaption, rvminmaxs, rpvalue);
This will trigger an exception if the `select` statement returns no row or several rows.
If you really need to build the INSERT query dynamically (say because the name of the table in only known at runtime):
sql_stmt := 'INSERT INTO ' || some_table || ' VALUES(:breakdate, :batch, :procode, :rpcaption, :rvminmaxs, :rpvalue)';
-- ^^^^^^^^^^^^^^^^
-- *this* require dynamic SQL
select "brkngdte", "batch", "prdtcode", 'RP1', 'S',
getminmax('qtyadded', 'vttrcwfdetatdryer', 'S', 'prdcode', 'PRC002', 'vttrcwfaddatdryer') Test_Sample
into breakdate, batch, procode, rpcaption, rvminmaxs,
rpvalue
from skybluem.VMtrcwfhdailyplansheet;
EXECUTE IMMEDIATE sql_stmt USING breakdate, batch, procode, rpcaption, rvminmaxs, rpvalue;
Once again, as you are using SELECT ... INTO the PL/SQL runtime ensure that one and only one row will be selected. And will trigger an exception if this is not the case.
In addition, as per OP request, if the SELECT query is dynamic too, you might use EXECUTE IMMEDIATE ... INTO to fetch the row data. After that, depending your use case, you can go either for a EXECUTE IMMEDIATE ... USING of a plain INSERT statement. Here is a "fully dynamic" example:
sel_stmt := 'select "brkngdte", "batch" ... from skybluem.VMtrcwfhdailyplansheet';
sql_stmt := 'INSERT INTO ' || some_table || ' VALUES(:breakdate, :batch, :procode, :rpcaption, :rvminmaxs, :rpvalue)';
EXECUTE IMMEDIATE sel_stmt
INTO breakdate, batch, procode, rpcaption, rvminmaxs, rpvalue;
EXECUTE IMMEDIATE sql_stmt
USING breakdate, batch, procode, rpcaption, rvminmaxs, rpvalue;
Please see the EXECUTE IMMEDIATE reference documentation for the various supported syntax and Using the EXECUTE IMMEDIATE Statement in PL/SQL for some introduction material.
Related
I'm doing something very similar to dnoeth's second comment on this question:
Insert Into table Teradata dynamic stored procedure SQL
I need to run it multiple times to loop the same insert statement but with different values for the "?" and I'm not sure how to go about that.
The dynamic value in my version is a date span. I can't run a massive insert without spooling out so I've broken the data into segments.
Thanks.
William,
As you are aware that you need to use cursor to iterate through each value you want to process.
For that purpose store dynamically created INSERT statement into a variable.
Final step: Use EXECUTE IMMEDIATE command to run insert statement stored in teradata variable
Here is the working sample that you may refer
REPLACE PROCEDURE td_user.sp_dynamic_insert( OUT proc_msg VARCHAR(5000) )
BEGIN
DECLARE lv_insert_txt VARCHAR(20000);
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
/* Error handling code if required */
END;
L0:
FOR insert_cursor AS select_list
CURSOR FOR
SELECT Col2
FROM test_1
DO
SET lv_insert_txt = 'INSERT INTO test_2(Col1,Col2) VALUES('||TRIM(insert_cursor.Col2)||','||TRIM(insert_cursor.Col2)||')';
EXECUTE IMMEDIATE lv_insert_txt;
END FOR L0;
SET proc_msg = 'Procedure completed successfully';
END;
For example, I have some table "Test" which has one column "my_date" . What I am trying to do is adding record to a table using some variable:
query_date := "SELECT sysdate FROM dual";
EXECUTE IMMEDIATE ('insert into test values query_date');
I need to insert the record to the table in this exact way by constructing string and executing query, however I get error. Is it possible to do so?
You can either get the result of the first query into a (date) variable and then use that:
SELECT sysdate into query_date FROM dual;
insert into test (my_date) values (query_date)
-- or if you really want dynamic SQL, with a bind variable
EXECUTE IMMEDIATE 'insert into test (my_date) values (:query_date)' using query_date;
Or reading your question literally, use the first string as part of the second string by concatenating it:
query_date := "SELECT sysdate FROM dual";
EXECUTE IMMEDIATE 'insert into test (my_date) ' || query_date;
If you printed out the second statement instead of executing it you'd see:
insert into test (my_date) SELECT sysdate FROM dual
... which is valid SQL. This will work if the query_string is more complicated or is itself being constructed dynamically. But if the number of column expressions in the query_string select list also varies, you will have to construct the column list dynamically too, otherwise you'll have too many or too few columns for the insert.
Exactly how you do that depends on how you're constructing the query string - essentially as you add an expression to the query string, you'd also add a column name to a separate list, and end up with:
EXECUTE IMMEDIATE 'insert into test (' || column_list ' ||) ' || query_string);
where column_list is built up as say col1, col2 and query_string as select x.col1, y.col2 from ....
There isn't an obvious reason to use dynamic SQL in what you've shown. Or, if you are really using sysdate, any need for separate query to get that, as you can just do:
insert into test (my_date) values (sysdate)
... so I assume your real scenario is really more complicated. But note that you don't use the values keyword with an insert ... select ... pattern. You can with a single column and a subquery but it's not a good idea even then, and doesn't work if you have multiple columns in the subquery.
Why you need an EXECUTE IMMEDIATE for Insert statement ?
As long as the base table where you are inserting the values remain same we dont need to do EXIMM. Now the query_date ? Just do a traditional looping or variable thing.
I have more than 40 tables in RATOR_MONITORING schema for which the table name is starting from 'TEMP_'. I want to delete data from all such tables at once in a single query instead of using delete statement for each and every table. I dont even want to generate statements. I can create anonymous block if required but dont know how to do that. I tried below query but its not working.
Delete from RATOR_MONITORING_CONFIGURATION.'%TEMP_';
If you want to delete all the rows, then better use TRUNCATE, it will reset the high watermark. But remember, truncate is a DDL statement, and thus there will be an implicit commit; With DELETE you can commit manually after validation.
Although, I would not do that in a production environment. If it is something you are doing in test environment to build test data, then you could (ab)use EXECUTE IMMEDIATE.
For example, execute the following anonymous block as RATOR_MONITORING user:
DECLARE
v_sql VARCHAR2(100);
BEGIN
FOR i IN
(SELECT table_name FROM user_tables where table_name like 'TEMP%'
)
LOOP
v_sql := 'TRUNCATE TABLE '||i.table_name;
EXECUTE immediate v_sql;
END LOOP;
END;
/
By the way, using a good text editor, it won't take more than a minute to build DELETE/TRUNCATE statements and do it in pure SQL.
I have a stored procedure which runs many queries and obtains a lot of values which it stores in variables. I want this procedure to be able to be able to insert the query results into a table which is given by the user. I would store the given table name as a varchar parameter but how can I insert into this table?
At compile time, Oracle is saying that the table does not exist.
Presumably it's telling you that a table with the name of the variable you're using doesn't exist, which of course it won't. The actual table may or may not exist at compile time; since it's flexible it's probably safer to assume it might not. Either way you don't know what it will be so you need to use dynamic SQL to achieve this.
As mentioned in a comment on another answer, you have to be careful about SQL injection. Normally you'd want to use bind variables in the dynamic SQL, but you can't use binds for object names, so you have to concatenate it. Hopefully you're using 11g, which includes the dbms_assert package.
Here's a simple example:
create or replace procedure p42 (table_name varchar2) as
begin
execute immediate 'insert into '
|| dbms_assert.qualified_sql_name(table_name)
|| ' select * from dual';
end;
/
I can then create a table after the procedure already exists, and successfully call the procedure:
create table t42 (dummy varchar2(1));
exec p42('t42');
select * from t42;
DUMMY
-----
X
Your real query will obviously be more complicated, and should use bind variables for any filter values you're passing in along with the target table name.
The advantage of the dbms_assert call is that it will error if something illegal is passed in, and if something nasty is passed:
exec p42('t42 select ''Y'' from dual union all');
ORA-44004: invalid qualified SQL name
ORA-06512: at "SYS.DBMS_ASSERT", line 207
ORA-06512: at "STACKOVERFLOW.P42", line 3
ORA-06512: at line 1
If the procedure simply concatenated the passed value:
execute immediate 'insert into ' || table_name || ' select * from dual';
... then that same call would insert two rows into the table:
exec p42('t42 select ''Y'' from dual union all');
select * from t42;
DUMMY
-----
Y
X
Which is something to worry about if the data integrity is at all important to you. If you can't use dbms_assert then you could try to check that the passed name actually exists in all_tables and doesn't contain anything like a union etc. but it's much safer to acknowledge that you won't think of all the possible attacks and let the built-in function do the hard work for you.
Try something like this:
exec ('insert into ' + #tblname + ' (col) values (123)')
I am converting a MSSQL script to Oracle, and I haven't been able to figure out the syntax to use a variable in place of a table name or column.
Here is a simple example that I've been try to make work in Oracle SQL Developer so I can better understand the syntax:
set serveroutput on format wrapped;
declare
VR_TABLE VARCHAR2(256);
VR_UPDATE VARCHAR2(256);
begin
VR_TABLE :='SYSTEM_STATUS';
EXECUTE IMMEDIATE 'select UPDATE_VERSION INTO VR_UPDATE from ' || VR_TABLE || 'where rownum < 2 ;'
end;
Where VR_TABLE is the variable table name that will get changed each iteration of the loop.
Can somebody point out what I'm doing wrong, or link me to a site that would be useful for me to read? I've read a few tutorials on this, but I haven't had any luck thus far.
You need to have a space between the table name and the subsequent WHERE clause
The INTO needs to be part of the EXECUTE IMMEDIATE, not part of the dynamic SQL statement.
The dynamic SQL statement should not have a trailing semicolon
The EXECUTE IMMEDIATE statement should end with a semicolon
Putting those together, something like this should work
declare
VR_TABLE VARCHAR2(256);
VR_UPDATE VARCHAR2(256);
begin
VR_TABLE :='SYSTEM_STATUS';
EXECUTE IMMEDIATE 'select UPDATE_VERSION from ' || VR_TABLE || ' where rownum < 2'
INTO VR_UPDATE;
end;
Of course, since you're not doing anything with VR_UPDATE, nothing will be displayed when this anonymous block is executed.
INTO part of the query should not be directly included in the query
string.
Syntax
EXECUTE IMMEDIATE(<SQL>)
[INTO<variable>]
[USING <bind_variable_value>]
The above syntax shows EXECUTE IMMEDIATE command.
Clause INTO is optional and used only if the dynamic SQL contains a select statement that fetches values. The variable type should match with the variable type of the select statement.
Clause USING is optional and used only if the dynamic SQL contains any bind variable.
https://www.guru99.com/dynamic-sql-pl-sql.html#2
You can visit this site for a better understanding of Dynamic SQL.