Declaring variables dynamically within PL/SQL - sql

The program is to extract numbers from an input string. Eg: ab123cde4f. Now if only the input string has numbers then I will declare a variable of number datatype (to extract the numbers) after checking for numbers within the Begin..End block. If there are no numbers I will not declare any variable and simply give dbms output that the input string does not contain any numbers. Suggest a pl/sql block.

If your questions is, if a variable can be declared within a BEGIN...END block. No, you always need a declare block for that.
However you can use declare inside a BEGIN...END Block as well.
BEGIN
IF 1=1 THEN
DECLARE
v_chr VARCHAR2(100) := 'hello';
BEGIN
dbms_output.put_line(v_chr);
END;
ELSE
DECLARE
v_chr VARCHAR2(100) := 'world';
BEGIN
dbms_output.put_line(v_chr);
END;
END IF;
END;
I wouldn't suggest it though, its much more KISS thingy to just define a variable.

Related

Variable scoping in BigQuery stored procedure

I am trying to create nested BEGIN..END blocks within the body of BigQuery stored procedure. The code is as follows:
CREATE OR REPLACE PROCEDURE dataset.proc(IN p_var1 INT64, OUT out_param STRING)
BEGIN
DECLARE p_abc INT64;
DECLARE p_bcd INT64;
BEGIN
DECLARE p_abc INT64 DEFAULT 0; //Error Here : re-declaration cannot occur.
WHILE (p_abc <= p_bcd) DO
BEGIN
SET p_abc = p_abc + 1;
END;
END WHILE;
END;
END;
The above stored procedure doesn't compile because of the redeclaration. Unlike in traditional databases, like Netezza or Teradata, I can easily perform such type of variable scoping.
Is there some way to do this on BigQuery or not possible at all?
The documentation says:
It is an error to declare a variable with the same name as a variable declared earlier in the current block or in a containing block.
So I would say it is impossible to create a variable with the same name in the case you described.

Why does invoking procedure that uses REGEX_SUBSTR through PLSQL code block return an extra '¬' char?

Editing in PLSQL.
I've got the following procedure:
SET SERVEROUTPUT ON
CREATE OR REPLACE PROCEDURE StringTest(StringToTest IN varchar2)
AS
result varchar2(100);
BEGIN
result := REGEXP_SUBSTR(StringToTest, '[a-zA-Z0-9]{1,}\/?\s?\w*(\/\d{4})?',1,1);
DBMS_OUTPUT.PUT_LINE('Result is ' || result);
END;
/
The purpose of this procedure is to take in a string, match it with the regex, and then return the first match in the string. I understand for this example the regex is more complicated than it needs to be, but that is because I have truncated the code to its simplest form. The actual code is much more complex, and therefore the regex looks more complex than it needs to for this example.
When I invoke the procedure through a PLSQL code block such as
SET SERVEROUTPUT ON
DECLARE
String1 varchar2(100);
BEGIN
String1 := '(‘Hello’)';
StringTest(String1);
END;
/
I get the following:
Result is Hello¬
When I invoke the procedure through an EXEC statement such as
EXEC StringTest('(‘Hello’)');
I get the following
Result is Hello
The second result is what I expect in both cases. My question is, why does invoking the same exact procedure through a PLSQL code block add the extra ¬ character to the output?

How bind variable is different from simple variable in plsql? what are their application?

variable deptno number ;
deptno1 number;
Not sure what was your question really... However, I hope, The following answer/ post (from Alex Poole) should give you a fair idea how to define a variable in SQL /PL-SQL and how.
Declare bind variables in SQL*Plus
We try to 'bind' them in both ways and avoid literals.
A bind variable is part of the interface between SQL and the calling application or host language, so if your 'simple variable in PL/SQL' is not used in a SQL statement then it isn't a bind variable :)
declare
msg varchar2(100) := 'Hello';
begin
dbms_output.put_line(msg);
end;
In the case of actual bind variables, these are identical whatever way you do it. The PL/SQL compiler just builds the code for you behind the scenes.
Plain PL/SQL:
declare
l_dummy varchar2(1) := 'X';
l_result integer;
begin
select count(*) into l_result from dual where dummy = l_dummy;
dbms_output.put_line(l_result);
end;
Bind variable explicitly defined in SQL*Plus:
var dummy varchar2(1)
exec :dummy := 'X'
declare
l_result integer;
begin
select count(*) into l_result from dual where dummy = :dummy;
dbms_output.put_line(l_result);
end;
Either way you will see from v$sql, dbms_xplan etc that the SQL it actually executed was
SELECT COUNT(*) FROM DUAL WHERE DUMMY = :B1
The main difference is performance-wise. Bind variables can be used to parse a query once and save future parses every time you run the query with different values.
Using regular variables forces Oracle to do a hard parse every time the value of the variables change.

sql oracle procedure IN OUT parameter, how to execute it

my procedure looks like this:
create or replace procedure odcitaj_surovinu_zo_skladu
(
v_id_suroviny IN surovina.id_suroviny%TYPE,
odcitaj IN OUT number
)
as
begin
...//some code here
odcitaj:=odcitaj-22;
...//some code here
end;
Procedure compiled w/o errors. I'm trying to execute it as:
execute odcitaj_surovinu_zo_skladu(1,200);
But it gives error, that '200' can't be used as target of assigment.
So how to execute it? Does ODCITAJ even need to be IN OUT? Cause i know that, if it was just IN , then it would act as constant and i won't be able to assign it anything
As Dmitry Bychenko said, you have to use a variable as the target of an OUT or IN OUT parameter, you can't provide a constant. Your parameter does need to be IN OUT since you're modifying it in the procedure. You can either use an anonymous block:
declare
l_odcitaj number;
begin
l_odcitaj := 200;
odcitaj_surovinu_zo_skladu(1, l_odcitaj);
-- do something with the updated value of l_odcitaj
end;
/
If you want to use the SQL*Plus/SQL Developer execute shorthand wrapper for an anonymous block you can declare a bind variable instead:
variable l_odcitaj number;
exec :l_odcitaj := 200;
exec odcitaj_surovinu_zo_skladu(1, :l_odcitaj);
Notice that the variable name has a colon in front when it is set and when the procedure is called, because it is a bind variable.
If you want you can then use that updated bind variable in other calls, or print it's post-procedure value:
print l_odcitaj
If the updated value - from odcitaj:=odcitaj-22; - doesn't need to be returned and is only used inside the procedure, you could declare the argument as IN and have a local variable which you set from the argument and then manipulate and use in the procedure.
create or replace procedure odcitaj_surovinu_zo_skladu
(
v_id_suroviny IN surovina.id_suroviny%TYPE,
v_odcitaj IN number
)
as
l_odcitaj number;
begin
l_odcitaj := v_odcitaj;
...//some code here
l_odcitaj:=l_odcitaj-22;
...//some code here
end;
/
You could then call the procedure with constant values. It just depends whether the caller needs to know the modified value.

When should I nest PL/SQL BEGIN...END blocks?

I've been somewhat haphazardly grouping subsections of code in BEGIN...END blocks when it seems right. Mostly when I'm working on a longer stored procedure and there's a need for a temporary variable in one spot I'll declare it just for that portion of the code. I also do this when I want to identify and handle exceptions thrown for a specific piece of code.
Any other reasons why one should nest blocks within a procedure, function or another larger block of PL/SQL?
When you want to handle exceptions locally like this:
begin
for emp_rec in (select * from emp) loop
begin
my_proc (emp_rec);
exception
when some_exception then
log_error('Failed to process employee '||emp_rec.empno);
end;
end loop;
end;
In this example, the exception is handled and then we carry on and process the next employee.
Another use is to declare local variables that have limited scope like this:
declare
l_var1 integer;
-- lots of variables
begin
-- lots of lines of code
...
for emp_rec in (select * from emp) loop
declare
l_localvar integer := 0;
begin
-- Use l_localvar
...
end
end loop;
end;
Mind you, wanting to do this is often a sign that your program is too big and should be broken up:
declare
l_var1 integer;
-- lots of variables
...
procedure local_proc (emp_rec emp%rowtype):
l_localvar integer := 0;
begin
-- Use l_localvar
...
end
begin
-- lots of lines of code
...
for emp_rec in (select * from emp) loop
local_proc (emp_rec);
end loop;
end;
I tend to nest blocks when I want to create procedures that are specific to data that only exists within the block. Here is a contrived example:
BEGIN
FOR customer IN customers LOOP
DECLARE
PROCEDURE create_invoice(description VARCHAR2, amount NUMBER) IS
BEGIN
some_complicated_customer_package.create_invoice(
customer_id => customer.customer_id,
description => description,
amount => amount
);
END;
BEGIN
/* All three calls are being applied to the current customer,
even if we're not explicitly passing customer_id.
*/
create_invoice('Telephone bill', 150.00);
create_invoice('Internet bill', 550.75);
create_invoice('Television bill', 560.45);
END;
END LOOP;
END;
Granted, it's not usually necessary, but it has come in really handy when a procedure can be called from many locations.
One reason to have nested BEGIN/END blocks is to be able to handle exceptions for a specific local section of the code and potentially continue processing if the exception is processed.