Is it possible to execute immediate like this - plsql

This code works:
uno:=1;
dos :='insert into TRABAJADOR('||inValuesToInsert||') values('||inValuestoPas||')';
execute IMMEDIATE dos using uno,addColN, addColS(8),addColD,addColS(100),
addColS(60),addFKColN('DEPT'),addFKColS('PAIS'),addFKColS('CATEGORIA') ;
But I need to do the execute immediate statement like this (the variable tres contains the data below, sorry for the syntax, I know it's not well done):
tres:= 'dos using uno,addColN, addColS(8),addColD,addColS(100),
addColS(60),addFKColN('DEPT'),addFKColS('PAIS'),addFKColS('CATEGORIA')'
uno:=1;
dos :='insert into TRABAJADOR('||inValuesToInsert||') values('||inValuestoPas||')';
execute IMMEDIATE dos using tres;
Is this possible? Please give to me another suggestion in case of do not be possible. Thanks

First of all, you need to understand the difference between just random part of string of SQL query (for example, ' from table1 t1 join table2 t2 ') and parameter. Parameter is a certain placeholder, which always means value of something (field, variable, etc.), that will be provided later, in runtime. In USING clause of EXECUTE IMMEDIATE statement you can use only parameters. If you want to construct your query in runtime from parts, you can do it, but you have to process names of fields and parameters by different methods. Example:
declare
field_list varchar2(100) := 'my_field1, my_field2, my_field3';
table_name varchar2(30) := 'my_table';
v1 number := 1;
v2 number := 2;
v3 number := 3;
begin
execute immediate 'insert into ' || table_name || '(' || field_list ||
') values (:p1, :p2, :p3)' using v1, v2, v3;
end;
/
Here :P1, :P2 and :P3 is parameters, that will be replaced by their values from variables v1, v2 and v3. table_name and field_list from the point of view of a server is NOT parameters, it is static names of database objects.

Related

Create a table as, where 'date condition' in dynamic PL/SQL

I got assigned the following task.
Assume we have a table A structured with an id column and a date column.
Write a procedure in PL/SQL that: takes as parameters the table name (in our case A) and a date D, creates a backup table named A_bck containing only the records of A with dates < D and removes from the table A all the records inserted in A_bck.
Here there is my code.
Unluckily I get this error:
Error report -
ORA-00904: "MAY": invalid identifier
ORA-06512: at line 41
ORA-06512: at line 80
00904. 00000 - "%s: invalid identifier"
If I try to achieve the same result using a where condition on the id column instead that on the date one, I have no problems.
Where is the mistake? Am I implementing it completely in the wrong way?
The problem you have is that as you're executing dynamic sql you're query is built up as a string. Oracle does not know that the date you've given is actually a date, it is simply being treated as part of the string. To solve this you should be able to do the following:
my_query := 'CREATE TABLE ' || table_name_backup || ' AS (SELECT * FROM ' || table_name || ' WHERE table_date < to_date(''' || backup_date || '''))';
This should sort out your issue for you. As a side note, you will probably want to change your "table_exists" query, as table names are all stored in upper case, e.g.
SELECT COUNT(*) INTO table_exists FROM USER_TABLES WHERE TABLE_NAME = upper(my_table);
Edit: Further explanation following comment
To explain why you don't have the above problem when using integers, it is important to remember that using execute immediate simply executes the given string as an SQL query.
For example:
declare
x INTEGER := 1;
i integer;
my_query VARCHAR2(256);
begin
my_query := 'select 1 from dual where 1 = ' || x;
EXECUTE IMMEDIATE my_query INTO i;
end;
my_query in the above example would be executed as:
select 1 from dual where 1 = 1
which is perfectly valid sql. In your example however, you were ending up with something like this:
CREATE TABLE abaco_bck AS (SELECT * FROM abaco WHERE table_date < 27-MAY-17)
As it isn't wrapped in quotes, or explicitly converted to a date, the SQL engine is trying to subtract "MAY" from 27, but it doesn't know what "MAY" is.
One other thing to mention, is that for some operations you could use bind variables instead of quotes (although you can't for DDL) e.g.
declare
lToday DATE := SYSDATE;
i INTEGER;
my_query VARCHAR2(256);
begin
my_query := 'select 1 from dual where sysdate = :1';
EXECUTE IMMEDIATE my_query INTO i USING lToday;
end;

Parsing string in PL/SQL and insert values into database with RESTful web service

I am reading the values which I want to insert into database. I am reading them by lines. One line is something like this:
String line = "6, Ljubljana, Slovenija, 28";
Web service needs to separate values by comma and insert them into database. In PL/SQL language. How do I do that?
Here is some pl/sql that I have used to parse through delimited strings and then extract the individual words. You may have to mess with it a bit when using with the web service but it works fine when you are running it right in oracle.
declare
string_line varchar2(4000);
str_cnt number;
parse_pos_1 number := 1;
parse_pos_2 number;
parsed_string varchar2(4000);
begin
--counting the number of commas in the string so we know how many times to loop
select regexp_count(string_line, ',') into str_cnt from dual;
for i in 1..str_cnt + 1
loop
--grabbing the position of the comma
select regexp_instr(string_line, ',', parse_pos_1) into parse_pos_2 from dual;
--grabbing the individual words based of the comma positions using substr function
--handling the last loop
if i = str_cnt + 1 then
select substr(string_line, parse_pos_1, length(string_line)+1 - parse_pos_1) into parsed_string from dual;
execute immediate 'insert into your_table_name (your_column_name) values (' || parsed_string || ' )';
execute immediate 'commit';
--handles the rest
else
select substr(string_line, parse_pos_1, parse_pos2 - parse_pos_1) into parsed_string from dual;
execute immediate 'insert into your_table_name (your_column_name) values (' || parsed_string || ' )';
execute immediate 'commit';
end if;
parse_pos_1 := parse_pos_2+1;
end loop;
end;
I found an answer to that particular question. If you have similar values to those I posted for a question, like numbers, which look something like this:
String line = "145, 899";
This string is sent via POST request (RESTful web service, APEX). Now getting the values in PL/SQL and inserting them into table looks something like this:
DECLARE
val1 NUMBER;
val2 NUMBER;
str CLOB;
BEGIN
str := string_fnc.blob_to_clob(:body); // we have to convert body
val1 := TO_NUMBER(REGEXP_SUBSTR(str, '[^,]+', 1, 1));
val2 := TO_NUMBER(REGEXP_SUBSTR(str, '[^,]+', 1, 2));
// REGEXP_SUBSTR(source, pattern, start_position, nth_appearance)
INSERT INTO PRIMER VALUES (val1, val2);
END;
However, this is the method to insert line by line into database, so if you have large amount of rows in a file to insert, this isn't a way to do it. But here is the example which I requested. I hope it helps to someone.

select the union of several tables together in a single step

I am very new to Oracle 11g and am trying to generate a large string by appending text for each column in a select statement and using a cursor to store the results. However I want the last statement to not have a union all included. The final result I want to build large string of each row generated or simply execute the result if possible.
Note: column1 has a list of schemas that I am interested in.
select 'select * from ' || column1 || '.' || column2 || ' union all ' from mytable
This is where column1 is the schema, column2 is the table name.
What is the simplest way to generate the final string without using rtrim to remove the last string. And is there a simple way to append all these rows together in the string automatically?
The final goal is to actually just execute the union into a resulting cursor.
If you're querying in a loop anyway I wouldn't try to construct the string as part of the select at all; I'd do it all within the loop. Something like (untested):
declare
str varchar2(32768);
begin
for rec in (select column1, column2, rownum as rn from mytable)
loop
if rec.rn > 1 then
str := str || ' union all ';
end if;
str := str || 'select * from "' || rec.column[ || '"."' || rec.column2 ||'"';
end loop;
-- do something with str e.g. display to verify the syntax
-- before using in a cursor
dbms_output.put_line(str);
end;
Rather than adding union all to the end of every row except the last one,the rn check means it's added to the start of every row except the first one, which is easier to detect.
I've also wrapped the schema and table names in double quotes, just in case you have to deal with any quoted identifiers. But if your stored values don't match the case of the owners and table names in all_tables this will cause a problem rather than solve it.

PLS-00103: Encountered the symbol ","

This procedure is getting following error.
CREATE OR REPLACE PROCEDURE SAMPLE
IS
BEGIN
EXECUTE IMMEDIATE
'CREATE TABLE COLUMN_NAMES AS (
SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP (ORDER BY COLUMN_NAME) AS STUDENTS
FROM
(SELECT DISTINCT COLUMN_NAME
FROM BW_COLUMN_ROW_CELL_JOIN)
)';
END;
/
gives:
PLS-00103: Encountered the symbol "," when expecting one of the following:
* & = - + ; < / > at in is mod remainder not rem return
returning <an exponent (**)> <> or != or ~= >= <= <> and or
like like2 like4 likec between into using || multiset bulk member submultiset
Can any one say what is wrong in this?
Thanks.
Another way (in Oracle 10g and later) is to use the alternative string literal notation - this means you don't need to worry about correctly escaping all the single quotes in the string, e.g. q'{my string's got embedded quotes}':
CREATE OR REPLACE PROCEDURE SAMPLE
IS
BEGIN
EXECUTE IMMEDIATE q'[
CREATE TABLE COLUMN_NAMES AS (
SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP (ORDER BY COLUMN_NAME) AS STUDENTS
FROM
(SELECT DISTINCT COLUMN_NAME
FROM BW_COLUMN_ROW_CELL_JOIN)
)]';
END;
/
The problem I think is you have single quotes within single quotes. I cant test this at the moment, but I'd suggest you try the following (note the inner quotes are double quotes '', which escapes them:
CREATE OR REPLACE PROCEDURE SAMPLE
IS
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE COLUMN_NAMES AS ( SELECT LISTAGG(COLUMN_NAME, '','') WITHIN GROUP (ORDER BY COLUMN_NAME) AS STUDENTS FROM (SELECT DISTINCT COLUMN_NAME FROM BW_COLUMN_ROW_CELL_JOIN) )';
END;
/
I'd also try the create table part of the code standalone first just to make sure its valid before wrapping it in a proc.
You can't use single quotes directly in select statement of Execute Immediate it need to be coded using CHR(39)
CREATE OR REPLACE PROCEDURE SAMPLE
IS
BEGIN
EXECUTE IMMEDIATE
'CREATE TABLE COLUMN_NAMES AS (
SELECT LISTAGG(COLUMN_NAME,'||chr(39)||','||chr(39)||') WITHIN GROUP (ORDER BY COLUMN_NAME) AS STUDENTS
FROM
(SELECT DISTINCT COLUMN_NAME FROM BW_COLUMN_ROW_CELL_JOIN))';
END;

How i can pass column names from variables in plsql update statement

DECLARE
v_name A.TRANSACTION_TYPE%TYPE :='SALARY';
v_salary A.SALARY%TYPE := 1000;
BEGIN
update A set v_name= v_salary where EMPID = 517;
-- PL/SQL: ORA-00904: "v_name": invalid identifier
--update A set SALARY = 1000 where EMPID = 517;
END;
/
My idea is to update table columns , but these column names are stored in variable. Is there any way to pass column names from variable ? Is there any options apart from Execute Immediate
Not sure if this will work in your situation, but I've written solutions where I wrote a script in SQLPlus and it "wrote" (using dbms_output.put_line or even just prompt) another script that did queries, and the columns/tables in those queries was determined by the logic in the SQLPlus script. Then I would execute as a script the output from my first script, and it would execute dynamically generated queries without ever needing execute immediate.
The following idea may work for multiple columns that are typed the same... As written, it will update all columns every time for a given record, but only the column specified by v_name will be changed to the value set in v_value; the other columns are simply updated to their existing value. The idea can be played with using DECODE, NVL or other similar conditional operators.
declare
v_name varchar2(20):= 'SAL';
v_value emptest.sal%TYPE := 5000;
begin
update emptest
set sal = ( select case when v_name = 'SAL' then v_value else sal end from dual),
comm = ( select case when v_name = 'COMM' then v_value else comm end from dual)
where empno = 7369;
commit;
end;

Resources