ORA-00933: SQL command not properly ended when using execute immediate - plsql

When I run
SELECT COUNT(*)
INTO l_entry_found
FROM hera.hera_user#iam
WHERE username = docm.eb_key;
without execute immediate it works.
execute immediate'
SELECT COUNT(*) INTO l_entry_found FROM hera.hera_user#iam WHERE
username = docm.eb_key;';
However throws an ora-00933.
What could be the Problem and how may i solve it? Thanks in advance!

you can't use select into in dynamic SQL
and you do not need a semicolon either
declare
l_entry number(10);
begin
execute immediate'
SELECT COUNT(*) FROM hera.hera_user#iam WHERE
username = '''||docm.eb_key||'''' INTO l_entry;
end;

You have some issues; say you have a table like
create table someTable(userName varchar2(100))
and a code like :
declare
someVariable varchar2(100);
l_entry_found number;
begin
someVariable := 'someName';
--
SELECT COUNT(*)
INTO l_entry_found
FROM someTable
WHERE username = someVariable;
end;
If you wat to switch to dynamic SQL, you need to
remove the semicolon
use bind variables to pass your parameter
move the INTO outside the dynamic part
Your code could be:
declare
someVariable varchar2(100);
l_entry_found number;
begin
someVariable := 'someName';
--
execute immediate
'SELECT COUNT(*)
FROM someTable
WHERE username = :bindVar'
into l_entry_found
using someVariable;
end;
Here I assume that you have a good reason to switch to dynamic SQL, for example, your table name could change based on some parameter; if not, plain SQL is good enough for your task.

The EXECUTE IMMEDIATE statement executes a dynamic SQL statement or anonymous PL/SQL block. You can use it to issue SQL statements that cannot be represented directly in PL/SQL, or to build up statements where you do not know all the table names, WHERE clauses, and so on in advance
Continuing with your question
-- Case using 1 output column 'COUNT(*)', 1 filter variable 'eb_key'
DECLARE
--
eb_key VARCHAR2(100) := 'something';
l_entry_found number;
--
BEGIN
execute immediate 'SELECT COUNT(*) FROM hera.hera_user#iam WHERE username = :eb_key;' USING eb_key INTO l_entry_found;
END;
-- Case using 2 output column 'COUNT(*)', 3 filter variable 'eb_key'
DECLARE
--
eb_column1 VARCHAR2(100) := 'something';
eb_column2 VARCHAR2(100) := 'something';
l_entry_found1 number;
l_entry_found2 number;
--
BEGIN
execute immediate 'SELECT column1, column2 FROM hera.hera_user#iam WHERE username = :eb_1 AND lastname = :eb_2;' USING eb_column1, eb_column2 INTO l_entry_found1,l_entry_found2;
END;

Related

Does Oracle support non-scalar cursor parameter?

This is a question about Oracle PL/SQL.
I have a procedure in which the exact WHERE clause is not known until the run time:
DECLARE
CURSOR my_cursor is
SELECT ...
FROM ...
WHERE terms in (
(SELECT future_term2 FROM term_table), -- whether this element should be included is conditional
(SELECT future_term1 FROM term_table),
(SELECT present_term FROM term_table)
);
BEGIN
(the processing)
END;
/
What the (SELECT ... FROM term_table) query returns is a 4-character string.
For a solution to this, I am thinking of using a parameterized cursor:
DECLARE
target_terms SOME_DATATYPE;
CURSOR my_cursor (pi_terms IN SOME_DATATYPE) IS
SELECT ...
FROM ...
WHERE terms in my_cursor.pi_terms;
BEGIN
target_terms := CASE term_digit
WHEN '2' THEN (
(SELECT future_term2 FROM term_table),
(SELECT future_term1 FROM term_table),
(SELECT present_term FROM term_table)
) ELSE (
(SELECT future_term1 FROM term_table),
(SELECT present_term FROM term_table)
)
END;
FOR my_record IN my_cursor (target_terms) LOOP
(the processing)
END LOOP;
END;
/
The problem is what the datatype for SOME_DATATYPE should be is not known to me, nor is it known whether Oracle supports such a cursor parameter at all. If supported, is the way shown above to fabricate the value for target_terms correct? If not, how?
Hope someone who know can advise. And thanks a lot for the help.
You can certainly pass a parameter to a cursor, just like you can to a function - but only IN parameters. However, PL/SQL is a strongly typed language, so the datatype must be specified at the time of compilation.
It looks to me like what you will need to do is construct the query dynamically and then use
OPEN cursor FOR l_query;
where l_query is the constructed string. This should give you a feel for what you can do:
CREATE OR REPLACE PACKAGE return_id_sal
AUTHID DEFINER
IS
TYPE employee_rt IS RECORD
(
employee_id employees.employee_id%TYPE,
salary employees.salary%TYPE
);
FUNCTION allrows_by (append_to_from_in IN VARCHAR2 DEFAULT NULL)
RETURN SYS_REFCURSOR;
END return_id_sal;
/
CREATE OR REPLACE PACKAGE BODY return_id_sal
IS
FUNCTION allrows_by (append_to_from_in IN VARCHAR2 DEFAULT NULL)
RETURN SYS_REFCURSOR
IS
l_return SYS_REFCURSOR;
BEGIN
OPEN l_return FOR
'SELECT employee_id, salary FROM employees ' || append_to_from_in;
RETURN l_return;
END allrows_by;
END return_id_sal;
/
DECLARE
l_cursor SYS_REFCURSOR;
l_row return_id_sal.employee_rt;
BEGIN
l_cursor := return_id_sal.allrows_by ('WHERE department_id = 10');
LOOP
FETCH l_cursor INTO l_row;
EXIT WHEN l_cursor%NOTFOUND;
END LOOP;
END;
/
You will need to take precautions against SQL injection with this sort of code. Certainly a user should never be able to pass SQL text directly to such a function!
You can use also some built-in VARRAY SQL types like SYS.ODCIVARCHAR2LIST or create your own :
CREATE OR REPLACE NONEDITIONABLE TYPE VARCHARLIST
AS VARRAY(32767) OF VARCHAR2(4000);
Then you can use it with SELECT COLUMN_VALUE FROM TABLE(COLLECTION) statement in your cursor:
DECLARE
l_terms SYS.ODCIVARCHAR2LIS; --or VARCHARLIST
CURSOR my_cursor (p_terms IN SYS.ODCIVARCHAR2LIS) IS
SELECT your_column
FROM your_table
WHERE terms in (select COLUMN_VALUE from table (p_terms));
BEGIN
select term
bulk collect into l_terms
from (
select 'term1' term from dual
union all
select 'term2' term from dual
);
FOR my_record IN my_cursor (l_terms) LOOP
--process data from your cursor...
END LOOP;
END;

In pl/sql how can I execute a select statement stored in CLOB type of field?

In Oracle DB, I have a large select staetment with lots of joins and cases that is stored in a CLOB field in one of my tables.
How can i execute this statement from the CLOB?
Look into the EXECUTE IMMEDIATE syntax.
Example table:
CREATE TABLE test(id number, largedata clob);
INSERT INTO test VALUES (1, 'select name from v$database');
commit;
select * from test;
DECLARE
l_sql clob;
l_result VARCHAR2(50);
BEGIN
SELECT LARGEDATA INTO l_sql FROM TEST;
EXECUTE IMMEDIATE l_sql INTO l_result;
dbms_output.put_line(l_result);
END;
/
Output is the DB name.

ROWTYPE definition in pl/sql

I have written a PL/SQL Procedure which compares data between two tables and print the difference if any, but the twist is the table names to the procedure is dynamic. Here is the procedure
create or replace PROCEDURE compareTables(
tabA IN VARCHAR2, tabB IN VARCHAR2) AS
cur_tab_name USER_TABLES%ROWTYPE;
lv_sql varchar2(4000);
lv_sql2 varchar2(4000);
BEGIN
--SELECT TABLE_NAME INTO cur_tab_name FROM USER_TABLES WHERE TABLE_NAME = tabA;
lv_sql2 := 'SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME = :b_tabA';
EXECUTE IMMEDIATE lv_sql2 INTO cur_tab_name USING tabA;
<<child>>
DECLARE
TYPE cursor_ref IS REF CURSOR;
cur_comp_result cursor_ref;
rec_comp_result cur_tab_name.TABLE_NAME%rowtype;
BEGIN
lv_sql := 'SELECT * FROM '||tabA||' MINUS SELECT * FROM '||tabB;
OPEN cur_comp_result FOR lv_sql;
LOOP
FETCH cur_comp_result INTO rec_comp_result;
EXIT WHEN cur_comp_result%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(rec_comp_result.empid || '' || rec_comp_result.name);
END LOOP;
CLOSE cur_comp_result;
Exception
When others then
dbms_output.put_line('The Problem is '||sqlerrm);
END;
END compareTables;
Now the problem is when I compile this procedure I am getting the following error
Error at line 14: PLS-00310: with %ROWTYPE attribute, 'CUR_TAB_NAME.TABLE_NAME' must name a table, cursor or cursor-variable
line 14:rec_comp_result cur_tab_name.TABLE_NAME%rowtype;
how will I solve it?
*NB: I don't have oracle installed in my system. I am using Oracle Apex Online tool which uses
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 and
PL/SQL Release 11.2.0.3.0
As a test, go to the last line, and after the semicolon, hit enter.
I know that Pro*C in particular will gag without a line terminator at the end of the file.
You may be encounting that issue.
Outside the scope of your question consider
SELECT columns
FROM TABLE1
MINUS
SELECT columns
FROM TABLE2
and
SELECT columns
FROM TABLE2
MINUS
SELECT columns
FROM TABLE1
Use: cur_tab_name.table_name. The variable CUR_TAB_NAMEis of type USER_TABLE%ROWTYPE, thus it has several fields.

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;

opening implicit cursor with for loop

I have a stored procedure that has following pl/sql block. This block was using select query in for statement but i need to change that static variable to dynamic query. As I changed that it has error. Is there any way to use variable with FOR LOOP in implicit cursor.
declare
sql_query varchar2(32767) := 'select ctlchar ';
kpiNameQuery varchar2(600);
isWg boolean := true;
begin
IF isWG then
kpiNameQuery := 'select distinct KPI_NAME from weeklykpi where kpi_name in (select kpi_wg from auxillary.kpi_types) order by 1';
Else
kpiNameQuery := 'select distinct KPI_NAME from weeklykpi where kpi_name in (select kpi_wg1 from auxillary.kpi_types) order by 1';
End IF;
for KPI_NAME in kpiNameQuery
loop
sql_query := sql_query || ' , min(case when KPI_NAME = '''||x.KPI_NAME||''' then KPI_VALUE end) as '||x.KPI_NAME;
dbms_output.put_line(sql_query);
end loop;
end;
You can achieve similar functionality with the following using cursor
declare
type t_cursor is ref cursor;
c_cursor t_cursor;
l_sql varchar2(512);
l_var number;
begin
l_sql := 'select count(*) from emp'; -- do dynamic check before here for
-- correct sql
open c_cursor for l_sql;
loop
fetch c_cursor
into l_var;
exit When c_cursor%notfound;
DBMS_OUTPUT.put_line ('val '||l_var);
end loop;
close c_cursor;
end;
Unfotunately no, the doc states:
If the dynamic SQL statement is a SELECT statement that returns multiple rows, native dynamic SQL gives you these choices:
Use the EXECUTE IMMEDIATE statement with the BULK COLLECT INTO clause.
Use the OPEN FOR, FETCH, and CLOSE statements.
So you will have to use a REF cursor (or EXECUTE IMMEDIATE and loop over the results).
Incidentally, in your case you could go for static SQL and have comparable performance:
BEGIN
FOR cc IN (SELECT DISTINCT KPI_NAME
FROM weeklykpi
WHERE kpi_name IN (SELECT CASE WHEN l_variable = 1
THEN kpi_wg
ELSE kpi_wg1
END
FROM auxillary.kpi_types) LOOP
ORDER BY 1
-- do something
END LOOP;
END;
You'll have to use some other type than boolean though since it's unknown to SQL.

Resources