Insert Into A Table With 200 Columns - oracle

We Have A Table in our Oracle DB With 200 Columns Which we have to do Insert into it and we have an C program which inserts requests in that table and it uses Pro*C to Call an stored program in oracle DB to do the job.
Currently we serialize all fields with TLV format{Tag-Length-Value} and have a function with a Varchar2 parameter(the serialized list of fields). for example:
008603701212345678901201100611111104800622222220000633333320100644444401201420200321164712
more human readable:
0086 037 012 123456789012 011 006 111111 048 006 222222 200 006 333333 201 006 444444 012 014 20200321164712
which means:
String Len:86
Field037 with len 12:123456789012
Field011 with len 6:111111
Field048 with len 6:222222
Field200 with len 6:333333
Field201 with len 6:444444
Field012 with len 14:20200321164712
each of these fields map to a field in the table, the stored program parses the huge string and fills them into a rowtype and finally inserts the row in the table.
For years it was working well, but when we get ready for more requests(thus more inserts) our DBA says we are using a lot of CPU for de-serializing the TLV. So we have to change our insert method.
I Currently created an structure in C and map all fields in it and call:
typedef struct
{
char field37[12+1];
char field11[6+1];
char field48[6+1];
char field200[6+1];
char field201[6+1];
char field12[14+1];
} NewRecord;
NewRecord newRecord;
TlvToStruct(sTlv, &newRecord);//Mapper Function
EXEC SQL INSERT INTO MY_TABLE(FIELD37, FIELD11, FIELD200, FIELD201, FIELD12) VALUES(:newRecord);
EXEC SQL COMMIT WORK RELEASE;
This approach works well for now, but my question is: Should I continue developing and add all 200 fields to this structure and use this approach or it's better to use PLSQL call(maybe develop and using another insert function) instead of this SQL Insert?
I am currently aware of PLSQL benefits, but there is some problems with it here:
1- Pro*C does not support PLSQL Records
[Oracle Document]1
2- It is not so reasonable to have a function with 200 parameters!
3- We use Types in plsql but never used them in Pro*C(actually I tried once and I failed long ago)
I tried to describe the problem here, if it is unclear please ask
Thanks
==================================================
EDIT:
Here is our old Get_Tag and Put_Tag functions:
FUNCTION GET_TAG(P_TAG_NAME IN VARCHAR2, P_TLV_STRG IN VARCHAR2, P_TAG_LEN OUT NOCOPY PLS_INTEGER, P_TAG_VALUE OUT NOCOPY VARCHAR2)
RETURN PLS_INTEGER IS
V_COUNTER_LOOP PLS_INTEGER := 1;
V_TLV_STRG VARCHAR2(4096) := SUBSTR(P_TLV_STRG, 5);
BEGIN
P_TAG_VALUE := NULL;
P_TAG_LEN := 0;
WHILE V_COUNTER_LOOP < LENGTH(V_TLV_STRG) LOOP
IF SUBSTR(V_TLV_STRG, V_COUNTER_LOOP, 3) = P_TAG_NAME THEN
P_TAG_LEN := TO_NUMBER(SUBSTR(V_TLV_STRG, V_COUNTER_LOOP + 3, 3));
P_TAG_VALUE := SUBSTR(V_TLV_STRG, V_COUNTER_LOOP + 6, P_TAG_LEN);
RETURN (DECLARATION_CST.OK);
END IF;
V_COUNTER_LOOP := V_COUNTER_LOOP + 6 + TO_NUMBER(SUBSTR(V_TLV_STRG, V_COUNTER_LOOP + 3, 3));
END LOOP;
RETURN 0;
EXCEPTION
WHEN OTHERS THEN
RETURN -1;
END GET_TAG;
===========================================================
FUNCTION PUT_TAG(P_TAG_NAME IN VARCHAR2, P_TAG_VALUE IN VARCHAR2, P_TLV_STRG IN OUT NOCOPY VARCHAR2)
RETURN PLS_INTEGER IS
V_COUNTER_LOOP PLS_INTEGER := 0;
TMP_VARCHAR VARCHAR2(4096);
BEGIN
P_TLV_STRG := SUBSTR(P_TLV_STRG, 5);
V_COUNTER_LOOP := 1;
WHILE V_COUNTER_LOOP < LENGTH(P_TLV_STRG) LOOP
IF SUBSTR(P_TLV_STRG, V_COUNTER_LOOP, 3) = SUBSTR(P_TAG_NAME, 1, 3) THEN
TMP_VARCHAR :=
SUBSTR(P_TLV_STRG, 1, V_COUNTER_LOOP - 1)
|| SUBSTR(P_TAG_NAME, 1, 3)
|| TO_CHAR(NVL(LENGTH(P_TAG_VALUE), 0), 'FM000')
|| P_TAG_VALUE
|| SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 6 + TO_NUMBER(SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 3, 3)));
P_TLV_STRG := TO_CHAR(LENGTH(TMP_VARCHAR), 'FM0000') || TMP_VARCHAR;
RETURN (DECLARATION_CST.OK);
END IF;
V_COUNTER_LOOP := V_COUNTER_LOOP + 6 + TO_NUMBER(SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 3, 3));
END LOOP;
P_TLV_STRG :=
P_TLV_STRG
|| SUBSTR(P_TAG_NAME, 1, 3)
|| TO_CHAR(NVL(LENGTH(P_TAG_VALUE), 0), 'FM000')
|| P_TAG_VALUE;
P_TLV_STRG := TO_CHAR(LENGTH(P_TLV_STRG), 'FM0000') || P_TLV_STRG;
RETURN 0;
EXCEPTION
WHEN OTHERS THEN
RETURN -1;
END PUT_TAG;

You need to create the mapping table first in the database which maps to the field and its length (in my example I have used CTE as your mapping table: YOUR_MAPPING_TABLE).
Oracle setup:
SQL> CREATE TABLE TEST1234 (
2 FIELD001 VARCHAR2(4000),
3 FIELD002 VARCHAR2(4000),
4 FIELD003 VARCHAR2(4000),
5 FIELD004 VARCHAR2(4000)
6 );
Table created.
Solution query:
SQL> INSERT INTO TEST1234
2 WITH YOUR_MAPPING_TABLE (FIELD_ID, LEN)
3 AS (
4 SELECT 'FIELD001', 4 FROM DUAL UNION ALL
5 SELECT 'FIELD002', 3 FROM DUAL UNION ALL
6 SELECT 'FIELD003', 3 FROM DUAL UNION ALL
7 SELECT 'FIELD004', 12 FROM DUAL
8 )
9 SELECT * FROM
10 ( SELECT M.FIELD_ID, -- Your string will go in following substring
11 SUBSTR('0086037012123456789012', M.STARTPOS + 1, M.TOTALLEN) AS FIELDVALUE
12 FROM ( SELECT FIELD_ID,
13 SUM(LEN) OVER(ORDER BY FIELD_ID ) - LEN AS STARTPOS,
14 LEN AS TOTALLEN
15 FROM YOUR_MAPPING_TABLE
16 ) M
17 ) PIVOT (
18 MIN ( FIELDVALUE )
19 FOR FIELD_ID IN ( 'FIELD001', 'FIELD002', 'FIELD003', 'FIELD004' )
20 );
1 row created.
Testing the result
SQL> SELECT * FROM TEST1234;
FIELD001 | FIELD002 | FIELD003 | FIELD004
---------- | ---------- | ---------- | -------------
0086 | 037 | 012 | 123456789012
SQL>
You can pass your big string in the query as per your logic.

Related

Generating unique value and a virtual column

In an attempt to generate random unique values, which can be exposed (ie customer_id) and used as a PRIMARY KEY I am doing the following:
Generate random number, grabbing the seconds and fractions of a second from the current timestamp, and also I append to that a fixed width sequence to guarantee the uniqueness. This is all working fine. See below as I am generating some customer information.
My question is how can I make the seed column virtual as I really don't need to have it stored but I would like to see the value if needed.
Thanks in advance to all who answer and apologies for the verbose test CASE
CREATE OR REPLACE PACKAGE mf_names IS
FUNCTION random_first_name(
gender IN VARCHAR2 DEFAULT NULL,
percentage_mf IN NUMBER DEFAULT 50
) RETURN VARCHAR2;
FUNCTION random_last_name RETURN VARCHAR2;
END;
/
CREATE OR REPLACE PACKAGE BODY mf_names IS
first_names_male SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(
'Tom', 'Andy', 'Paul', 'Peter', 'Keith', 'Mark', 'Solomon', 'Joseph', 'John', 'Roger', 'Douglas','Harry', 'Barry', 'Larry', 'Gary', 'Jeffrey', 'David', 'Stuart', 'Karl', 'Seth', 'David', 'Brian', 'Sidney', 'James', 'Shane', 'Zachary', 'Anthony'
);
first_names_female SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(
'Alice', 'Anna', 'Lee', 'Barbara', 'Carol', 'Debra', 'Madison', 'Faith', 'Cheryl', 'Beth', 'Kathy', 'Abigail', 'Jill', 'Grayce', 'Lynn', 'Roz', 'Carolyn', 'Deena', 'Laura', 'Sophia', 'Elise'
);
last_names SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(
'Cooper', 'Dimeo', 'Caygle', 'Luppo', 'Coralnick', 'Torchiano', 'Fazio', 'Behrens', 'Zaza', 'Lebowitz', 'Stern', 'Malden', 'Kramer', 'Stein', 'Tessio', 'Weinreb', 'Dillon', 'Zanona', 'Rucker', 'Zanzone', 'Santoro', 'Barese', 'Silverberg', 'Aarron', 'Kern', 'Saladino', 'Rice', 'Sanford', 'Orr', 'Roth'
);
FUNCTION random_first_name(
gender IN VARCHAR2 DEFAULT NULL,
percentage_mf IN NUMBER DEFAULT 50
) RETURN VARCHAR2
IS
BEGIN
IF UPPER(gender) LIKE 'M%' THEN
RETURN first_names_male(FLOOR(DBMS_RANDOM.VALUE(1, first_names_male.COUNT + 1)));
ELSIF UPPER(gender) LIKE 'F%' THEN
RETURN first_names_female(FLOOR(DBMS_RANDOM.VALUE(1, first_names_female.COUNT + 1)));
ELSIF DBMS_RANDOM.VALUE(0, 100) < percentage_mf THEN
RETURN random_first_name('M');
ELSE
RETURN random_first_name('F');
END IF;
END;
FUNCTION random_last_name RETURN VARCHAR2
IS
BEGIN
RETURN last_names(FLOOR(DBMS_RANDOM.VALUE(1, last_names.COUNT + 1)));
END;
END;
/
CREATE TABLE CUSTOMERS (
customer_id VARCHAR2 (20),
seed NUMBER,
first_name VARCHAR2 (20),
last_name VARCHAR2 (20),
constraint customer_id_pk primary key (customer_id));
create sequence customer_seq start with 1000000 minvalue 1000000 maxvalue 9999999 cycle;
create or replace function base34(p_num number) return varchar2 is
l_dig varchar2(34) := 'AB0CD1EF2GH3JK4LM5NP6QR7ST8UV9WXYZ';
l_num number := p_num;
l_str varchar2(38);
begin
loop
l_str := substr(l_dig,mod(l_num,34)+1,1) || l_str ;
l_num := trunc(l_num/34);
exit when l_num = 0;
end loop;
return l_str;
end;
/
create or replace function dec34(p_str varchar2) return number is
l_dig varchar2(34) := 'AB0CD1EF2GH3JK4LM5NP6QR7ST8UV9WXYZ';
l_num number := 0;
begin
for i in 1 .. length(p_str) loop
l_num := l_num * 34 + instr(l_dig,upper(substr(p_str,i,1)))-1;
end loop;
return l_num;
end;
/
create or replace trigger customer_trg
before update on customers for each row
begin
if ( updating('customer_id') )
then
raise_application_error(-20000,'Cant Update customer_id');
end if;
if ( updating('seed') )
then
raise_application_error(-20001,'Cant Update seed');
end if;
end;
/
DECLARE
seed NUMBER;
begin
for i in 1 .. 100 loop
seed := (to_number(trunc(dbms_random.value(1000,9999))|| to_char(systimestamp,'FFSS')||customer_seq.nextval));
INSERT into customers(
customer_id,
seed,
first_name,
last_name
) VALUES (
base34(seed),
seed,
mf_names.random_first_name(),
mf_names.random_last_name()
);
end loop;
end;
/
select to_char(seed) from customers where rownum = 1
union all
select base34(seed) from customers where rownum = 1
union all
select to_char(dec34(base34(seed))) from customers where rownum = 1;
TO_CHAR(SEED)
5444355405000301000000
BZCK24C0D3CHYE6
5444355405000301000000
select base34(seed),
dump(base34(seed)) from customers where rownum = 1
BASE34(SEED) DUMP(BASE34(SEED))
BZCK24C0D3CHYE6 Typ=1 Len=15: 66,90,67,75,50,52,67,48,68,51,67,72,89,69,54
I'm afraid you can't do that because dbms_random.value and systimestamp functions you'd want to use aren't deterministic (they should return the same value whenever called, while these two don't).
For example: this works because 1 + 2 is always 3:
SQL> create table test
2 (id number generated always as identity,
3 seed number generated always as (1 + 2)
4 );
Table created.
SQL> drop table test;
Table dropped.
Can you use dbms_random.value? Nope:
SQL> create table test
2 (id number generated always as identity,
3 seed number generated always as (to_number(trunc(dbms_random.value(1000,9999))))
4 );
seed number generated always as (to_number(trunc(dbms_random.value(1000,9999))))
*
ERROR at line 3:
ORA-30553: The function is not deterministic
If the function is deterministic, mark it DETERMINISTIC. If it is not deterministic (it depends on package state, database state, current time, or anything other than the function inputs) then do not create the index. The values returned by a deterministic function should not change even when the function is rewritten or recompiled.
Can you use sysdate (or systimestamp, no difference)? No:
SQL> create table test
2 (id number generated always as identity,
3 seed number generated always as (to_number(to_char(sysdate, 'yyyymmdd')))
4 );
seed number generated always as (to_number(to_char(sysdate, 'yyyymmdd')))
*
ERROR at line 3:
ORA-54002: only pure functions can be specified in a virtual column expression
Column expression contained a function whose evaluation is non-deterministic
Therefore, that won't work.
Wouldn't it be simpler to just use SYS_GUID function to generate the primary key value ? It is a unique number. We do this for all the tables in our projects.
CREATE TABLE some_name
( some_name_id NUMBER DEFAULT ON NULL to_number(sys_guid(), 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX') NOT NULL
PRIMARY KEY
--other columns
) ;
No function needed, no seeding needed, no virtual column needed.

Oracle hierarchical recursive query stop at cycle and continue with other package name

I am a little struggling at the moment with the following problem.
I want to know which package calls which packages. The table is not hierarchical.
Its the table user_dependencies.
The code so far:
CREATE OR REPLACE PACKAGE object_x is
type ObjectRec is record(
dName varchar2(250),
level number
);
type ObjectTemp is table of ObjectRec;
function Referenced(dname VARCHAR2, level NUMBER, maxl NUMBER) return ObjectTemp pipelined;
end;
/
CREATE OR REPLACE PACKAGE BODY object_x is
function Referenced(dname VARCHAR2, level NUMBER, maxl NUMBER) return ObjectTemp pipelined is
rData ObjectRec;
begin
if level >= maxl then
return;
end if;
if level = 1 then
rData.dName := name;
rData.Level := maxl;
pipe row(rData);
end if;
for r in (
select referenced_name
from user_dependencies
where name = upper(dname)
and type = 'PACKAGE BODY'
and referenced_type = 'PACKAGE'
and referenced_name != UPPER(dname)
and referenced_name != name
)
loop
rData.dName := LPAD(' ', 3, ' ') || r.Referenced_name;
rData.level := level+1;
pipe row(rData);
rData.Name := r.Referenced_name;
for r2 in (select * from table(Referenced(rData.Name, level + 1, maxl))) loop
rData.Name := LPAD(' ', 3, ' ') || r2.dName;
rData.Level := r2.Level;
pipe row(rData);
null;
end loop;
end loop;
RESULT:
Level Dname
---------- --------------------------------------------------------------------------------
1 PAC1
2 PAC2
2 PAC3
2 PAC4
2 PAC5
3 PAC6
3 PAC2
3 PAC7
3 PAC8
4 PAC9
4 PAC10
5 PAC6
5 PAC11
5 PAC3
5 PAC9
5 PAC12
5 PAC6
5 PAC3
5 PAC9
5 PAC4
5 PAC8
3 PAC10
4 PAC6
4 PAC11
4 PAC3
4 PAC9
4 PAC12
4 PAC4
EXPECTED RESULT:
Level Dname
---------- --------------------------------------------------------------------------------
1 PAC1
2 PAC2
2 PAC3
2 PAC4
2 PAC5
3 PAC6
3 PAC2
3 PAC7
3 PAC8
4 PAC9
4 PAC10
5 PAC6
5 PAC11
5 PAC3
5 PAC9
5 PAC12
5 PAC6
5 PAC3
5 PAC9
5 PAC4
5 PAC8
3 PAC10 LOOP!!!!
-----BREAK------
CONTINUE WITH OTHER PACKAGES………..
Thanks for any advice.
Ultimately, yes, it is possible to do what you want.
One way to do this is to use a nested table to store the chain of dependent packages that you've built up through the current chain of recursive function calls. You can use the MEMBER OF operator with this nested table to check whether a package is already in this chain (and so you've found a loop). If not, you can call your Referenced function recursively, but adding an extra item to this table for the current package.
I made these changes to your code and ended up with the following:
CREATE OR REPLACE TYPE varchar2_table AS TABLE OF VARCHAR2(250);
/
CREATE OR REPLACE PACKAGE object_x is
type ObjectRec is record(
dName varchar2(250),
dLevel number
);
type ObjectTemp is table of ObjectRec;
function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER) return ObjectTemp pipelined;
function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER, pkgs_so_far IN varchar2_table) return ObjectTemp pipelined;
end;
/
CREATE OR REPLACE PACKAGE BODY object_x is
function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER) return ObjectTemp pipelined is
rData ObjectRec;
begin
for r2 in (select * from table(Referenced(dname, dlevel, maxl, varchar2_table(dname))))
loop
rData.dName := r2.dName;
rData.dLevel := r2.dLevel;
pipe row(rData);
end loop;
end;
function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER, pkgs_so_far IN varchar2_table) return ObjectTemp pipelined is
rData ObjectRec;
new_pkgs_so_far varchar2_table;
loop_detected boolean;
begin
if dlevel >= maxl then
return;
end if;
if dlevel = 1 then
rData.dName := dname;
rData.dLevel := 1;
pipe row(rData);
end if;
for r in (
select referenced_name
from user_dependencies
where name = upper(dname)
and type = 'PACKAGE BODY'
and referenced_type = 'PACKAGE'
and referenced_name != UPPER(dname)
and referenced_name != name
)
loop
loop_detected := r.referenced_name MEMBER OF pkgs_so_far;
rData.dName := LPAD(' ', 3, ' ') || r.Referenced_name;
if loop_detected then
rData.dName := rData.dName || ' --- LOOP';
end if;
rData.dLevel := dLevel + 1;
pipe row(rData);
if not loop_detected THEN
new_pkgs_so_far := pkgs_so_far;
new_pkgs_so_far.EXTEND(1);
new_pkgs_so_far(new_pkgs_so_far.COUNT) := r.referenced_name;
for r2 in (select * from table(Referenced(r.referenced_name, dLevel + 1, maxl, new_pkgs_so_far))) loop
rData.dName := LPAD(' ', 3, ' ') || r2.dName;
rData.dLevel := r2.dLevel;
pipe row(rData);
end loop;
end if;
end loop;
end;
end;
/
I've modified your pipelined function to add the pkgs_so_far parameter, which contains the table of packages currently encountered. I've added another pipelined function with the same signature as your original function, and this just calls the other one with the table of packages containing only the given package.
In the modified function, we now use the MEMBER OF operator to check whether we've already come across the current package. If we have, we can add an extra --- LOOP marker to indicate this. If we haven't come across this package before, we add the package name to the table and continue to recursively examine its references. Note that assigning the table in the line new_pkgs_so_far := pkgs_so_far; appears to set new_pkgs_so_far to a copy of the table in pkgs_so_far, so we can mutate new_pkgs_so_far without this affecting pkgs_so_far.
However, there were also a number of other issues I cleared up with your code:
The package body appears to be missing the final two end; lines, one to end the procedure and one to end the package body itself.
Your code contains some references to the name field of your record, but it is named dName. I've tidied these all up to use dName.
Oracle didn't seem to like the name level being used, so I changed it to dLevel. (LEVEL is an Oracle keyword, it's used in hierarchical SQL.)
I'm not sure it's correct to pipe out a row with dLevel set to maxl in the case where the variable dlevel is 1, so I've changed this to 1 instead.
When calling your References function recursively, I've changed the first parameter to be r.referenced_name, as then we don't need to assign to rData.dName.
I've removed an unnecessary null; statement within the loop that iterates through a recursive call to your procedure.
I did also note that you've written LPAD(' ', 3, ' ') twice, but I'm not sure why: this is a long-winded way of writing ' ', where there are three spaces in the string.
Some of these errors may have arisen because you anonymised your code before posting it here but you didn't check whether the code you posted compiles before posting. In future, please ensure your code compiles, as otherwise anyone who wishes to help you will have to first fix your compilation errors.
I also don't have all of your packages (and I suspect in reality they aren't named PAC1, PAC2 and so on), so I created three packages PAC1 to PAC3. PAC2 and PAC3 both reference each other and PAC1, and the output I got was as follows:
SQL> COLUMN dname FORMAT a30
SQL> select * from table(object_x.referenced('PAC2', 1, 10));
DNAME DLEVEL
------------------------------ ----------
PAC2 1
PAC1 2
PAC3 2
PAC1 3
PAC2 --- LOOP 3

to call a funktion with a rowtype Parameter

hello everyone i have the following function:
function Entfernung(v_stadt Rheinlandstädte%rowtype)
Return Abstandstabelle is
tabelle ABSTANDSTABELLE;
cursor c_städte IS
Select *
From Rheinlandstädte
;
v_andereStadt Rheinlandstädte%rowtype;
v_entfernung float;
begin
Open c_städte;
LOOP
fetch c_städte into v_andereStadt;
v_entfernung := Abstand(v_stadt, v_andereStadt);
tabelle(v_andereStadt.stadtname) := v_entfernung;
exit when c_städte%NOTFOUND;
END Loop;
close c_städte;
return tabelle;
end Entfernung;
how can i call this function in oracle. The parameter is rowtype and I cant't use SELECT function from DUAL;
A simple example:
SQL> create table tab_par(a number, b number)
2 /
Table created.
SQL> create or replace type tab_numbers as table of number
2 /
Type created.
SQL> create or replace function fun_par (r IN tab_par%rowtype) return tab_numbers is
2 retVal tab_numbers := new tab_numbers();
3 begin
4 retVal.extend(2);
5 retVal(1) := r.a;
6 retVal(2) := r.b;
7 return retVal;
8 end;
9 /
Function created.
SQL> insert into tab_par values ( 10, 5);
1 row created.
SQL> declare
2 r tab_par%rowtype;
3 n tab_numbers;
4 begin
5 select *
6 into r
7 from tab_par
8 where rownum = 1;
9 -- call your function
10 n := fun_par(r);
11 dbms_output.put_line('n = ' || n(1));
12 dbms_output.put_line('n = ' || n(2));
13 end;
14 /
n = 10
n = 5
PL/SQL procedure successfully completed.
SQL>
here is how I call this function, this function is in the package:
DECLARE
v_stadtname_a VARCHAR(20):='Düsseldorf';
v_stadtA Rheinlandstädte%rowtype;
tabelle GEOGRAPHICAL_PACKAGE.ABSTANDSTABELLE;
v_stadt Rheinlandstädte%rowtype;
CURSOR c_sta IS
SELECT * FROM Rheinlandstädte;
BEGIN
SELECT * INTO v_stadtA
FROM rheinlandstädte
WHERE stadtname=v_stadtname_a;
OPEN c_sta;
LOOP
FETCH c_sta INTO v_stadt;
EXIT WHEN c_sta%NOTFOUND;
tabelle := GEOGRAPHICAL_PACKAGE.Entfernung(v_stadtA);
dbms_output.put_line('Abstand zwischen '||v_stadtA.stadtname||' und '||
v_stadt.stadtname||' beträgt: '||tabelle(v_stadt.stadtname)||' km.');
END LOOP;
CLOSE c_sta;
END;

Error while creating and executing a function

I have a table as follows:
SCREENING_ID PLAN_ID THEATRE_ID SCREENING_DATE SCREENING_START_HH24 SCREENING_START_MM60
1 1 3 01.06.2015 00:00:00 9 0
2 1 3 01.06.2015 00:00:00 11 30
3 1 3 01.06.2015 00:00:00 14 0
4 1 4 01.06.2015 00:00:00 14 0
I have to check whether a particular time slot is available or not.This function should contain the following input parameters: plan_id_p, theatre_id_p, screening_date_p, screening_start_hh24_p, screening_start_mm60_p. It should return 1 if having a time slot otherwise return 0.
My code:
CREATE OR REPLACE FUNCTION Func_is_time_available
(plan_id_p IN NUMBER,
theatre_id_p IN OUT NUMBER,
screening_date_p IN OUT DATE,
screening_start_hh24_p IN OUT NUMBER,
screening_start_mm60_p IN OUT NUMBER)
RETURN NUMBER
AS
return_val NUMBER;
CURSOR cr2 IS
SELECT plan_id,
THEATRE_ID,
SCREENING_DATE,
SCREENING_START_HH24,
SCREENING_START_MM60
FROM screening s
WHERE plan_id = plan_id_p and
theatre_id=theatre_id_p and
SCREENING_DATE=SCREENING_DATE_P and
SCREENING_START_HH24=SCREENING_START_HH24_P and
SCREENING_START_MM60=SCREENING_START_MM60_P;
BEGIN
OPEN cr2;
FETCH cr2 INTO plan_id_p,
THEATRE_ID_P,
SCREENING_DATE_P,
SCREENING_START_HH24_P,
SCREENING_START_MM60_P;
IF cr2%NOTFOUND THEN
return_val := 1;
ELSE
return_val := 0;
END IF;
CLOSE cr2;
RETURN return_val;
END;
And the execution part:
DECLARE
v_result NUMBER;
BEGIN v_result:=Func_is_time_available(plan_id_p=>1,
theatre_id_p=>3,
screening_date_p=>'1/JUN/2015',
screening_start_hh24_p=>9,
screening_start_mm60_p=>0);
END;
But this is giving error as:
ORA-06550: line 18, column 44:
PLS-00363: expression '<expression>' cannot be used as an assignment target
Can anyone explain what my mistake is here?
I am using oracle.
I have made changes to your code refer below
CREATE OR REPLACE FUNCTION Func_is_time_available
(plan_id_p IN NUMBER,
theatre_id_p IN NUMBER,
screening_date_p IN DATE,
screening_start_hh24_p IN NUMBER,
screening_start_mm60_p IN NUMBER)
RETURN NUMBER
AS
return_val NUMBER;
var1 number;
begin
SELECT count(*) into var1
FROM screening s
WHERE plan_id = plan_id_p and
theatre_id=theatre_id_p and
SCREENING_DATE=SCREENING_DATE_P and
SCREENING_START_HH24=SCREENING_START_HH24_P and
SCREENING_START_MM60=SCREENING_START_MM60_P;
if var1 >= 1 then -- count can be 1 or > 1
return_val:= 1;
else return_val:=0;
end if;
end;
Make all parameters as IN instead of IN OUT and try.

using long string(over 4000) in oracle query

I know that in sql varchar2 can only be around 4000.
I know that in oracle PL varchcar2 can be around 32000.
I have a varchar2 variable defined that is over 4000 characters long and I want to use it in a query. I don't want to insert the value into a table. The value is a dilimited string that I am parsing and inserting into a table with this query. This query works when the variable is less than 4000 characters long. Is there a way to make it work with up to 32000 characters?
create global temporary table t(single_element varchar(500),element_no number);
declare
--declared as 32767 but this string contains less than 4000 characters.
--This will work. If you expand the string to 32000 characters it will not work.
myvar varchar2(32767) := 'tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4';
begin
delete from t;
insert into t
SELECT SUBSTR(str, start_pos, (next_pos-start_pos)) AS single_element, element_no
FROM (
SELECT
ilv.str,
nt.column_value AS element_no,
INSTR(ilv.str, '^~', DECODE(nt.column_value, 1, 0, 1), DECODE(nt.column_value, 1, 1, nt.column_value-1)) + 2 AS start_pos,
INSTR(ilv.str, '^~', 1, DECODE(nt.column_value, 1, 1, nt.column_value)) AS next_pos
FROM (
select '~' || myvar || '^~' as str,
(Length(myvar) - length(replace(myvar,'^~','')))/2 + 2 as no_of_elements
from dual) ilv,
TABLE(
CAST(
MULTISET(
SELECT ROWNUM FROM dual CONNECT BY ROWNUM < ilv.no_of_elements
) AS number_ntt )) nt
);
end;
The error I get when expanding "myvar" to 32000 characters is
can bind a LONG value only for insert into a LONG column
Is there a way I can get around this size restraint because i'm not actually inserting this value into a table, i'm just using it in the query?
Do you have to define the variable as a VARCHAR2? Could you define it as a CLOB instead?
If I change the declaration of MYVAR from a VARCHAR2(32767) to a CLOB and define the NUMBER_NTT type, your code runs for me
SQL> ed
Wrote file afiedt.buf
SP2-0161: line 2 truncated.
1 declare
2 myvar clob := 'tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testms
<<snip>>
~tcd3~#testmsg3^~tcd4~#testmsg4';
4 begin
5 delete from t;
6 insert into t
7 SELECT SUBSTR(str, start_pos, (next_pos-start_pos)) AS single_element, elem
ent_no
8 FROM (
9 SELECT
10 ilv.str,
11 nt.column_value AS element_no,
12 INSTR(ilv.str, '^~', DECODE(nt.column_value, 1, 0, 1), DECODE
(nt.column_value, 1, 1, nt.column_value-1)) + 2 AS start_pos,
13 INSTR(ilv.str, '^~', 1, DECODE(nt.column_value, 1, 1, nt.colu
mn_value)) AS next_pos
14 FROM (
15 select '~' || myvar || '^~' as str,
16 (Length(myvar) - length(replace(myvar,'^~','')))/2 + 2 as n
o_of_elements
17 from dual) ilv,
18 TABLE(
19 CAST(
20 MULTISET(
21 SELECT ROWNUM FROM dual CONNECT BY ROWNUM < ilv.n
o_of_elements
22 ) AS number_ntt )) nt
23 );
24* end;
25 /
PL/SQL procedure successfully completed.
SQL> select count(*) from t;
COUNT(*)
----------
172
That being said, that's not how I'd parse a delimited string, particularly in PL/SQL. But it does the job.
OK, well this skirts close to the edges of your implementation bias, although remember that forall IS a bulk-binding operation, not a real loop, but have you looked at the dbms_utility.comma_to_table function?
It is an optimized internal oracle parsing function, although with some limitations as you can read about here: http://www.techiegyan.com/2009/02/17/oracle-breaking-comma-separated-string-using-dbms_utilitycomma_to_table/
You would need to replace() to make it comma-delimited, and also double-quotes-enclose if you have parsed fields that starts with numbers, special characters, contains commas, etc
But if your data will allow - it sure makes your code look cleaner (and will likely work much faster too)
declare
myvar varchar2(32000) := 'tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3';
mycnt binary_integer;
myresults sys.dbms_utility.lname_array;
begin
sys.dbms_utility.comma_to_table('"'||replace(myvar,'^~','","')||'"', mycnt, myresults );
delete from t;
forall ix in myresults.first..myresults.last
insert into tvalues (myresults(ix));
commit;
end;

Resources