Difference of two collections - plsql

I'm trying to get a difference of two collections however every time getting below exception. Kindly help. My sample code is also available below. Not sure what's going wrong here... can MULTISET EXCEPT not be applied on RECORD types?
PLS-00306: wrong number or types of arguments in call to
'MULTISET_EXCEPT_ALL'
DECLARE
TYPE FRND_OBJ IS RECORD
(
FrndID NUMBER,
JOBID NUMBER,
REGIONID NUMBER,
COLLEGEID NUMBER
);
TYPE FRND_LIST IS TABLE OF FRND_OBJ;
CURSOR c_date_rolling
IS
SELECT 1,
2,
3,
4
FROM DUAL;
lv_roll_date FRND_LIST;
lv_roll_date_full FRND_LIST;
lv_roll_date_delta FRND_LIST;
g_error_msg VARCHAR2 (1500);
BEGIN
SELECT 5,
6,
7,
8
BULK COLLECT INTO lv_roll_date_full
FROM DUAL;
OPEN c_date_rolling;
FETCH c_date_rolling BULK COLLECT INTO lv_roll_date;
CLOSE c_date_rolling;
DBMS_OUTPUT.put_line ('Full Count: ' || lv_roll_date_full.COUNT);
DBMS_OUTPUT.put_line ('Rolling Count: ' || lv_roll_date.COUNT);
lv_roll_date_delta := lv_roll_date_full MULTISET EXCEPT lv_roll_date;
DBMS_OUTPUT.put_line (lv_roll_date_delta.COUNT);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
g_error_msg :=
SUBSTR (SQLERRM, 1, 200)
|| SUBSTR (DBMS_UTILITY.FORMAT_ERROR_BACKTRACE, 20);
DBMS_OUTPUT.PUT_LINE (g_error_msg);
WHEN OTHERS
THEN
g_error_msg :=
SUBSTR (SQLERRM, 1, 200)
|| SUBSTR (DBMS_UTILITY.FORMAT_ERROR_BACKTRACE, 20);
DBMS_OUTPUT.PUT_LINE (g_error_msg);
END;
/

Here is explained, that you will need a MAP or ORDER method. In your sample:
CREATE OR REPLACE TYPE frnd_obj
AUTHID DEFINER AS OBJECT
(
frndid NUMBER,
jobid NUMBER,
regionid NUMBER,
collegeid NUMBER,
MAP MEMBER FUNCTION tostring
RETURN VARCHAR2
)
/
SHOW ERRORS;
CREATE OR REPLACE TYPE BODY frnd_obj AS
MAP MEMBER FUNCTION tostring
RETURN VARCHAR2 IS
BEGIN
RETURN TO_CHAR (frndid) || ';' || TO_CHAR (jobid) || ';' || TO_CHAR (regionid) || ';' || TO_CHAR (collegeid);
END tostring;
END;
/
CREATE TYPE FRND_LIST IS TABLE OF FRND_OBJ;
And now you can use it:
DECLARE
-- TYPE FRND_OBJ IS RECORD
-- (
-- FrndID NUMBER,
-- JOBID NUMBER,
-- REGIONID NUMBER,
-- COLLEGEID NUMBER
-- );
-- TYPE FRND_LIST IS TABLE OF FRND_OBJ;
CURSOR c_date_rolling
IS
SELECT FRND_OBJ(1,
2,
3,
4)
FROM DUAL;
-- union all
-- SELECT FRND_OBJ(5,
-- 6,
-- 7,
-- 8)
-- FROM DUAL;
lv_roll_date FRND_LIST;
lv_roll_date_full FRND_LIST;
lv_roll_date_delta FRND_LIST;
g_error_msg VARCHAR2 (1500);
BEGIN
SELECT FRND_OBJ(5,
6,
7,
8)
BULK COLLECT INTO lv_roll_date_full
FROM DUAL;
OPEN c_date_rolling;
FETCH c_date_rolling BULK COLLECT INTO lv_roll_date;
CLOSE c_date_rolling;
DBMS_OUTPUT.put_line ('Full Count: ' || lv_roll_date_full.COUNT);
DBMS_OUTPUT.put_line ('Rolling Count: ' || lv_roll_date.COUNT);
lv_roll_date_delta := lv_roll_date_full MULTISET EXCEPT ALL lv_roll_date;
DBMS_OUTPUT.put_line (lv_roll_date_delta.COUNT);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
g_error_msg :=
SUBSTR (SQLERRM, 1, 200)
|| SUBSTR (DBMS_UTILITY.FORMAT_ERROR_BACKTRACE, 20);
DBMS_OUTPUT.PUT_LINE (g_error_msg);
WHEN OTHERS
THEN
g_error_msg :=
SUBSTR (SQLERRM, 1, 200)
|| SUBSTR (DBMS_UTILITY.FORMAT_ERROR_BACKTRACE, 20);
DBMS_OUTPUT.PUT_LINE (g_error_msg);
END;
/

Related

invalid refernce to variable c sys refcursor

DECLARE
c SYS_REFCURSOR;
lv_emp emp%ROWTYPE;
BEGIN
OPEN c FOR SELECT employee_id FROM emp;
--fetch c into lv_emp;
DBMS_OUTPUT.put_line (c.emloyee_id);
END;
ERROR at line 7:
ORA-06550: line 7, column 24:
PLS-00487: Invalid reference to variable 'C'
ORA-06550: line 7, column 1:
Statement ignored
If the objective is to list all the employee_id, you could do with REFCURSOR as cited below in an anonymous block.
VARIABLE c REFCURSOR
DECLARE
BEGIN
OPEN :c FOR SELECT empno FROM scott.emp;
END;
/
PRINT c;
SYS_REFCURSOR in an anonymous block
DECLARE
c_cursor SYS_REFCURSOR;
l_row scott.emp%ROWTYPE;
BEGIN
OPEN c_cursor FOR SELECT * FROM scott.emp;
LOOP
FETCH c_cursor INTO l_row;
EXIT WHEN c_cursor%NOTFOUND;
DBMS_OUTPUT.put_line (l_row.empno || ' : ' || l_row.ename);
END LOOP;
CLOSE c_cursor;
END;
/
Using SYS_REFCURSOR in a Procedure
Procedure Creation
CREATE OR REPLACE PROCEDURE test_proc_cursor (c OUT SYS_REFCURSOR)
AS
BEGIN
OPEN c FOR SELECT empno FROM scott.emp;
END test_proc_cursor;
/
Execution of the above procedure
DECLARE
cur SYS_REFCURSOR;
p_empno VARCHAR2 (50);
BEGIN
test_proc_cursor (c => cur);
LOOP
FETCH cur INTO p_empno;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.put_line ('empno -->' || p_empno);
END LOOP;
CLOSE cur;
END;
/

error in executing ODCIIndexStart() routine

I have a SEARCH package with several functions within that each return a pipelined table.
I am getting the following error:
ORA-29902: error in executing ODCIIndexStart() routine
ORA-06512: at "<SCHEMA_NAME>.SEARCH", line 955
ORA-06512: at "<SCHEMA_NAME>.SEARCH", line 216
ORA-06512: at line 1
ORA-06512: at "<SCHEMA_NAME>.SEARCH", line 1143
ORA-06512: at line 1
29902. 00000 - "error in executing ODCIIndexStart() routine"
*Cause: The execution of ODCIIndexStart routine caused an error.
*Action: Examine the error messages produced by the indextype code and
take appropriate action.
The command that I use to get this error is:
select * from table(search.user_search(10000003, '', 1, 20, 5, '', '', ''));
Here is the code around line 1143 of the SEARCH body:
FUNCTION USER_SEARCH(p_user_id number, p_search varchar2, p_page number, p_num_results number, p_concept_type_id number := null, p_standard_org varchar2 := null, p_standard_title varchar2 := null, p_standard_number varchar2 := null) RETURN SEARCH_RESULTS_TABLE AS
v_sub char(1);
v_rtn search_results_table := search_results_table();
v_user_id number;
v_page number;
v_num_results number;
v_query varchar2(4000);
cursor c_concept is
select * from table(search.concept_new(v_user_id, v_query, v_page, v_num_results, p_concept_type_id, p_standard_org, p_standard_title, p_standard_number));
BEGIN
v_page := p_page;
v_num_results := p_num_results;
v_query := p_search;
if (v_query = '') then
v_query := null;
end if;
-- ONLY DURING DEVELOPMENT
v_user_id := p_user_id;
if v_user_id = 0 then
v_user_id := 2;
end if;
if v_page = null then
v_page := 1;
end if;
if v_num_results = null then
v_num_results := -1;
end if;
select security.has_permission(p_user_id, 'UNLIMITED_RESULTS') into v_sub from dual;
case v_sub
when 'N' then
v_rtn := search.term(v_user_id, p_search, 1, 20, P_concept_type_id, p_standard_org, p_standard_title, p_standard_number);
when 'Y' then
for r_concept in c_concept loop -- line 1143
v_rtn.extend;
v_rtn(v_rtn.last) := search_results_row(r_concept.SCORE, r_concept.FROM_TABLE, r_concept.ID, r_concept.IN_DICTIONARY);
end loop;
end case;
return v_rtn;
END USER_SEARCH;
The code around line 216:
FUNCTION CONCEPT_NEW(p_user_id number, p_search varchar2, p_page number := 1, p_num_results number := -1, p_concept_type_id number := null, p_standard_org varchar2 := null, p_standard_title varchar2 := null, p_standard_number varchar2 := null) RETURN SEARCH_RESULTS_TABLE PIPELINED AS
out_rec search_results_row := search_results_row(null,null,null,null);
v_num_pages number;
v_num_results number;
cursor c_count is
select count(*) cnt, ceil(count(*) / p_num_results) num_pages from (
select * from (
select rownum rn, bb.* from (
select * from (
select sum(score) score, concept_id from
(
(
select
sum(score) score,
concept_id
from
(
select
s.score + (case
when c.eotd = 'N' then
200
else
0
end) score,
ct.concept_id
from
table(search.synonym_search(p_search)) s,
term_synonym ts,
concept_term ct,
concept c
where
c.eotd = 'N' and
s.table_id = ts.synonym_id and
ts.term_id = ct.term_id and
c.concept_id = ct.concept_id
)
group by
concept_id
)
union all
(
select
sum(score) score,
concept_id
from
(
select
s.score + (case
when c.eotd = 'N' then
200
else
0
end) score,
cd.concept_id
from
table(search.definition_search(p_search)) s,
concept_definition cd,
concept c
where
c.eotd = 'N' and
s.table_id = cd.definition_id and
c.concept_id = cd.concept_id
)
group by
concept_id
)
)
group by concept_id
) order by score desc
) bb) where score > 100) where rn < 1000;
-- another cursor here called c_search (removed for cleanliness)
v_added boolean;
v_in_dict varchar(1);
v_concept_id number;
cursor c_in_dict is
select * from dictionary_concept where concept_id = v_concept_id and dictionary_id in (select dictionary_id from company_dictionary where client_company_id in (select client_company_id from users where users_id = p_user_id));
v_in_dict_result c_in_dict%rowtype;
BEGIN
open c_count;
fetch c_count into v_num_results, v_num_pages; -- line 216
close c_count;
out_rec.SCORE := v_num_results;
out_rec.FROM_TABLE := 'HEADER';
out_rec.ID := v_num_pages;
out_rec.IN_DICTIONARY := 'N';
pipe row(out_rec);
-- more code here but been removed
RETURN;
END CONCEPT_NEW;
The code around line 995:
FUNCTION STANDARD_SEARCH(p_search varchar2) RETURN RESULT_TABLE
PIPELINED
AS
out_rec result_type := result_type(null, null);
cursor c_search is
select
45 score,
source_id
from
source
where
source_title = p_search or
standard_number = p_search
union all
select
40,
source_id
from
source
where
lower(source_title) = lower(p_search) or
lower(standard_number) = lower(p_search)
union all
select
35,
source_id
from
source
where
lower(source_title) like lower(p_search) || '%' or
lower(standard_number) like lower(p_search) || '%'
union all
select
30,
source_id
from
source
where
lower(source_title) like '%' || lower(p_search) || '%' or
lower(standard_number) like '%' || lower(p_search) || '%'
union all
select
10 * contains(source_title, lower(p_search)),
source_id
from
source
where
contains(source_title, lower(p_search)) > 1
union all
select
10 * contains(standard_number, lower(p_search)),
source_id
from
source
where
contains(standard_number, lower(p_search)) > 1;
BEGIN
for r_search in c_search loop -- line 995
out_rec.SCORE := r_search.SCORE;
out_rec.TABLE_ID := r_search.SOURCE_ID;
pipe row(out_rec);
end loop;
return;
END;
Change = null to is null. Zero-length strings in Oracle are already NULL so = '' is also incorrect.
Those are some obvious bugs but the code is so complex I'm not sure if they are directly related to the errors. You may need to shrink your code down until it can fit inside a small, reproducible test case. That can take hours of work sometimes.
Another suggestion is to rethink whether you need to use pipelined functions. In my experience pipelined functions are buggy and over-used. Sometimes they can be replaced by regular SQL statements. Sometimes they can be replaced by a simpler function that returns a complete collection, instead of returning it one row at a time. Unless the collection is so ginormous that it can't reasonably fit in memory, or you need the function to return the first N results so they can be processed immediately, the pipelined feature doesn't help.

PLSQL invalid number error after fetch

sorry if this is an confusing post and the style is not proper but this is my first question but im having an error after i want to fetch c_latlong :
the things he will fetch are:
LAT value : 50,9459744669855
LON value : 5,9704909091251
ORA-01722: invalid number
ORA-06512: at "DOMINOS.GETLATLONG", line 14
ORA-06512: at "DOMINOS.ZOEKWINKELVOORADRES2", line 34
ORA-06512: at line 9
This is the code i use:
create or replace FUNCTION getLatLong(v_postcode varchar2, v_huisnummer varchar2)
RETURN varchar2
IS
v_lat NVARCHAR2(38) ;
v_long NVARCHAR2(38);
v_postcode_id varchar2(200);
v_all varchar2(200);
CURSOR c_latlong IS
SELECT LAT, LON
FROM POSTCODE
WHERE POSTCODE = v_postcode AND v_huisnummer BETWEEN MINNUMBER AND MAXNUMBER;
BEGIN
OPEN c_latlong;
FETCH c_latlong INTO v_lat, v_long;
v_all := v_lat || '-' || v_long;
-- print(v_all);
RETURN v_all;
CLOSE c_latlong;
END getLatLong;
create or replace FUNCTION zoekWinkelVoorAdres2(v_postcode varchar2, v_huisnummer varchar2)
RETURN varchar2
IS
v_array_this apex_application_global.vc_arr2 := apex_util.string_to_table(getLatLong(v_postcode,v_huisnummer), '-');
v_temp_postcode varchar2(200);
v_temp_huisnummer varchar2(200);
v_temp_id varchar2(200);
v_temp_winkel apex_application_global.vc_arr2;
v_array dbms_sql.varchar2_table;
v_max number;
v_closest_winkel varchar2(200);
v_closest_dis varchar2(200);
CURSOR c_all_stores_max IS
SELECT count(*)
FROM WINKEL;
CURSOR c_all_stores IS
SELECT ID, POSTCODE, HUISNR
FROM WINKEL;
CURSOR c_select_dis IS
SELECT WINKEL_ID, DISTANCE
FROM allwinkeldis ORDER BY DISTANCE DESC;
BEGIN
--DELETE FROM allwinkeldis;
OPEN c_all_stores_max;
FETCH c_all_stores_max INTO v_max;
CLOSE c_all_stores_max;
OPEN c_all_stores;
FOR i IN 1..v_max
LOOP
FETCH c_all_stores INTO v_temp_id, v_temp_postcode, v_temp_huisnummer;
-- print('-------------------1---------------------' ||v_temp_postcode);
print(getLatLong(v_temp_postcode,v_temp_huisnummer));
v_temp_winkel :=apex_util.string_to_table(getLatLong(v_temp_postcode,v_temp_huisnummer), '-');
-- print(v_temp_id || ' ' || v_temp_winkel(1) || ' ' || v_temp_winkel(2));
-- print('-------------------2---------------------');
--v_array(i) := v_temp_id ||','||distance(v_array_this(2), v_array_this(3),v_temp_winkel(2), v_temp_winkel(3));
INSERT INTO allwinkeldis(winkel_id, distance) VALUES (v_temp_id, distance(v_array_this(1), v_array_this(2), v_temp_winkel(1), v_temp_winkel(2)));
--print(v_temp_id ||' -------------- '||distance(v_array_this(2), v_array_this(3), v_temp_winkel(2),v_temp_winkel(3)));
END LOOP;
CLOSE c_all_stores;
OPEN c_select_dis;
FETCH c_select_dis INTO v_closest_winkel, v_closest_dis;
CLOSE c_select_dis;
print(v_closest_winkel || '--------------' || v_closest_dis);
END zoekWinkelVoorAdres2;
I'm not sure what is the data type of LAT and LON in your table POSTCODE.
But whatever variables you define to fetch data into, they must match these types.
For examples, assuming that LAT and LON are of the type NVARCHAR2, then I would rewrite your declaration section as follows:
CREATE OR REPLACE FUNCTION getLatLong(v_postcode varchar2, v_huisnummer varchar2)
RETURN varchar2
IS
v_postcode_id varchar2(200);
v_all varchar2(200);
CURSOR c_latlong IS
SELECT LAT, LON
FROM POSTCODE
WHERE POSTCODE = v_postcode AND v_huisnummer BETWEEN MINNUMBER AND MAXNUMBER;
TYPE temp_nvarchar2 IS TABLE OF NVARCHAR2(38);
v_lat temp_nvarchar2;
v_long temp_nvarchar2;
BEGIN
....
The invalid number error occurs when it expects a number and gets something else instead, like a varchar.
v_lat and v_lon are declared as NVARCHAR2. Is this the same datatype that is stored in POSTCODE? If not, this is the cause of the error.
It might also be having trouble with the line v_all := v_lat || '-' || v_long;. If v_lat or v_long are numbers, then you need to cast them to chars instead, like so:
v_all := TO_CHAR(v_lat) || '-' || TO_CHAR(v_long);

I'm using a collection to compare with i/p parameter ,but the last index of the collection is not getting compared

create or replace procedure save_pod_place_tag1 ( p_START_DATE in varchar2,
p_END_DATE in varchar2,
p_USER_ID in number,
p_TAG_ID in out number,
p_status out varchar2,
p_status_dtl out varchar2) is
v_rec_cnt number;
v_ID Number;
v_CNT Number;
type l_START_DATE is table of varchar2(100);
type l_END_DATE is table of varchar2(100);
v_st_dt l_START_DATE := l_START_DATE();
v_ed_dt l_END_DATE := l_END_DATE();
begin
v_ID := pod_unique_val_seq.nextval;
select to_char(start_date,'dd-mon-yyyy'),to_char(end_date,'dd-mon-yyyy')
bulk collect into v_st_dt,v_ed_dt
from pod_place_tag_tb;
for i in v_st_dt.first .. v_st_dt.count loop
if nvl(v_st_dt(i),chr(0)) < nvl(p_START_DATE,chr(0)) or nvl(v_st_dt.last,chr(0)) < nvl
(p_START_DATE,chr(0)) then
dbms_output.put_line('START_DATES are' ||' ' ||v_st_dt(i));
dbms_output.put_line('INSERTION IS ALLOWED');
else
dbms_output.put_line('INSERTION NOT ALLOWED');
end if;
end loop;
The last start _date is not getting compared and I get the o/p insertion not possible even i am passing proper p_START_DATE
I think this in roots is wrong... you cant get compare on varchar2 type expecting result as date, because varchar2 orders in other way.
example
select to_char(sysdate+level,'dd-mon-yyyy') dates from dual connect by level<=100 order by dates;
Result
01-aug-2014
01-jul-2014
01-jun-2014
01-sep-2014
02-aug-2014
02-jul-2014
02-jun-2014
03-aug-2014
03-jul-2014
03-jun-2014
04-aug-2014
04-jul-2014
04-jun-2014
...
select trunc(sysdate)+level dates from dual connect by level<=100 order by dates ASC
result
2014/05/25/
2014/05/26/
2014/05/27/
2014/05/28/
...
check anonimos block
declare
v_date1 date;
v_date2 date;
v_check_date boolean;
v_char_date1 varchar2(200);
v_char_date2 varchar2(200);
v_check_char_date boolean;
v_start_date date:=to_date('01.01.2014','dd.mm.yyyy');
v_count pls_integer:=0;
begin
for i IN 1 .. 300
LOOP
v_char_date1:=to_char(v_start_date+i,'dd-mon-yyyy');
v_date1 :=v_start_date+i;
for i1 IN 1..300
LOOP
v_char_date2:=to_char(v_start_date+i1,'dd-mon-yyyy');
v_date2 :=v_start_date+i1;
IF v_date1<v_date2 THEN
v_check_date:=true;
ELSIF v_date1>v_date2 THEN
v_check_date:=false;
END IF;
IF v_char_date1<v_char_date2 THEN
v_check_char_date:=true;
ELSIF v_char_date1>v_char_date2 THEN
v_check_char_date:=false;
END IF;
IF (v_check_char_date AND not v_check_date) THEN
dbms_output.put_line ('v_date1='||v_date1||' > '||'v_date2='||v_date2||' AND '||'v_char_date1='||v_char_date1||' < '||'v_char_date2='||v_char_date2);
exit;
ELSIF (not v_check_char_date AND v_check_date) THEN
dbms_output.put_line ('v_date1='||v_date1||' < '||'v_date2='||v_date2||' AND '||'v_char_date1='||v_char_date1||' > '||'v_char_date2='||v_char_date2);
exit;
END IF;
v_count:=v_count+1;
END LOOP;
IF (v_check_char_date AND not v_check_date) OR (not v_check_char_date AND v_check_date) THEN
exit;
END IF;
END LOOP;
dbms_output.put_line ('count='||v_count);
end;

PLSQL, execure formula stored in a table

I'm a newbie in PLSQL. I was just wondering if I can save my formula into a table as string and use it in my functions to calculate some values.
Here is an example:
ID NAME FORMULA
1 test prm_testval*prm_percent/18
2 test2 (prm_testval +20)*prm_percent
what I want to do is to select formula column from the table and use the string in my functions
select t.* from table t where id=1
prm_calculated_value = t.formula
I don't want the string value of formula in here, just the formula itself.
Any ideas, If I can use it or not?
The starting point is execute immediate-statement. It's PL/SQL's eval().
create table formulas (
id number,
name varchar2(20),
formula varchar2(50)
);
insert into formulas values (1, 'test 1', 'prm_testval*prm_percent/18');
insert into formulas values (2, 'test 2', '(prm_testval +20)*prm_percent');
/* eval from: http://www.adp-gmbh.ch/blog/2005/may/5.html */
create or replace function eval (
expr in varchar2
) return varchar2 as
ret varchar2(32767);
begin
execute immediate 'begin :result := ' || expr || '; end;' using out ret;
return ret;
end;
/
create or replace function eval2 (
vars in varchar2,
expr in varchar2
) return varchar2 as
ret varchar2(32767);
begin
execute immediate vars || ' begin :result := ' || expr || '; end;' using out ret;
return ret;
end;
/
create or replace function calc_prm (
id_ in number,
prm_testval in varchar2,
prm_percent in varchar2
) return number as
formula_ formulas.formula%type;
vars constant varchar2(32767) :=
'declare prm_testval constant number := to_number(' || prm_testval ||
'); prm_percent constant number := to_number(' || prm_percent || ');';
begin
select formula into formula_ from formulas where id = id_;
return eval2(vars, formula_);
end;
/
/* call from SQL */
select eval('3*4') from dual;
select calc_prm(1, 97, 10) from dual;
select calc_prm(2, 97, 10) from dual;
/* call from PL/SQL block */
begin
dbms_output.put_line(eval('3*4'));
dbms_output.put_line(calc_prm(1, 97, 10));
dbms_output.put_line(calc_prm(2, 97, 10));
end;
/
Based on this example you can start building your own way to map symbol values (prm_testval and prm_percent) to real values. Next you might want to have a look into DBMS_SQL.
Beware SQL injection when using client supplied data ! See also e.g. Bobby Tables: A guide to preventing SQL injection.

Resources