Passing Parameter from SQL-File to procedure - sql

Sadly I don't know the exact names for each part, probably I can explain it:
We have a SQL-File, which is defined like this:
declare
l_exec boolean := true;
l_print boolean := false;
---
begin
dbms_output.enable;
for t in (
And so on. We use this to create some Triggers etc. in our Oracle-Database.
Now this File grew to much and I need to split it up.
So I created a Main-File, which is looking like this:
SET SERVEROUTPUT on
set feedback off
DEFINE l_exec = TRUE;
DEFINE l_print = FALSE;
ACCEPT ora_instance PROMPT "Zielinstanz angeben [123|...]: " DEFAULT somedefault
CONNECT somelogins
PROMPT -- GENERATING CODE --
#file1 l_print, l_exec
This would work fine, but I'd like to pass the l_exec and l_print to each file. I read in some other Threads passing the parameters like this is ok. But the problem is: I not only need to pass it from file to file, but I need to pass it to the Procedure-Block after begin.
I'm not sure if I made it understandable, but I'd like to use the variable I pass at #file1 in the begin block of the Files.
Is this kindahow possible? I googled alot about SQLPlus, but didn't find a solution so far.
Thanks as always and a nice week
Matthias

Ok, so if your main file has the call to your sub-file:
...
CONNECT somelogins
PROMPT -- GENERATING CODE --
#file1 l_print, l_exec
then your sub-file can use the parameters in a positional manner thus:
declare
l_print boolean := &1;
l_exec boolean := &2;
---
begin
dbms_output.enable;
for t in (

Related

AsyncKeyDown pressed key value

Using Intraweb in Delphi,
Is there a way to find AsyncKeyDown pressed key value?
I know its somehow inside EventParams: TStringList but I only can see it through the Local variable list at runtime like this, string = 'char=i', and I cannot really get it out from there as a char unless I make a special function to pull it from there, so I'm thinking if there is a simple way to just get the pressed key as char from AsyncKeyDown ?
I know it's been a long time since the question was asked. But it will be good for those who still need it. I used it and working. You can get it as char as following:
http://www.devsuperpage.com/search/Articles.aspx?G=2&ArtID=52557
procedure TfrmLHasta.edSuzAsyncKeyDown(Sender: TObject;
EventParams: TStringList);
var
lKey: char;
begin
lKey := Chr(StrToInt(EventParams.Values['which']));
// WebApplication.ShowMessage(lKey);
if lKey = #13 then DoSomething;
end;

Cast to TObject

I am struggling to get the ItemIndex of my a Item in my TComoBox...
Usually that doesn't seem to be a hard thing to do for me... but somehow when i try to cast my String(which I got from a SQL select) to a TObject. That doesn't seem to work. I tried to debug my code with writing the String manually into the Object like so:
TObject('U');
That somehow does work, i just cant explain that...
The way i fill the ComoBox:
for i := Low(_VerkaufTypenBez) to High(_VerkaufTypenBez) do
begin
CBBelegart.AddItem(VerkaufTypenBez(i), Tobject(VerkaufTypenShort[i]));
end;
The way i tried to set the Index:
CB.ItemIndex := CB.Items.IndexOfObject(TObject(SetIndexWithSQL('select top 1 * from KOMSAconfig_Allgemein', 'Belegart'))); //index = -1
helper := 'U';
CB.ItemIndex := CB.Items.IndexOfObject(TObject(helper)); //index = -1
CB.ItemIndex := CB.Items.IndexOfObject(TObject('U')); //index = 1
Any suggestions?
This is what happens when you try to use a visual control as if it were a general purpose container. A visual control is used purely for display and user interaction. Don't try to bend it to do more than that.
In your scenario it is pointless to attempt to add a second string into the combo box. Stop doing that. Change your loop that populates to be like so:
for i := Low(_VerkaufTypenBez) to High(_VerkaufTypenBez) do
begin
CBBelegart.Items.Add(VerkaufTypenBez(i));
end;
The other string is held in an array like structure and seemingly can be accessed like this: VerkaufTypenShort[i]. So, if you want to lookup the index of a particular value, that can be done like so:
function GetIndexFromShortString(const Value: string): Integer;
begin
for Result := Low(_VerkaufTypenBez) to High(_VerkaufTypenBez) do
if VerkaufTypenShort[Result]=Value then
exit;
Result := -1;
end;
Notice that this function is completely independent of the visual control. You should strive to use visual controls for the bare minimum, and write your business logic without reference to any visual controls.
Your problem here seems to be that you get two strings from your query, where one is used as display text and the other is needed for internal lookups (coming from a different query).
I see two ways to solve this:
change your second query, so it returns the string, that is used in the combobox
have a lookup in your form that connects both strings
For the second you could use a TDictionary. My approach here assumes that filling the combobox is done only in one loop and there aren't strings added or removed dynamically.
Add the Dictionary to your form:
TForm1 = class(TForm)
...
private
FLookUp: TDictionary<string, Integer>;
In the FormCreate and FormDestroy events create and destroy your Dictionary.
FLookUp := TDictionary<string, Integer>.Create;
FLookUp.Free;
In the loop you use to fill the Combobox, store the Indices and connect them to your lookup-strings (I just copied your loop, assuming that it works as is).
procedure TForm1.FillCombo;
var
Index, I: Integer;
begin
FLookUp.Clear;
for i := Low(_VerkaufTypenBez) to High(_VerkaufTypenBez) do
begin
Index := CBBelegart.Items.Add(VerkaufTypenBez(i));
FLookUp.Add(VerkaufTypenShort[i], Index);
end;
end;
Now you can look for the correct Index using that Dictionary
function TForm1.GetIndexOfString(const Value: string): Integer;
begin
if not FLookUp.TryGetValue(Value, Result) then
Result := -1;
end;
Use it like
CB.ItemIndex := GetIndexOfString(SetIndexWithSQL(...));

What does the line " OPEN C_1 for Q_STR using MIN_NBR" mean? What does using the bind variable mean in this context

CREATE OR REPLACE
PROCEDURE SELECT_12(
MIN_NBR NUMBER ,
FIELD_NAME VARCHAR2 )
IS
TYPE cur_type
IS
REF
CURSOR;
C_1 CUR_TYPE;
QUERY_STR VARCHAR2(1000);
FIRST_NAME VARCHAR(1000);
BEGIN
Q_STR:= 'SELECT BORR_FST_NM from OCN_DM_DDS.' || field_name ||' WHERE MIN_NBR = :MINNBR';
OPEN C_1 FOR Q_STR USING MIN_NBR;
LOOP
FETCH C_1 INTO FIRST_NAME;
EXIT
WHEN C_1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(FIRST_NAME);
END LOOP;
NULL;
END SELECT_12;
What is happening in the line OPEN C_1 FOR Q_STR USING MIN_NBR? What does open "using the bind variable" mean?
The dynamic SQL statement includes:
WHERE MIN_NBR = :MINNBR
The colon indicates a bind variable; it can be called anything really but it's often named to match either the column name or more usefully the PL/SQL local variable that will be used to set it.
When the dynamic statement is executed;
OPEN C_1 FOR Q_STR USING MIN_NBR;
there are two stages. First it is parsed by the optimiser which decides on the most efficient way to fulfil the query, and at that point it still uses the bind variable placeholder rather than a specific value for that column. So it may decide there's an index it can use, whatever the actual value, for example. (It's a bit more complicated than that, but that's the gist).
Then it actually runs the query, using the plan it already knows it needs, but using the specific value you supplied. It gets that from the using part - the PL/SQL MIN_NBR variable's value is used to find the actual rows you want.
So while it's similar to doing:
SELECT BORR_FST_NM from OCN_DM_DDS.field_name WHERE MIN_NBR = fixed_value
the bind variable allows the same plan to be used for multiple queries.
When the bind value changes the same plan can be used - Oracle doesn't have to do a 'hard parse' to figure out how best to do it, it does a 'soft parse', recognises that it has seen the same query before, and uses the existing plan. This saves some time. It also stops the SQL cache being filled up with lots of almost-identical queries.
In your dynamic sql you have MIN_NBR = :MINNBR', where :MINNBR is the bind variable. You need to pass a value into the bind variable in order to make your query work when you run it - that's what the USING MIN_NBR part is doing.
It's saying pass the value of the MIN_NBR variable into the :MINNBR bind variable.

Output user input in PL/SQL using SQL developer

Hi everyone I'm new to PL/SQL ,however I'm wrting a small code that a prompt a user to input a 2 numbers and display the numbers using DBMS_output.Put_line .
but I get a compilation error ,below is my code ,I'm using "Oracle SQL developer"
SET SERVEROUTPUT ON SIZE 1000000;
DECLARE
n_no_1 number(8,2);
n_no_2 number(8,2);
BEGIN
DBMS_output.put_line('Enter Value for no 1');
&n_no_1;
DBMS_output.put_line('Enter value for no 2');
&n_no_2;
DBMS_OUTPUT.PUT_LINE('The value of No 1 is' || n_no_1 );
DBMS_OUTPUT.PUT_LINE('The value of No 2 is' || n_no_2 );
END;
/
These 2 lines are your problem, however, not for the reasons mentioned in other answer:
&n_no_1;
&n_no_2;
In SQL, you can use the ampersand (&) to trigger something called "Macro substitution".
When the compiler comes across something like this (ie &n_no1), it prompts the user to input a value for it to substitute in it's place.
So if you enter "Hello". Your code becomes:
DBMS_output.put_line('Enter Value for no 1');
Hello;
And as you can see, that would fail, if you had just typed that out.
What you want to do is to assign that value to a variable, like this:
n_no_1 := '&n_no_1';
That gets "replaced" by this:
n_no_1 := 'Hello';
which compiles - and runs - just fine.
That all said, this is NOT the best way to do this, although this appears to be a learning excercise ?
Look up the PROMPT and ACCEPT keywords .. you can use those in SQL (outside your BEGIN / DECLARE / END block) to capture the values first in a neater fashion :)
http://docs.oracle.com/cd/E11882_01/server.112/e16604/ch_twelve032.htm#SQPUG052
http://docs.oracle.com/cd/E11882_01/server.112/e16604/ch_twelve005.htm#SQPUG026
Found an additional link here worth a good read. Explains a lot more than what you're looking at, but it discusses substitution variables, and other similiar things (and some other unrelated things :) )
https://blogs.oracle.com/opal/entry/sqlplus_101_substitution_varia
The &variable syntax is not PL/SQL: it is part of SQL Developer. I see what you are trying to do and syntax errors, but there's no point in correcting them because it's not going to work in the end.
The reason is: you cannot accept user input via PL/SQL at runtime.

Selecting large sequence value into global variable not working in PL/SQL code

I have a script where I would like to have a global variable to store a transaction number for later use. The code is working fine on one schema where the sequence value that I'm fetching is relatively low. It is not working on another schema with a higher sequence value where I get a "Numeric Overflow". If I change that sequence value to a lower number it is working as well but that is not an option.
VAR TRANSACTIONNR NUMBER;
BEGIN
--Works with NEXTVAL being around 946713241
--Doesn't work with NEXTVAL being around 2961725541
SELECT MY_SEQUENCE.NEXTVAL INTO :TRANSACTIONNR FROM DUAL;
MY_PACKAGE.STARTTRANSACTION(:TRANSACTIONNR);
END;
/
-- SQL Statements
BEGIN
MY_PACKAGE.ENDTRANSACTION;
MY_PACKAGE.DO_SOMETHING(:TRANSACTIONNR);
END;
/
What is also working is selecting the sequence into a variable declared in the DECLARE block:
DECLARE
TRANSACTIONNR NUMBER;
BEGIN
SELECT MY_SEQUENCE.NEXTVAL INTO TRANSACTIONNR FROM DUAL;
MY_PACKAGE.STARTTRANSACTION(TRANSACTIONNR);
END;
/
But that means I won't be able to reuse it in the block at the end. Setting the size of the number is not possible.
VAR TRANSACTIONNR NUMBER(15)
is not valid.
Any ideas what I could try or other ways to store global state?
On further investigation this looks like it might be a SQL Developer bug (making assumptions about what you're doing again, of course...). I can get the same error with:
VAR TRANSACTIONNR NUMBER;
BEGIN
SELECT 2961725541 INTO :TRANSACTIONNR FROM DUAL;
END;
/
It appears that SQL Developer's NUMBER is limited to 2^31, which isn't the case normally for Oracle.
A possibly workaround is to use BINARY_FLOAT to store the value, but you'll run into precision problems eventually (not sure where, but looks OK up to 2^53-ish), and you'll need to cast() it back to NUMBER when using it.
VAR TRANSACTIONNR BINARY_DOUBLE;
BEGIN
SELECT 2961725541 INTO :TRANSACTIONNR FROM DUAL;
-- dbms_output.put_line(cast(:TRANSACTIONNR as NUMBER)); -- null for some reason
END;
/
...
BEGIN
dbms_output.put_line(cast(:TRANSACTIONNR as NUMBER));
END;
/
For some reason I don't seem to be able to refer to the bind variable again in the anonymous block I set it in - it's null in the commented-out code - which seems to be another SQL Developer quirk, regardless of the var type; but as you were doing so in your code, I may again have assumed too much...
Original answer for posterity, as it may still be relevant in other circumstances...
Presumably you're doing something to end the current transaction, e.g. a commit in endtransaction; otherwise you could just refer to my_sequence.currval in the do_something call. A number variable is fine for this size of number though, it won't have a problem with a sequence that size, and it won't make any difference that it is from a sequence rather than manually assigned. I don't think the problem is with the storage or the sequence.
It seems rather more likely that the error is coming from one of the package procedures you're calling, though I can't quite imagine what you might be doing with it; something like this will cause the same error though:
create sequence my_sequence start with 2961725541;
create package my_package as
procedure starttransaction(v_num number);
procedure endtransaction;
procedure do_something(v_num number);
end my_package;
/
create package body my_package as
procedure starttransaction(v_num number) is
begin
dbms_output.put_line('starttransaction(): ' || v_num);
for i in 1..v_num loop
null;
end loop;
end starttransaction;
procedure endtransaction is
begin
dbms_output.put_line('endtransaction()');
end endtransaction;
procedure do_something(v_num number) is
begin
dbms_output.put_line('do_something(): ' || v_num);
end do_something;
end my_package;
/
When your code is run against that it throws your error:
BEGIN
*
ERROR at line 1:
ORA-01426: numeric overflow
ORA-06512: at "STACKOVERFLOW.MY_PACKAGE", line 6
ORA-06512: at line 5
endtransaction()
do_something():
Note the error is reported against line 6 in the package, which is the for ... loop line, not from the assignment in your anonymous block.
Looping like that would be an odd thing to do of course, but there are probably other ways to generate that error. The breakpoint for it working is if the nextval is above 2^31. If I start the sequence with 2147483647 it works, with 2147483648 it errors.
I'm assuming you are actually getting an ORA-01426 from the original question; if it's actually a ORA-1438 or ORA-06502 then it's easier to reproduce, by trying to assign the value to a number(9) column or variable. 'Numeric overflow' is pretty specific though.