Use a declared variable in a SELECT statement - plsql

I'm using Oracle 10g and need to use a variable in a where clause of a SELECT; eg.
DECLARE
v_blah NUMBER;
BEGIN
v_blah := 13;
SELECT * FROM PEOPLE p WHERE p.LuckyNumber = v_blah;
END;
but am getting an error saying
PLS-00428: an INTO clause is expected in this SELECT statement
It seems to work fine in a DELETE or INSERT statement, so I'm not sure why it wouldn't work here.

The correct syntax is:
DECLARE
v_blah NUMBER := 13;
v_people_rec PEOPLE%ROWTYPE;
BEGIN
SELECT * INTO v_people_rec FROM PEOPLE p WHERE p.LuckyNumber = v_blah;
END;
The select statement in PL/SQL requires a place where store the query result. In this example the place is v_people_rec variable.
The example above expects exactly one row to be returned. In other cases it will throw exceptions NO_DATA_FOUND or TOO_MANY_ROWS.

That isn't anything to do with your parameter, it is because you're executing your code as a procedural block of code so it doesn't allow you to select to nothing.
What do you want to do with the result of the query? Display it to the screen? If so, select it to a cursor, iterate through and use dbms_output.

Related

How to correctly make a procedure in Pl/SQL in which I create a TABLE and use a CURSOR

The assignment I am trying to do is
"Create a procedure that places the names of all presidents who were born in one specific
state, in a temporary table. Display the contents of this table."
The procedure complies but when I try to invoke it, it gives me:
00000 - "table or view does not exist"
Error(8,5): PLS-00103: Encountered the symbol "CREATE" when expecting one of the following: begin function pragma procedure subtype type current cursor delete exists prior
I have been stuck for a while now. Does anybody know what I am doing wrong?
My code so far is:
CREATE OR REPLACE PROCEDURE stateofpresident(p_state president.state_born%TYPE)
AS
CURSOR c_state IS
SELECT *
FROM president;
BEGIN
execute immediate 'CREATE TABLE presidentFromState;
(
president_name VARCHAR2
)';
FOR r_state IN c_state LOOP
IF(p_state = r_state.state_born) THEN
execute immediate 'INSERT INTO presidentFromState VALUES(r_state.pres_name)';
commit;
END IF;
END LOOP;
execute immediate 'DROP TABLE presidentFromState';
END stateofpresident;
/
SET SERVEROUT ON
BEGIN
stateofpresident('VIRGINIA');
END;
/
SELECT *
FROM presidentFromState;
The immediate cause of your error is the semi-colon (;) at "presidentFromState;" At run fhat terminates the statement and the SQL interpreter at that point does not know what is want, the create syntax is invalid. The statement compiles because at compile time it is a properly formatted string. That is why dynamic SQL should be avoid if at all possible. Your script also has an additional error. Your last select will fail as the table presidentFromState ws not only created but also dropped in the procedure. Finally, just an FYI, the entire FOR cursor and the cursor itself is entirely unnecessary, the entire operation can be completed is one statement: Look into the structure
Insert into table_name(columns)
Select columns ...
Since this obviously an assignment or tutorial I'll leave the exact for your research.

bind variable substitution in oracle

The below simple procedure is suppose to provide grants to the user scott in oracle.
The value for &scott_SCHEMA is already defined in a seperate file(define_variable.sql) and the value is getting substituted correctly, but im getting the error(as specified in the bottom of the script), a help is much appreciated.
SET SERVEROUTPUT ON
declare
l_sql varchar2(3200);
begin
for i in ( select table_name as oname,'TABLE' as type from all_tables where owner='HR' AND table_name not like 'BIN$%' union all select view_name as oname,'VIEW' as type from all_views where owner='HR' and view_name not like 'BIN$%' )
loop
if i.type = 'TABLE' then
dbms_output.put_line(l_sql);
l_sql:= 'grant select,insert,update,delete on hr.'||i.oname||' to :owner with grant option';
execute immediate l_sql using '&scott_SCHEMA';
else
l_sql:= 'grant select on hr.'||i.oname||' to :owner with grant option';
end if;
end loop;
end;
/
*declare
*
ERROR at line 1:
ORA-00987: missing or invalid username(s)
ORA-06512: at line 12*
You can't use bind variables in place of identifiers (specifically schema or object names) in DDL or DML statements, they can only be used in place of value expressions.
Since you're using substitution variables, you could just place it in the sql statement itself:
l_sql:= 'grant select,insert,update,delete on hr.'||i.oname||' to &scott_SCHEMA with grant option';
and execute it with out passing it as a parameter:
execute immediate l_sql;
On a side note, your DBMS_OUTPUT line should come after you assignment to l_sql, otherwise you'll miss outputting one or more of the statements being processed. A good place would be between the assignment statement and the execute statement.

Is there a way to define the order of execution of my triggers in sqlite?

If have many on insert triggers for a table. Can i specify which trigger will be executed first when i insert values into my table using sqlite?
i thought of combining my triggers but the problem is that i have different when statements for each trigger and if i understand the doc correctly the when statement commes before my update/insert etc. statement.
CREATE TRIGGER trigger_name AFTER INSERT ON table_name
WHEN expr
BEGIN
update-stmt;
END;
Combine all of the triggers into one:
CREATE TRIGGER some_trigger
AFTER INSERT ON some_table
FOR EACH ROW BEGIN
-- statement 1;
-- statement 2;
-- statement 3;
...
-- statement n;
END;
If the trigger is to do an update, as in the OP's example, then this won't help. But if each trigger is just checking something and possibly raising an error, which I think is a pretty common case, then it is possible to pack them all into a single trigger procedure (with implicit order of execution) and to use different expressions with each:
CREATE TRIGGER some_trigger
AFTER INSERT ON some_table
BEGIN
SELECT RAISE(FAIL, "error message one") WHERE (<select-statement-one>) == <value-one>;
SELECT RAISE(FAIL, "error message two") WHERE (<select-statement-two>) == <value-two>;
...
END;
(Remember that the value of a subquery expression is the first row of the result from the enclosed SELECT statement.)

Execute Immediate

Can any one help me to to find out the error.
create or replace procedure sample
is
begin
DECLARE AGG_COLUMNS VARCHAR2(2000);
BEGIN
EXECUTE IMMEDIATE 'SELECT COLUMNS FROM COLUMN_NAMES' INTO AGG_COLUMNS;
END;
begin
EXECUTE IMMEDIATE
'CREATE TABLE NEW_BW_COLUMN_ROW_CELL_JOIN AS
(
SELECT *
FROM BW_COLUMN_ROW_CELL_JOIN
PIVOT
(
MAX(STRING_VALUE)
FOR COLUMN_NAME IN ('||AGG_COLUMNS||')))';
END;
end;
Error(9,5): PL/SQL: Statement ignored
Error(17,36): PLS-00201: identifier 'AGG_COLUMNS' must be declared
Thanks
The immediate error is that the local variable AGG_COLUMNS is declared in the first nested PL/SQL block. That means that it is out of scope as soon as the first nested block completes. You cannot, therefore, use it in the second nested PL/SQL block. You can fix that by declaring the local variable in your procedure's declaration section. You can also get rid of the nested PL/SQL blocks
create or replace procedure sample
is
AGG_COLUMNS VARCHAR2(2000);
begin
EXECUTE IMMEDIATE 'SELECT COLUMNS FROM COLUMN_NAMES' INTO AGG_COLUMNS;
EXECUTE IMMEDIATE
'CREATE TABLE NEW_BW_COLUMN_ROW_CELL_JOIN AS
(
SELECT *
FROM BW_COLUMN_ROW_CELL_JOIN
PIVOT
(
MAX(STRING_VALUE)
FOR COLUMN_NAME IN ('||AGG_COLUMNS||')))';
end;
This should remove the immediate compilation error. I'm not sure, though, whether that resolves all your problems.
COLUMN_NAMES isn't a table in a default Oracle install. This would have to be something that you created in order for this code to run.
If you did create the COLUMN_NAMES table and COLUMNS stores a comma-separated string, then there is no need to use dynamic SQL to query the table. You can do a simple SELECT ... INTO.

Can I delete rows using user defined functions in oracle?

I created a user defined function to delete some data. It doesn't work with delete but works with select. I am Oracle 9i.
The function is something like this:
create or replace function UFN_PURGEDATA(INPUTID IN VarChar2) return number is
Result number;
begin
Result := 0;
DELETE FROM MyTable WHERE MyTable.ID=INPID;
COMMIT;
Result := 1;
EXCEPTION WHEN OTHERS THEN
return(Result);
end UFN_PURGEDATA;
Then I use select UFN_PURGEDATA('test') from dual to run it but got result 0.
The answer to your question is "no".
If you remove your error "handling" you will find that the delete is failing with an exception like:
ORA-14551: cannot perform a DML
operation inside a query
i.e. you cannot perform an insert, update or delete from within a function called in a SELECT statement.
To execute this function in an IDE or SQL Plus, wrap it in some more PL/SQL like this:
declare
l_result number;
begin
l_result := my_function(123);
end;
However, you will need to add a RETURN statement to your function first otherwise it will fail.
(NB I said "handling" above in quotes because it is really "mishandling" - it completely disguises the actual problem in a very unhelpful way.)
You can perform DML inside a function used in a SELECT if you add PRAGMA AUTONOMOUS_TRANSACTION. For example:
create or replace function UFN_PURGEDATA(INPUTID IN VarChar2) return number is
pragma autonomous_transaction;
begin
DELETE FROM MyTable WHERE MyTable.ID=INPUTID;
COMMIT;
return 1;
EXCEPTION WHEN OTHERS THEN
return 0;
end UFN_PURGEDATA;
/
But you definitely want to avoid this approach if possible. In general, there's no way to know how many times a function will be executed if it's used in a SELECT.
Yes you can delete rows using user-defined functions in Oracle, but not from within a SELECT statement.
There are a couple of problems with your code:
- you don't return a value if your function does not raise an exception
- you must not use a function performing DML in a SELECT statement; if you remove your exception block, you get an ORA-14551
Why not create a procedure (instead of function) with OUT parameter returning the number? Without doing the autonomous transaction trick, Oracle doesn't want you running functions (used in selects) with "side-effects" (understandable why we don't want a select to result in DML changes).

Resources