Execute immediate with variable throwing error ORA-00936: missing expression - sql

Getting missing expression error when my procedure reaches this statement.
ORA-00936: missing expression
What is wrong with this statement?
EXECUTE IMMEDIATE 'SELECT /*+ parallel (8) */ COUNT(1)
INTO '||v_datacount_backuptable||
' FROM cs_transassignment_26weeks';

What you are trying to achieve is unclear. In any case, you cannot pass a variable name as a variable.
If you want to feed the count into the variable from a fixed query, then you can skip the EXECUTE IMMEDIATE: it is meant to execute a query that is dynamically composed, which is not the case in the code that you showed:
declare v_datacount_backuptable int;
begin
SELECT /*+ parallel (8) */ COUNT(1) INTO v_datacount_backuptable FROM cs_transassignment_26weeks;
dbms_output.put_line(v_datacount_backuptable);
end;
/
If you actually built the query somewhere else and you need to execute it, then, as answered by Littlefoot already, you need to move the INTO clause out of the query string:
declare
v_datacount_backuptable int;
v_query varchar(500);
begin
v_query := 'SELECT /*+ parallel (8) */ COUNT(1) FROM cs_transassignment_26weeks';
EXECUTE IMMEDIATE v_query INTO v_datacount_backuptable;
dbms_output.put_line(v_datacount_backuptable);
end;
/
Demo on DB Fiddle

Should be
EXECUTE IMMEDIATE 'SELECT /*+ parallel (8) */ COUNT(1) FROM cs_transassignment_26weeks'
INTO v_datacount_backuptable;
i.e. INTO is outside of the statement being executed.

Related

PL/SQL: ORA-00942, error reported in logically unreachable else block. Needed to add EXECUTE IMMEDIATE for it to work. Why?

I am trying to write a PL/SQL script, which executes a few SQL statements if a certain table exists/not exists.
For eg:
SET SERVEROUTPUT ON;
DECLARE
cnt NUMBER := 0;
cnt_2 NUMBER := 0;
BEGIN
SELECT count(*) INTO cnt FROM all_tables where TABLE_NAME='DOES_NOT_EXIST';
IF cnt = 0 THEN
dbms_output.put_line('Table does not exist');
ELSE
dbms_output.put_line('Table exists ' || cnt);
SELECT COUNT(*) INTO cnt_2 from DOES_NOT_EXIST;
END IF;
END;
/
When I execute this, I get below error
SELECT COUNT(*) INTO cnt_2 from DOES_NOT_EXIST;
*
ERROR at line 10:
ORA-06550: line 10, column 37:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 10, column 5:
PL/SQL: SQL Statement ignored
But, if I add EXECUTE IMMEDIATE in line 11. Then it works fine.
SET SERVEROUTPUT ON;
DECLARE
cnt NUMBER := 0;
cnt_2 NUMBER := 0;
BEGIN
SELECT count(*) INTO cnt FROM all_tables where TABLE_NAME='DOES_NOT_EXIST';
IF cnt = 0 THEN
dbms_output.put_line('Table does not exist');
ELSE
dbms_output.put_line('Table exists ' || cnt);
EXECUTE IMMEDIATE 'SELECT COUNT(*) INTO cnt_2 from DOES_NOT_EXIST';
END IF;
END;
/
Now it works
Table does not exist
PL/SQL procedure successfully completed.
Could you please help me understand why an error is being reported if the ELSE block cannot be reached logically? The cnt variable will always be 0, then, I would assume the ELSE block is never reached.
How come adding EXECUTE IMMEDIATE is not producing the error?
Does this mean, I should add EXECUTE IMMEDIATE for all such statements in the ELSE block?
I searched on SO and other places but couldn't find an answer. Honestly, I don't know what other search terms to use. All search hits leads to generic errors. So, I am asking this question. If this is already answered, please point me to it and close this.
Thank you.
What you are missing is that statements are processed in two steps; they are compiled before they are executed.
During the compilation phase, the identifiers (tables and columns and more) are identified and looked up. If a table name (or column name or whatever) is not found, then you get a compilation error.
Using execute immediate short-circuits this process. Instead of "executing immediately" this is really "delaying compilation". The statement is both compiled and executed during run time. That is why execute immediate prevents the error.
Looks like you need something like this:
select
owner,
table_name,
xmlcast(
xmlquery(
'/ROWSET/ROW/CNT'
passing xmltype(dbms_xmlgen.getXML('select count(*) cnt from "'||owner||'"."'||table_name||'"'))
returning content null on empty
)
as int
) as cnt
from all_tables
where owner in ('XTENDER',user);
This query returns number of rows for each table from all_tables filtered by your predicates

db2 dynamic sql with select into clause in anonymous block

I am working on a DB2 SQL code.
I need to get the count of records from a list of tables. The table details will be fetched from a cursor with select.
To get the count of records I have been trying with a SELECT INTO statement. Since the table names will be varying, I am using a dynamic SQL code.
I am sharing the piece of code that I have been trying.
I am not quite sure of the syntax while using DB2 SELECT INTO and Dynamic SQL combination. I am getting the following error with the below attempt.
Can anyone tell me why this is so? If possible, appreciate if you could share a working code of DB2 select into and dynamic sql.
SQL0104N An unexpected token "statmnt2" was found following "SET statmnt2 := 'set ? = (SELECT COD_TIPO_ARQU FROM '||indbn". Expected tokens may include: "".
DECLARE
indbnm VARCHAR(30);
intblnm VARCHAR(30);
v_errorText varchar2(50);
statmnt2 VARCHAR(1000);
VAR_COD_TIPO_ARQU CHAR(1);
stmt1 STATEMENT;
statmnt2 VARCCHAR2(100);
BEGIN
indbnm := "db2inst5";
intblnm:= "rules";
SET statmnt2 := 'set ? = (SELECT COD_TIPO_ARQU FROM '||indbnm||'.'||intblnm||' FETCH FIRST 1 ROWS ONLY)';
PREPARE stmt1 FROM statmnt2;
EXECUTE stmt1 into VAR_COD_TIPO_ARQU ;
DBMS_OUTPUT.PUT_LINE(VAR_COD_TIPO_ARQU);
EXCEPTION
WHEN OTHERS THEN
v_errorText :=SUBSTR(SQLERRM,1, 1024);
DBMS_OUTPUT.PUT_LINE('FAILED WITH MESSAGE: '||v_errorText);
END;

How to get tables to be created immediately in nested pl/sql block

I am creating tables, then taking the count of those tables and storing them in variables, all inside a nested pl/sql block on an Oracle database. I am getting error "ORA-00942: table or view does not exist". If I create the tables first, then run my block, it executes with no errors.
Therefore, I know the tables aren't being created, even though I am using the EXECUTE IMMEDIATE command. (I thought this command would bypass the fact that all objects used in an PL/SQL block must exist BEFORE the PL/SQL block is executed.)
It will be difficult at this point to create the tables outside of my block, is there another way?
Sample code below:
DECLARE
g_user varchar2(30) := 'schema';
BEGIN
/* more code and nested blocks*/
DECLARE
v_Count_Task1A number(6);
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE ' ||g_user||'.T_Task1A_Roles_w_User_IDs AS
SELECT ROWNUM AS Dummy_Field, USUS_ID AS Task1A_Role, User_ID AS Task1A_User_ID
FROM Task1A_Roles_w_User_IDs';
SELECT count(1) INTO v_Count_Task1A
FROM T_Task1A_Roles_w_User_IDs;
/*more operations like this*/
END;
/* more code and nested blocks*/
END;
The problem isn't that your table doesn't get created when the block runs, the problem is that the table doesn't exist when the PL/SQL is compiled and so it can't be compiled and therefore cannot be run at all.
The solution here is to use more dynamic SQL to perform the count, since dynamic SQL is not parsed until execution of the block:
DECLARE
g_user varchar2(30) := 'schema';
BEGIN
/* more code and nested blocks*/
DECLARE
v_Count_Task1A number(6);
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE ' ||g_user||'.T_Task1A_Roles_w_User_IDs AS
SELECT ROWNUM AS Dummy_Field, USUS_ID AS Task1A_Role, User_ID AS Task1A_User_ID
FROM Task1A_Roles_w_User_IDs';
EXECUTE IMMEDATE 'SELECT count(1) FROM T_Task1A_Roles_w_User_IDs'
INTO v_Count_Task1A;
/*more operations like this*/
END;
/* more code and nested blocks*/
END;
You need SELECT INTO in dynamic sql:
DECLARE
g_user varchar2(30) := 'schema';
BEGIN
/* more code and nested blocks*/
DECLARE
v_Count_Task1A number(6);
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE ' ||g_user||'.T_Task1A_Roles_w_User_IDs AS
SELECT ROWNUM AS Dummy_Field, USUS_ID AS Task1A_Role, User_ID AS Task1A_User_ID
FROM Task1A_Roles_w_User_IDs';
EXECUTE IMMEDIATE '
SELECT count(1)
FROM T_Task1A_Roles_w_User_IDs'
INTO v_Count_Task1A;
/*more operations like this*/
END;
/* more code and nested blocks*/
END;

variable as column name in where clause in Oracle PL/SQL

I am working on PL/SQL code where I need to perform a select query using variable as column name in where clause. Column names are stored in a table as varchar and I am using a loop to pass those column names to my select statement.
Please find sample code segment I am trying to run:
set serveroutput on;
declare
var varchar2(100);
counter number;
begin
var:='description';
select count(*)
into counter
from nodetable
where var like '%Ship%';
dbms_output.put_line(counter);
end;
Output:
anonymous block completed
0
However the result should be 86.
Oracle is comparing last condition as two string and not column=string.
Please let me know if this is even feasible in oracle or if there is a workaround for it.
Regards
Ankit
You have to use dynamic SQL, preferrably with bind-variables:
EXECUTE IMMEDIATE
'select count(*) from nodetable where '||var||' like :p1'
INTO counter
USING '%Ship%';
Try this
declare
var varchar2(100);
counter number;
begin
var:='description';
EXECUTE IMMEDIATE
'select count(*)
into counter
from nodetable
where '||var||' like ''%Ship%'' ';
dbms_output.put_line(counter);
end;
You need to be carefull with the colon's (').
I agreed with previous answer in implementation, but i strictly recommend you to change your technical requirements, because you can't use bind variables for this, and it's potential place for injection. For example, if someone will edit value in your table which stores column names, to something like that: "description = inject_function or description". Then your dynamic sql block will execute this statement:
select count(*) from nodetable where description = inject_function or description like '%Ship%
and example implementation of function
create function inject_function
return varchar2
is pragma autonomous_transaction;
begin
delete * from most_important_table;
commit;
return to_char(null);
exception when others then
rollback;
return to_char(null);
end;

Missing Expression in Oracle when I try inserting a variable in update statement

I am trying to create an update which will update the table with a variable declared by me, but it is giving me an error which says"Missing expression". I am at a loss as to what could possibly cause this.
Here is my code.
DECLARE
VBILL BILL%ROWTYPE;
CURSOR BILLAMT IS
SELECT * FROM BILL;
VCUST1 NUMBER(3);
VCUST2 NUMBER(3);
VCUST3 NUMBER(3);
BEGIN
SELECT FREQUENCY INTO VCUST1 FROM RANGEOFBILLS WHERE RANGE=100;
SELECT FREQUENCY INTO VCUST2 FROM RANGEOFBILLS WHERE RANGE=1000;
SELECT FREQUENCY INTO VCUST3 FROM RANGEOFBILLS WHERE RANGE=10000;
OPEN BILLAMT;
LOOP
FETCH BILLAMT INTO VBILL;
EXIT WHEN BILLAMT%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(VBILL.AMOUNT);
IF VBILL.AMOUNT>100 and VBILL.AMOUNT<=1000 THEN
VCUST1:=VCUST1+1;
EXECUTE IMMEDIATE('UPDATE RANGEOFBILLS SET FREQUENCY=#VCUST1 WHERE RANGE=100');
DBMS_OUTPUT.PUT_LINE(VBILL.AMOUNT);
END IF;
END LOOP;
CLOSE BILLAMT;
END;
I think the problem is with the execute immediate statement. I even tried this:
EXECUTE IMMEDIATE ('UPDATE RANGEOFBILLS SET FREQUENCY=:a WHERE RANGE=100' USING VCUST1);
You don't need EXECUTE IMMEDIATE for a DML statement. Just do
UPDATE RANGEOFBILLS SET FREQUENCY = VCUST1 WHERE RANGE = 100;
And I'm not sure what you're trying to do, but I imagine you can do this in a single UPDATE statement without the loop, which would make it much more efficient.
Remove the parentheses from the second attempt:
EXECUTE IMMEDIATE 'UPDATE RANGEOFBILLS SET FREQUENCY=:a WHERE RANGE=100'
USING VCUST1;
But there is no need for dynamic SQL, it would be better to use UPDATE RANGEOFBILLS SET FREQUENCY=vcust1 WHERE RANGE=100;. Also, it is generally a good idea to avoid CURSOR/OPEN/FETCH/EXIT WHEN/CLOSE. Implicit cursor for-loops are simpler and faster:
DECLARE
VCUST1 NUMBER(3);
VCUST2 NUMBER(3);
VCUST3 NUMBER(3);
BEGIN
SELECT FREQUENCY INTO VCUST1 FROM RANGEOFBILLS WHERE RANGE=100;
SELECT FREQUENCY INTO VCUST2 FROM RANGEOFBILLS WHERE RANGE=1000;
SELECT FREQUENCY INTO VCUST3 FROM RANGEOFBILLS WHERE RANGE=10000;
FOR VBILL IN (SELECT * FROM BILL) LOOP
DBMS_OUTPUT.PUT_LINE(VBILL.AMOUNT);
IF VBILL.AMOUNT>100 and VBILL.AMOUNT<=1000 THEN
VCUST1:=VCUST1+1;
UPDATE RANGEOFBILLS SET FREQUENCY=vcust1 WHERE RANGE=100;
DBMS_OUTPUT.PUT_LINE(VBILL.AMOUNT);
END IF;
END LOOP;
END;
/