PL/SQL Using SELECT inside of CONDITION - windows

I've installed Oracle Instance inside of docker container and I put sql script whitch runs when a container starts:
Dockerfile:
FROM...
...
CMD powershell echo #C:\scripts\entrypoint.sql | sqlplus -S sys/password as sysdba
entrypoint.sql:
set serveroutput on format wrapped;
declare
userexist integer;
db_schema_version varchar2(20);
begin
select count(*) into userexist from dba_users where username='DEV';
if (userexist = 0) then
dbms_output.put_line('SCHEMA NOT FOUND!');
dbms_output.put_line('CONNECTION STRING: ' || 'sqlplus sys/password#localhost/ORA193 as sysdba');
elsif (userexist = 1) then
dbms_output.put_line('SCHEMA FOUND!');
select DB_SCHEMA_VERSION into db_schema_version from DEV.VER_INFO where CODE = 'CORE';
dbms_output.put_line('DB_SCHEMA_VERSION: ' || db_schema_version);
dbms_output.put_line('CONNECTION STRING: ' || 'sqlplus DEV/password#localhost/ORA193');
end if;
end;
/
The issue is that query select DB_SCHEMA_VERSION into db_schema_version from DEV.VER_INFO where CODE = 'CORE'; is executing even if elsif condition is false
What is the correct way of writing PL/SQL to get select query executed only in case of condition is true?

The problem isn't that the query is being executed if the condition is false. The problem is that the PL/SQL block has to be compiled before it can be executed. And the compilation fails if it has a reference to an object that doesn't exist (if you posted the error message, it would be clear that it's a compilation error not a runtime error).
If you want to reference an object that might not exist, you'd want to use dynamic SQL. Something like
execute immediate 'select db_schema_version from dev.ver_info'
into db_schema_version;
would work in this case. In more complicated cases, you'd want to build the SQL statement in a local variable that you can log for debugging purposes or potentially use the dbms_sql package.

Related

Oracle Stored Procedure posing a prob

[EDIT]Editing the code to reflect changes coming from comments
I have a problem with one of the stored procedures I'm trying to create in an Oracle database.
The goal is to update every table which has an indiv column.
CREATE OR REPLACE PROCEDURE sp_majUserOnAllK (lastU IN VARCHAR2, newU IN VARCHAR2)
AS
BEGIN
FOR item IN (
select table_name , owner
from all_tab_columns
where column_name = 'INDIV' AND OWNER ='K'
)
LOOP
EXECUTE IMMEDIATE 'UPDATE K.' || item.table_name || ' SET indiv = :newValue WHERE indiv = :oldValue' USING newU, lastU;
END LOOP;
END sp_majUserOnAllK;
exec sp_majUserOnAllK( 'hum','hum');
Problem is, when I try to execute the stored procedure, I got an error message with no detail at all ('non valid SQL').
I tried taking the code out of the stored procedure. And there, it works. Only the beginning is changing to :
DECLARE
newU NVARCHAR2(50);
lastU NVARCHAR2(50);
req VARCHAR2(100);
CURSOR ctable IS
select table_name , owner from all_tab_columns where column_name = 'INDIV' AND OWNER ='KEXPLOIT';
BEGIN
newU := 'hum';
lastU := 'hum';
FOR item IN ctable
....
Like that, it works perfectly and does exactly what it is supposed to do.
As the only difference is the assignation of the variable, I think I may have a problem with my procedure declaration but I can't find a solution. The compilation is ok.
Any idea ?
Your procedure's syntax is not correct. Try this.
CREATE OR REPLACE PROCEDURE sp_majUserOnAllK (lastU IN VARCHAR2, newU IN VARCHAR2)
IS
req VARCHAR2(100);
BEGIN
FOR item IN (select table_name , owner from all_tab_columns where column_name = 'INDIV' AND OWNER ='K')
LOOP
req := 'UPDATE K.' || item.table_name || ' SET indiv = :newValue WHERE indiv = :oldValue';
EXECUTE IMMEDIATE req USING newU, lastU;
END LOOP;
-- return 1; -- note: procedures do not return values
END;
/
A five-second Google search on "dbeaver exec command" brought this up among the first few hits:
https://github.com/dbeaver/dbeaver/issues/749
In it, we learn that EXEC is not supported by dbeaver.
EXEC is an SQL*Plus command. It is not Oracle SQL, and it is not PL/SQL. SQL*Plus is a shell program of sorts for interacting with Oracle databases; it has its own language, distinct from SQL and PL/SQL.
SQL Developer and Toad (and perhaps other similar programs) support (most of) SQL*Plus, but apparently dbeaver (with which I am not familiar) does not.
The link I copied above suggests using the CALL command instead. See the link for examples.
As an aside, when we use EXEC in SQL*Plus and SQL Developer, there is no semicolon at the end of the procedure call. Adding an unnecessary semicolon, however, does not throw an error (SQL*Plus is, apparently, smart enough to simply ignore it).

ORACLE: Cursor with dynamic query - throws error "invalid identifier" for cursor field

I have a logic to implement where I have to use dynamic sql(column names and where clause is decided on the fly).So here my cursor(emp_ref_cursor) has a dynamic sql, and has 3 cursor fields(emp_id,emp_name,dept).
Using these cursor fields in WHERE clause I am trying to execute another dynamic sql inside the loop.Bt oracle isn't able to identify the cursor field and throws an error like "ORA-00904: "EMP_REC"."EMP_ID": invalid identifier" though I am able to output emp_rec.emp_id through DBMS_OUTPUT.
NOTE: Please don't comment on the code quality this is not the actual code.
This is just used to describe the problem. I can't post the actual code due to
some compliance related stuff.
DECLARE
emp_ref_cursor sys_refcursor;
v_sql varchar2(3900);
TYPE emp_rec_type IS RECORD (emp_id number,emp_name varchar2(100),dept_id varchar2(100));
emp_rec emp_rec_type;
v_dept_id number:='1234';
v_dob varchar2(100);
v_desig varchar2(100);
x_dynamic_col_1 varchar2(100):='dob'; --dynamic column(based on some condition)
x_dynamic_col_2 varchar2(100):='designation'; --dynamic column(based on some condition)
x_dynamic_col_3 varchar2(100):='emp_id'; --dynamic column(based on some condition)
BEGIN
v_sql:='SELECT emp_id,emp_name,dept FROM employee WHERE dept_id=' || v_dept_id;
OPEN emp_ref_cursor FOR v_sql;
LOOP
FETCH emp_ref_cursor INTO emp_rec;
exit WHEN emp_ref_cursor%NOTFOUND;
stmt:='SELECT ' || x_dynamic_col_1 || ',' || x_dynamic_col_2 || '
FROM employee A
WHERE emp_id=emp_rec.' || x_dynamic_col_3;
DBMS_OUTPUT.PUT_LINE(stmt);
--Prints the SQL query as expected
DBMS_OUTPUT.PUT_LINE('emp_rec.emp_id:'||emp_rec.emp_id);
--Displays the value!!!
execute immediate stmt into v_dob, v_desig;
--But why is it saying emp_rec.emp_id is invalid identifier??
END LOOP;
END;
You have emp_rec defined as a local PL/SQL variable. None of the PL/SQL data is in scope to the dynamic SQL execution. When it is executed it as if you tried to run the statement - as it is displayed by your dbms_output standalone in a separate SQL context. If you did that it would be clear that emp_rec doesn't exist to the query.
You refer to it you would need to use a bind variable:
WHERE emp_id=:dynamic_col_3';
And then execute it with:
execute immediate stmt using emp_rec.emp_id;
But you can't use the x_dynamic_col_3 local variable in the using clause. Since - in this example anyway - the query would also need to change to use a different table column is the dynamic record field changed - that doesn't seem too much of a problem. But you said the where clause will change on the fly too. In that case you could have another local variable that you set to the relevant x field before the executin.
You have incorrect using of EXECUTE IMMEDIATE. You don't need to put INTO clause to SQL query. Use this instead:
stmt:='SELECT ' || x_dynamic_col_1 || ',' || x_dynamic_col_2 || '
FROM employee A
WHERE emp_id=emp_rec.' || x_dynamic_col_3;
execute immediate stmt into v_dob, v_desig;

anonymous block completed in oracle SQL

Whilst trying to create a query cursor as follows:
DECLARE CURSOR Query1
IS
SELECT * FROM RACE
WHERE Race_Time='22-SEP-14 12.00.00.000000000';
BEGIN
OPEN Query1;
END;
I get the following error. anonymous block completed. Does anyone know how to fix this? I tried setting the
'SET SERVEROUTPUT ON;'
before the declare but this did not seem to fix the error. Thanks in advance!
It seems that dbms_output is turned off
you can see you out put if you put SET SERVEROUTPUT ON; in the beginning of your script.
or you can view dbms_output window (View then DBMS Output) then press the "+" at the top of the Dbms Output window and then select an open database
"anonymous block completed" means your PL/SQL code was successfully executed.
To Display: try using a output statement...
For example:
BEGIN
dbms_output.put_line ('Hello, world!');
END;
If you want to control the process in PL/SQL, you could do something like
DECLARE
l_race_rec race%rowtype;
CURSOR Query1
IS
SELECT *
FROM RACE
WHERE Race_Time='22-SEP-14 12.00.00.000000000';
BEGIN
OPEN Query1;
LOOP
FETCH query1 INTO l_race_rec;
EXIT WHEN query1%notfound;
dbms_output.put_line( l_race_rec.column1 || ' ' || l_race_rec.column2 || ... || l_race_rec.columnN );
END LOOP;
CLOSE Query1;
END;
Unless your assignment requires the use of explicit cursors, though, implicit cursors are likely easier to use
BEGIN
FOR x IN( SELECT *
FROM RACE
WHERE Race_Time='22-SEP-14 12.00.00.000000000')
LOOP
dbms_output.put_line( x.column1 || ' ' || x.column2 || ... || x.columnN );
END LOOP;
END;
If you are using SQL*Plus, you can also do something like
VAR rc REFCURSOR;
BEGIN
OPEN :rc
FOR SELECT *
FROM race
WHERE race_time = '22-SEP-14 12.00.00.000000000';
END;
PRINT rc
If race_time is really a timestamp, you should really be comparing a timestamp with another timestamp rather than comparing a timestamp to a string. Use explicit conversion with an explicit format mask to avoid errors due to different sessions having different NLS settings
WHERE race_time = to_timestamp( '22-SEP-14 12.00.00.000000000',
'DD-MON-RR HH24:MI:SS.FF9' )
Of course, I'm not sure why you would use a timestamp in the first place here-- it seems unlikely that you really know the nanosecond at which a race started.

Oracle returning result from dynamic SQL

Hi I am trying to return a value from Oracle using dynamic SQL. I am getting an error SQL command not properly ended. I can't figure out what I am doing wrong. Here is the latest code I've tried:
PROCEDURE get_record_counts
AS
v_EXT_RECCOUNT VARCHAR2(05) := '0';
BEGIN
EXECUTE IMMEDIATE 'select count(*) from ' || r_cls.EXT_TABLE || ' RETURN v_EXT_RECCOUNT into v_EXT_RECCOUNT ';
END get_record_counts;
You'd want
EXECUTE IMMEDIATE 'select count(*) from ' || r_cls.ext_table
INTO v_ext_reccount
assuming that r_cls.ext_table resolves to a varchar2 variable that contains a table name that the caller has appropriate permissions on. In the snippet you posted, that is not a valid variable name but I'm guessing that there is more code that you've removed that declares that variable.

"Select ... into .." statement fails with "table or view does not exist" even though it should not be run

I have the following PL/SQL script that I'm trying to run through SQL*Plus:
DECLARE
table_exists number;
sequence_exists number;
sequence_start number;
BEGIN
select count(*) into sequence_exists
from all_sequences
where sequence_name='DBSEQ';
select count(*) into table_exists
from dba_tables
where table_name='DBTABLE';
IF sequence_exists = 0 AND table_exists > 0 THEN
select MAX(ID) + 1 into sequence_start from DBTABLE;
ELSE
sequence_start := 1;
END;
IF sequence_exists = 0 THEN
execute immediate 'CREATE SEQUENCE DBSEQ
start with ' || sequence_start || '
increment by 1
nomaxvalue';
END IF;
END;
(this is somewhat minimized, to show the parts that are actually failing).
The problem is that the row
select MAX(ID) + 1 into sequence_start from DBTABLE;
fails with "PL/SQL: ORA-00942: table or view does not exist", because the table does not exist (and I've already verified that the table_exists variable is actually 0). So basically, the select runs (or at least fails) even though the if-clause does not execute. I've also verified that the if-clause indeed fails by replacing the select with e.g. creating a table, which does not exist after running the script.
I did also try catching exceptions instead, doing something like this:
BEGIN
select MAX(ID) + 1 into sequence_start from DBTABLE;
EXCEPTION
WHEN OTHERS THEN
sequence_start := 1;
END;
but that produced the same error.
So, is there anything special with the select statement, that makes it run before anything else? And how should I solve my problem?
The problem is that Oracle has to compile the block before it can run it. Part of compiling the block involves resolving all the static references. If you have a reference to a table that does not exist, the block will fail to compile so it cannot be run. Your exception handler doesn't do anything because it is a compilation error, not an execution error.
If you want your block to refer to a table that may not exist at the time the block is compiled, you would need to use dynamic SQL.
EXECUTE IMMEDIATE 'select max(id) from dbtable'
INTO sequence_start;
That will allow the block to compile successfully since Oracle doesn't need to resolve references in dynamic SQL statements. If your code tries to execute the dynamic SQL statement when dbtable doesn't exist, you'll get a run-time error (which you could catch with an exception handler).

Resources