PL/SQL variable scope in nested blocks - plsql

I need to run some SQL blocks to test them, is there an online app where I can insert the code and see what outcome it triggers?
Thanks a lot!
More specific question below:
<<block1>>
DECLARE
var NUMBER;
BEGIN
var := 3;
DBMS_OUTPUT.PUT_LINE(var);
<<block2>>
DECLARE
var NUMBER;
BEGIN
var := 200;
DBMS_OUTPUT.PUT_LINE(block1.var);
END block2;
DBMS_OUTPUT.PUT_LINE(var);
END block1;
Is the output:
3
3
200
or is it:
3
3
3
I read that the variable's value is the value received in the most recent block so is the second answer the good one? I'd love to test these online somewhere if there is a possibility.
Also, is <<block2>> really the correct way to name a block??
Later edit:
I tried this with SQL Fiddle, but I get a "Please build schema" error message:
Thank you very much, Dave! Any idea why this happens?
create table log_table
( message varchar2(200)
)
<<block1>>
DECLARE
var NUMBER;
BEGIN
var := 3;
insert into log_table(message) values (var)
select * from log_table
<<block2>>
DECLARE
var NUMBER;
BEGIN
var := 200;
insert into log_table(message) values (block1.var || ' 2nd')
select * from log_table
END block2;
insert into log_table(message) values (var || ' 3rd')
select * from log_table
END block1;

In answer to your three questions.
You can use SQL Fiddle with Oracle 11g R2: http://www.sqlfiddle.com/#!4. However, this does not allow you to use dbms_output. You will have to insert into / select from tables to see the results of your PL/SQL scripts.
The answer is 3 3 3. Once the inner block is END-ed the variables no longer exist/have scope. You cannot access them any further.
The block naming is correct, however, you aren't required to name blocks, they can be completely anonymous.
EDIT:
So after playing with SQL Fiddle a bit, it seems like it doesn't actually support named blocks (although I have an actual Oracle database to confirm what I said earlier).
You can, however, basically demonstrate the way variable scope works using stored procedures and inner procedures (which are incidentally two very important PL/SQL features).
Before I get to that, I noticed three issues with you code:
You need to terminate the insert statements with a semi-colon.
You need to commit the the transactions after the third insert.
In PL/SQL you can't simply do a select statement and get a result, you need to select into some variable. This would be a simple change, but because we can't use dbms_output to view the variable it doesn't help us. Instead do the inserts, then commit and afterwards select from the table.
In the left hand pane of SQL Fiddle set the query terminator to '//' then paste in the below and 'build schema':
create table log_table
( message varchar2(200)
)
//
create or replace procedure proc1 as
var NUMBER;
procedure proc2 as
var number;
begin
var := 200;
insert into log_table(message) values (proc1.var || ' 2nd');
end;
begin
var := 3;
insert into log_table(message) values (var || ' 1st');
proc2;
insert into log_table(message) values (var || ' 3rd');
commit;
end;
//
begin
proc1;
end;
//
Then in the right hand panel run this SQL:
select * from log_table
You can see that proc2.var has no scope outside of proc2. Furthermore, if you were to explicitly try to utilize proc2.var outside of proc2 you would raise an exception because it is out-of-scope.

Related

PL/SQL Query for column and use it in the function call

I am trying this but sure I am missing a lot
declare
my_id table.ISR_ID%type;
begin
select NVL(MAX(table.ISR_ID)+1,1) into isr_id
from table;
select my_pkg.getFunction(InputToFunction=> isr_id); -- from ?
end;
If you declared MY_ID variable, you should have selected into it, not into ISR_ID (which is never declared).
Also, you should return function's result into something (probably another variable?). I've declared it as FUN_RES - see the comment within the PL/SQL anonymous block.
Saying that you are missing a lot doesn't help much; you should specify which errors you get. Anyway: try such a code, say whether it works or not and - if not - say why not (possible errors, etc.).
declare
my_id table.ISR_ID%type;
fun_res number; --> function result should be returned into this variable.
-- I don't know what it returns, so I set it to be a NUMBER.
-- Change it, if necessary.
begin
select NVL(MAX(table.ISR_ID) + 1, 1)
into my_id
from table;
fun_res := my_pkg.getFunction(my_id);
end;
[EDIT]
If you have to select function's value for every ISR_ID in a table, then you don't need PL/SQL but
select isr_id,
my_pkg.getfunction(isr_id) fun_res
from table;
If you want PL/SQL, then do it in a loop, for example:
begin
for cur_r in (select isr_id from table) loop
dbms_output.put_line(cur_r.isr_id ||', result = ' || my_pkg.getfunction(cur_r.isr_id));
end loop;
end;
/

Teradata Insert Count into Variable

Description what I am trying to do:
I have 2 environments one has data (X) second one has no data (Y).
I have done procedure which has input parameter P_TableName. It should check if in this table is any data and IF There is then we will take data to Y environment.
So Mostly it works but I have problem with one freaking simple thing ( I have not much experience in TD but in Oracle it would be a 10seconds).
I need to pass select count(*) from X to variable how to do that?.
I was trying by SET VAR = SELECT...
INSERT INTO VAR SELECT...
I was trying to make a variable for statement which is directly executing
SET v_sql_stmt = 'INSERT INTO ' || VAR|| ' SELECT COUNT(*) FROM ' || P_TableName;
CALL DBC.SYSEXECSQL(v_sql_stmt);
It's probably really simple thing but I can't find good solution for that. Please help
You'll have to open a cursor to fetch the results since you are running dynamic SQL. There is a good example in the Teradata help doc on Dynamic SQL:
CREATE PROCEDURE GetEmployeeSalary
(IN EmpName VARCHAR(100), OUT Salary DEC(10,2))
BEGIN
DECLARE SqlStr VARCHAR(1000);
DECLARE C1 CURSOR FOR S1;
SET SqlStr = 'SELECT Salary FROM EmployeeTable WHERE EmpName = ?';
PREPARE S1 FROM SqlStr;
OPEN C1 USING EmpName;
FETCH C1 INTO Salary;
CLOSE C1;
END;
You can't use INTO in Dynamic SQL in Teradata.
As a workaround you need to do a cursor returning a single row:
DECLARE cnt BIGINT;
DECLARE cnt_cursor CURSOR FOR S;
SET v_sql_stmt = ' SELECT COUNT(*) FROM ' || P_TableName;
PREPARE S FROM v_sql_stmt;
OPEN cnt_cursor;
FETCH cnt_cursor INTO cnt;
CLOSE cnt_cursor;

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;

Getting a Tablename from all_tables for a trigger

We have an application that creates a table with a randomly generated name. I would like to create a trigger on this table.Since I do not know the name of the tabe I would like to get it from the all_table view. How can I go about achieveing something like this?
create or replace trigger t1
after insert or update on (select table_name from all_tables where owner = 'CustomAPP' and table_name like 'STAGE_%')
-- for each row
declare
-- local variables here
begin
end t1;
The SQL above obviously gives an error because of the select clause after the create trigger instead of a table name. Please advise
You would need to make the entire CREATE TRIGGER dynamic in order to do this. Something like this should work. You probably want to make the trigger name depend on the name of the table assuming there could be multiple tables that your query against ALL_TABLES might return multiple rows. And you certainly want the trigger to do something rather than having an empty body.
SQL> create table stg_12345( col1 number );
Table created.
SQL> begin
2 for x in (select *
3 from user_tables
4 where table_name like 'STG%')
5 loop
6 execute immediate
7 'create or replace trigger trg_foo ' ||
8 ' before insert on ' || x.table_name ||
9 ' for each row ' ||
10 'begin ' ||
11 ' null; ' ||
12 'end;';
13 end loop;
14 end;
15 /
PL/SQL procedure successfully completed.
SQL> select count(*) from user_triggers where trigger_name = 'TRG_FOO';
COUNT(*)
----------
1
Of course, the idea of an application that creates tables on the fly is one that frightens me to the core. If you have any control over that, I would strongly suggest reconsidering the architecture.
Solution 1:
If the problem is "poor performance due to lack of statistics", perhaps changing the OPTIMIZER_DYNAMIC_SAMPLING parameter at a system or session level can help. See the Performance Tuning Guide for a more thorough discussion, but I've found the default of 2 (64 blocks) to be insufficient, especially for large data sets where keeping optimizer statistics current is impractical.
Solution 2:
If you really want to automatically create a trigger after a table's been created, you'll need to create a DDL trigger for the schema. The SQL below demonstrates that.
CREATE OR REPLACE TRIGGER MAKE_ME_A_TRIGGER
AFTER CREATE ON CUSTOM_APP_SCHEMA
AS
l_trigger_sql varchar2(4000);
BEGIN
if l_ora_obj_dict_type = 'TABLE'
then
l_trigger_sql := 'create or replace trigger ' || ora_dict_obj_name
' before insert on ' || ora_dict_obj_type||
' for each row ' ||
'begin ' ||
' null; ' ||
'end;'
execute immediate l_sql;
end if;
END;
/
You can use EXECUTE IMMEDIATE to dynamically execute SQL, including DDL scripts, provided the active connection has appropriate permissions on the database. Use PL/SQL to build the full DDL statement via string concatenation, and then you can execute it dynamically.
Docs:
http://docs.oracle.com/cd/B12037_01/appdev.101/b10807/13_elems017.htm
More Docs:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/dynamic.htm

PL/SQL Blocks - seeing output simply ? [A Very simple question I'm sure !]

I'm sure what I want is very simple but I cannot figure out how.
I want :
To declare some variables and initialize them to certain values
To excecute a number of selects (predicated by the above variable values) and see the results as if i had executed the results straight on the sqlplus command line
I believe it's necessary to use the block structure in order that I may declare and make use of variables within the predicates of the queries. Although the examples shown here are quite simple in the real case there are numberous, much more complex SELECT's.
I tried doing this (forgetting about predicates for a moment) ...
DECLARE
EMP_EMPLOYEE_ID_IN VARCHAR2(12);
BEGIN
EXECUTE IMMEDIATE 'SELECT * FROM DEPT WHERE DEPNO';
END;
/
... but when I do that I get to execute the select without seeing the output.
I've also tried this ...
DECLARE
EMP_EMPLOYEE_ID_IN VARCHAR2(12);
BEGIN
SELECT * FROM DEPT;
END;
/
... but then I get ...
PLS-00428: an INTO clause is expected in this SELECT statement
... I really don't want to have to declare a variable for every column which would appear in my output.
Can anyone tell me how I can execute the SELECTs but simply and easily see the output as if I were on the sqlplus command line, ie to see the same output as if I did this
SQL> SELECT * FROM DEPT;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
Thanks
I have now tested the answer given by Shannon Severance below and found that it will do what I want.
For the sake of later readers I thought it might be useful to show the complete script here.
set line 32000;
set trimspool on;
var V_CURSOR1 REFCURSOR;
var V_CURSOR2 REFCURSOR;
var V_CURSOR3 REFCURSOR;
DECLARE
DEPT_NUM_IN VARCHAR2(12);
BEGIN
DEPT_NUM_IN := '10';
OPEN :V_CURSOR1 FOR SELECT * FROM DEPT;
OPEN :V_CURSOR2 FOR SELECT * FROM DEPT ORDER BY LOC;
OPEN :V_CURSOR3 FOR SELECT * FROM DEPT WHERE DEPTNO = DEPT_NUM_IN ORDER BY LOC;
END;
/
print V_CURSOR1
print V_CURSOR2
print V_CURSOR3
From sqlplus, other tools may be different.
First declare a sqlplus refcursor variable
SQL> var l_cursor refcursor
Then open that cursor within a PL/SQL block, where you will have access to declared variables and everything:
SQL> edit
Wrote file afiedt.buf
1 declare
2 l_number number;
3 begin
4 open :l_cursor for select table_name from all_tables where rownum < 10;
5* end;
SQL> /
PL/SQL procedure successfully completed.
Notice above that the refcursor variable is prepended with a :, this is because we are binding a sqlplus variable into the PL/SQL anonymous block
Next, use the SQLPLUS print command:
SQL> print l_cursor
TABLE_NAME
------------------------------
ICOL$
CON$
UNDO$
PROXY_ROLE_DATA$
FILE$
UET$
IND$
SEG$
COL$
9 rows selected.
DECLARE
v_result VARCHAR2(400);
BEGIN
SELECT dummy
INTO v_result
FROM dual;
dbms_output.put_line(v_result);
END;

Resources