Inserting Data Into Table Using Execute Immediate in Oracle - sql

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.

Related

Trying to create dynamic query strings with PL/PgSQL to make DRY functions in PostgreSQL 9.6

I have tables that contain the same type of data for every year, but the data gathered varies slightly in that they may not have the same fields.
d_abc_2016
d_def_2016
d_ghi_2016
d_jkl_2016
There are certain constants for each table: company_id, employee_id, salary.
However, each one might or might not have these fields that are used to calculate total incentives: bonus, commission, cash_incentives. There are a lot more, but just using these as a examples. All numeric
I should note at this point, users only have the ability to run SELECT statements.
What I would like to be able to do is this:
Give the user the ability to call in SELECT and specify their own fields in addition to the call
Pass the table name being used into the function to use in conditional logic to determine how the query string should be constructed for the eventual total_incentives calculation in addition to passing the whole table so a ton of arguments don't have to be passed into the function
Basically this:
SELECT employee_id, salary, total_incentives(t, 'd_abc_2016')
FROM d_abc_2016 t;
So the function being called will calculate total_incentives which is numeric for that employee_id and also show their salary. But the user might choose to add other fields to look at.
For the function, because the fields used in the total_incentives function will vary from table to table, I need to create logic to construct the query string dynamically.
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS numeric AS
$$
DECLARE
-- table name lower case in case user typed wrong
tbl varchar(255) := lower($2;
-- parse out the table code to use in conditional logic
tbl_code varchar(255) := split_part(survey, '_', 2);
-- the starting point if the query string
base_calc varchar(255) := 'salary + '
-- query string
query_string varchar(255);
-- have to declare this to put computation INTO
total_incentives_calc numeric;
BEGIN
IF tbl_code = 'abc' THEN
query_string := base_calc || 'bonus';
ELSIF tbl_code = 'def' THEN
query_string := base_calc || 'bonus + commission';
ELSIF tbl_code = 'ghi' THEN
-- etc...
END IF;
EXECUTE format('SELECT $1 FROM %I', tbl)
INTO total_incentives_calc
USING query_string;
RETURN total_incentives_calc;
END;
$$
LANGUAGE plpgsql;
This results in an:
ERROR: invalid input syntax for type numeric: "salary + bonus"
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 16 at EXECUTE
Since it should be returning a set of numeric values. Change it to the following:
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS SETOF numeric AS
$$
...
RETURN;
Get the same error.
Figure well, maybe it is a table it is trying to return.
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS TABLE(tot_inc numeric) AS
$$
...
Get the same error.
Really, any variation produces that result. So really not sure how to get this to work.
Look at RESULT QUERY, RESULT NEXT, or RESULT QUERY EXECUTE.
https://www.postgresql.org/docs/9.6/static/plpgsql-control-structures.html
RESULT QUERY won't work because it takes a hard coded query from what I can tell, which won't take in variables.
RESULT NEXT iterates through each record, which I don't think will be suitable for my needs and seems like it will be really slow... and it takes a hard coded query from what I can tell.
RESULT QUERY EXECUTE sounds promising.
-- EXECUTE format('SELECT $1 FROM %I', tbl)
-- INTO total_incentives_calc
-- USING query_string;
RETURN QUERY
EXECUTE format('SELECT $1 FROM %I', tbl)
USING query_string;
And get:
ERROR: structure of query does not match function result type
DETAIL: Returned type character varying does not match expected type numeric in column 1.
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 20 at RETURN QUERY
It should be returning numeric.
Lastly, I can get this to work, but it won't be DRY. I'd rather not make a bunch of separate functions for each table with duplicative code. Most of the working examples I have seen have the whole query in the function and are called like such:
SELECT total_incentives(d_abc_2016, 'd_abc_2016');
So any additional columns would have to be specified in the function as:
EXECUTE format('SELECT employee_id...)
Given the users will only be able to run SELECT in query this really isn't an option. They need to specify any additional columns they want to see inside a query.
I've posted a similar question but was told it was unclear, so hopefully this lengthier version will more clearly explain what I am trying to do.
The column names and tables names should not be used as query parameters passed by USING clause.
Probably lines:
RETURN QUERY
EXECUTE format('SELECT $1 FROM %I', tbl)
USING query_string;
should be:
RETURN QUERY
EXECUTE format('SELECT %s FROM %I', query_string, tbl);
This case is example why too DRY principle is sometimes problematic. If you write it directly, then your code will be simpler, cleaner and probably shorter.
Dynamic SQL is one from last solution - not first. Use dynamic SQL only when your code will be significantly shorter with dynamic sql than without dynamic SQL.

how Insert data use execute immediate in oracle?

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.

pgSQL, dynamic drop tables statement

i want to dynamically delete some tables from database. I select them first, and then i am trying to use delete statement but it doesn't works.
tmp TEXT;
FOR tmp IN SELECT names from Garbage
LOOP
DROP TABLE tmp;
END LOOP;
but unfortuntly i got errors at drop statement. It always trying to delete table "tmp" instead of deleting the value of tmp(which are some strings like "table1" "table2").
You will need a dynamic query to be executed. To do that, you need to construct your query and call EXECUTE:
DECLARE
tmp TEXT;
...
BEGIN
...
FOR tmp IN SELECT names FROM Garbage
LOOP
EXECUTE 'DROP TABLE ' || quote_ident(tmp);
END LOOP;
...
Notice that I used quote_ident to escape the name properly, it is better and safer. But, if you created a table, let's say named MyTable but didn't quoted (double-quotes) it on creation time, PostgreSQL also store its name as mytable, so quote_ident('MyTable') will generate "MyTable" which doesn't exists, you should take care of it (e.g. always use the lowercase names or always quote your IDs).

Inserting query results into table named by a variable

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)')

Oracle: select into variable being used in where clause

Can I change the value of a variable by using a select into with the variable's original value as part of the where clause in the select statement?
EI would the following code work as expected:
declare
v_id number;
v_table number; --set elsewhere in code to either 1 or 2
begin
select id into v_id from table_1 where name = 'John Smith';
if(v_table = 2) then
select id into v_id from table_2 where fk_id = v_id;
end if;
end;
Should work. Have you tried it? Any issues?
After parsing your select statements should have bind variables where your v_id is. The substitution is made when the statement is actually executed.
Edit:
Unless you're sticking constants into your queries, Oracle will always parse them into statements with bind variables - it enables the DBMS to reuse the same basic query with multiple values without reparsing the statement - a huge performance gain. The whole idea of a bind variable is runtime substitution of values into a parsed query. Think of it this way: in order to process a query, all of the values need to be known. You send them to the engine, Oracle does it's work, and returns a result. It's a serial process with no way for the output value to step on the input one.