PL SQL fuzzy search with clause - oracle

I am working on call center customer search query. If user enter last name and driving license I have to find out the most close matching customer(s) from database. I am using fuzzy search, soundex() to compare customer last name. Base on different conditions I have to keep using soundex function, it is effecting performance. I try to use With Clause but since I have many IF and ELSE parts it is giving table or view not found exception.
I tried to google to find how to use with the clause and with clause with function, none of them are helpful.
PROCEDURE RetrieveCustomer
(
i_lastname IN varchar,
i_DL IN varchar,
o_cursor OUT T_CURSOR
)
IS
v_DLAndExactLastNameCnt Number;
v_DLAndLastNameCnt Number;
v_DLCnt Number;
BEGIN
SELECT COUNT(*) INTO v_DLAndExactLastNameCnt
FROM t1, t2
WHERE t1.DL = i_DL AND (upper(t2.last_name) like upper(i_lastname));
IF(v_DLAndExactLastNameCnt > 0) THEN
OPEN o_cursor FOR
select
a,
b,
'DL + ELN' as search_result_baseon --exact last name match
from t1, t2, t3, t4
where t1.DL = i_DL AND (upper(t2.last_name) like upper(i_lastname));
ELSE
SELECT COUNT(*) INTO v_DLAndLastNameCnt
FROM t1, t2
WHERE t1.DL= i_DL
AND p.last_name in (select last_name from t2 where soundex(last_name) = soundex(i_lastname));
IF(v_DLAndLastNameCnt > 0) THEN
OPEN o_cursor FOR
select
a,
b,
'DL + LN' as search_result_baseon -- last name not exact match
from t1, t2, t3, t4
where
t1.DL= i_DL
AND p.last_name in (
select last_name from t2 where soundex(last_name) = soundex(i_lastname)
);
ELSE
SELECT COUNT(*) INTO v_DLCnt
FROM t1
WHERE t1.DL = i_DL;
IF(v_DLCnt > 0) THEN
OPEN o_cursor FOR
select a
b,
'DL' as search_result_baseon
from t1, t2, t3, t4
where t1.DL = i_DL;
ELSE
OPEN o_cursor FOR
select
a,
b,
'LN' as search_result_baseon
from t1, t2, t3, t4
where t2.last_name IN (
select last_name from t2 where soundex(last_name) = soundex(i_lastname)
)
END IF;
END IF;
END IF;
END RetrieveCustomer;
Is there any way I can store matching last name record in some in-memory table and use it as and when require in that procedure? Can anyone of you please suggest me better way to write my procedure:

Related

add values from other table using cursor in oracle

I am trying to execute the following steps:
Let's say we have a table t1
CREATE TABLE t1 (i1 varchar2,d1 varchar2,l1 varchar2, h1 number(1),q1 number, s1 date );
insert into t1 values('123','1','123-1',0,0,sysdate);
insert into t1 values('123','1','234-1',0,0,sysdate+2);
insert into t1 values('456','2','345-1',0,0,sysdate);
insert into t1 values('456','2','456-1',0,0,sysdate+2);
CREATE TABLE t2 (i2 varchar2,d2 varchar2,q2 number,a2 date );
insert into t2 values('123','1','1230',sysdate);
insert into t2 values('123','1','2340',sysdate+1);
insert into t2 values('456','2','3450',sysdate);
insert into t2 values('456','2','4560',sysdate+1);
Now I have to order t1 data in ascending order of s1 for same i1 and d1 and then check with a2 column of t2. If a2>=s1 but a2< next greater value of s1 then add q2 and assign it to q1.
I am trying to write a cursor which will take values from t1 in ascending order of s1,
for t2.a2>=s1 and t2.a2<s2 --s2 is next greater date value of t1.s1 column
q1 = q1+q2;
I am unable to find an efficient way to do this.
my cursor is:
cursor c1 is (select * from(select i1,d1,s1,min(s1) from t1 where h1=0
group by i1,d1,s1 order by i1,d1,s1));
declare
v_s
v_i
v_d
v_s1
v1 date;
v_q number;
begin
open c1;
fetch c1 into v_i,v_d,v_s,v1;
loop
select s1 into v_s1
from t1 t
where t.i1 = v_i and t.d1=v_d and t.s1>v_s;
select sum(t2.q2) into v_q
from t2 t
where t2.i1=v_i and t2.d2=v_d and t2.a2>=v_s and t2.a2<v_s1;
update t1 set q1 = v_q;
end loop;
close c1;
end;
I want q1 to be populated with sum(q2) for all a2 dates if a2 falls between min and max(s1).
I am unable to execute this. I either keep getting no data found or too many rows fetched or some other error. How can I do this efficiently?
Since you have not shared the end result, I think you want rows 2 & 4 to be null for column Q1. You can try the below co-related sub query to achieve the desired result -
UPDATE t1
SET Q1 = (SELECT SUM(q2)
FROM (SELECT t2.*, a2 - lag(a2) over(partition by i2, d2 order by a2) date_diff
FROM t2) t2
WHERE t1.i1 = t2.i2
AND t1.d1 = t2.d2
AND t1.s1 <= t2.a2
AND NVL(date_diff, 1) = 1);
Demo.

Is there any faster way to perform merge on 120k records weekly in oracle pl/sql?

There are around 120k records in the database, and based on a few functions I calculate scores for all the records, weekly I have to update the table with new records and respective scores.
Below is a procedure that I am using to merge data into the table:
create or replace procedure scorecalc
AS
score1 number;
score2 number;
score3 number;
CURSOR cur IS
SELECT Id_number from tableA;
r_num cur%ROWTYPE;
BEGIN
--OPEN cur;
FOR r_num IN cur
LOOP
select functionA(r_num.id_number),functionb(r_num.id_number),functionc(r_num.id_number) into score1, score2,score3 from dual;
Merge into scores A USING
(Select
r_num.id_number as ID, score1 as scorea, score2 as scoreb, score3 as scorec, TO_DATE(sysdate, 'DD/MM/YYYY') as scoredate
FROM DUAL) B
ON ( A.ID = B.ID and A.scoredate = B.scoredate)
WHEN NOT MATCHED THEN
INSERT (
ID, scorea, scoreb, scorec, scoredate)
VALUES (
B.ID, B.scorea, B.scoreb, B.scorec,B.scoredate)
WHEN MATCHED THEN
UPDATE SET
A.scorea = B.scorea,
A.scoreb = B.scoreb,
A.scorec = B.scorec;
COMMIT;
END LOOP;
END;
whereas functionA/ B/ C has complex queries, joins in it to calculate the score.
Please suggest me any way to improve the performance because currently with this snippet of code I am only able to insert some 2k records in 1 hour? Can I use parallel DML here?
Thank you!
Why are you doing this in a procedure? This could all be done via DML:
MERGE INTO scores a USING
(SELECT ta.id_number AS ID,
functionA(ta.id) AS scoreA,
functionB(ta.id) AS scoreB,
functionC(ta.id) AS scoreC,
TO_DATE(sysdate, 'DD/MM/YYYY') as scoredate
FROM tableA ta) b
ON (a.id = b.id AND a.scoredate = b.scoredate)
WHEN MATCHED THEN UPDATE SET
a.scorea = b.scorea,
a.scoreb = b.scoreb,
a.scorec = b.scorec
WHEN NOT MATCHED THEN INSERT (ID, scorea, scoreb, scorec, scoredate)
VALUES (B.ID, B.scorea, B.scoreb, B.scorec,B.scoredate);
If you want to try using PARALLEL hint after that, feel free. But you should definitely get rid of that cursor and stop doing "Slow-by-slow" processing.
Something I've had some success with has been inserting from a select statement. It is pretty performant as it doesn't involve row by row inserting.
In your case, I'm thinking it would be something like:
INSERT INTO table (ID, scorea, scoreb, scorec, scoredate)
SELECT functionA(id_number), functionB(id_number), functionC(id_number)
FROM tableA
An example of this can be found at the link below:
https://docs.oracle.com/cd/B12037_01/appdev.101/b10807/13_elems025.htm
To schedule it just put the statement #Del in a procedure block;
create or replace procedure Saturday_Night_Merge is
begin
<Put the merge statement here>
end Saturday_Night_Merge;

Oracle delete from tableA where a duplicate row is in tableB

As the title says, I am looking for a way to remove all rows from TableA where there is a matching row in TableB.
the Tables A & B have about 30 columns in them so a WHERE A.col1 = B.col1 etc would be a little problematical. Ideally I was hoping for something like
DELETE FROM tableA WHERE IN TableB
(overly simplified by this type of thing)
IN clause can compare all columns returned from select
DELETE FROM tableA WHERE ( col1,col2,col3,.. ) IN ( select col1,col2,col3... FROM TableB );
The brute force way to establish if two records from each table are the same is to just compare every column:
DELETE
FROM tableA a
WHERE EXISTS (SELECT 1 FROM tableB b WHERE a.col1 = b.col1 AND a.col2 = b.col2 AND ...
a.col30 = b.col30);
You could create function which checks structures of tables and, if they are the same, creates string containing correct conditions to compare.
For example here are two tables:
create table t1 (id, name, age) as (
select 1, 'Tom', 67 from dual union all
select 2, 'Tia', 42 from dual union all
select 3, 'Bob', 16 from dual );
create table t2 (id, name, age) as (
select 1, 'Tom', 51 from dual union all
select 3, 'Bob', 16 from dual );
Now use function:
select generate_condition('T1', 'T2') from dual;
result:
T1.ID = T2.ID and T1.NAME = T2.NAME and T1.AGE = T2.AGE
Copy this, paste and run delete query:
delete from t1 where exists (select 1 from t2 where <<PASTE_HERE>>)
Here is the function, adjust it if needed. I used user_tab_columns so if tables are on different schemas you need all_tab_columns and compare owners too. If you have Oracle 11g you can replace loop with listagg(). Second table has to contain all columns of first table and they have to be same type and length.
create or replace function generate_condition(i_t1 in varchar2, i_t2 in varchar2)
return varchar2 is
v varchar2(1000) := '';
begin
for rec in (select column_name, u2.column_id
from user_tab_cols u1
left join (select * from user_tab_cols where table_name = i_t2) u2
using (column_name, data_type, data_length)
where u1.table_name = i_t1 order by u1.column_id)
loop
if rec.column_id is null then
v := 'ERR: incompatible structures';
goto end_loop;
end if;
v := v||' and '||i_t1||'.'||rec.column_name
||' = '||i_t2||'.'||rec.column_name;
end loop;
<< end_loop >>
return(ltrim(v, ' and '));
end;
If you want to avoid running process manually you need dynamic PL/SQL.
create table tableA (a NUMBER, b VARCHAR2(5), c INTEGER);
create table tableB (a NUMBER, b VARCHAR2(5), c INTEGER);
As you said
WHERE A.col1 = B.col1 etc would be a little problematical
you could intersect the tables and mention all columns from tableA one time, like this:
delete tableA
where (a,b,c) in (select * from tableA
intersect
select * from tableB);

Oracle Add procedure using pl/sql

The Procedure is like below!
I'm new to oracle. As i copy the sqlserver procedure to oracle and change some parts. It will be a great thxs help me solve this !
CREATE OR REPLACE PROCEDURE SP_GetAdminResource
(
AdminId IN NVARCHAR2,
p_ResultSet OUT sys_refcursor
)
AS
BEGIN
WITH T AS(
SELECT T1.ResId, T1.UpResId
FROM SYS_Resource T1
WHERE T1.IsActive = 1
AND T1.ResId IN (SELECT DISTINCT T2.ResId
FROM SYS_RoleResource T2
WHERE T2.RoleId IN
(SELECT T3.RoleId
FROM SYS_RoleAdministrator T3, SYS_Role T10
WHERE T10.RoleId = T3.RoleId
AND T10.IsActive = 1
AND T3.AdminId = AdminId))),
TT AS (SELECT *
FROM T
UNION ALL (SELECT T4.ResId, T4.UpResId
FROM SYS_Resource T4, T
WHERE T4.IsActive = 1
AND T4.ResId = T.UpResId)),
SELECT T5.ResId, T5.UpResId, T5.ResIcon,T5.ResName, T5.ResUrl,T5.OrderNum,T8.ActionCode
FROM SYS_Resource T5 INTO p_ResultSet
LEFT JOIN (SELECT T6.ResId, T6.ActionCode
FROM SYS_RoleResource T6
WHERE T6.RoleId IN
(SELECT T7.RoleId
FROM SYS_RoleAdministrator T7, SYS_Role T9
WHERE T9.RoleId = T7.RoleId
AND T9.IsActive = 1
AND T7.AdminId = AdminId)) T8 ON T5.ResId =
T8.ResId
WHERE T5.IsActive = 1
AND T5.ResId IN (SELECT DISTINCT TT.ResId FROM TT)
ORDER BY T5.OrderNum ASC, T5.ResName ASC;
END SP_GetAdminResource;
Check below the working version of your procedure. I have put my comments in between . Please go through it and work accordingly.
CREATE OR REPLACE PROCEDURE SP_GetAdminResource
(
AdminId IN NVARCHAR2,
p_ResultSet OUT sys_refcursor
)
AS
BEGIN
Open p_ResultSet for -- this is the way to use refcursor in your procedure
WITH T AS(
SELECT T1.ResId, T1.UpResId
FROM SYS_Resource T1
WHERE T1.IsActive = 1
AND T1.ResId IN (SELECT DISTINCT T2.ResId
FROM SYS_RoleResource T2
WHERE T2.RoleId IN
(SELECT T3.RoleId
FROM SYS_RoleAdministrator T3, SYS_Role T10
WHERE T10.RoleId = T3.RoleId
AND T10.IsActive = 1
AND T3.AdminId = t2.AdminId))), ----Check which AdminId need to be joined.I joined it with T2
TT AS (SELECT T.ResId, T.UpResId
FROM T
UNION ALL (SELECT T4.ResId, T4.UpResId
FROM SYS_Resource T4, T
WHERE T4.IsActive = 1
AND T4.ResId = T.UpResId) )
-- , ----Extra Need to remove it
SELECT T5.ResId,
T5.UpResId
-- T5.ResIcon, ---Uncomment these columns in your query, I just made tables to test without these columns.
-- T5.ResName,
-- T5.ResUrl,
-- T5.OrderNum,
-- T8.ActionCode
FROM SYS_Resource T5
-- INTO p_ResultSet -------------------------------No need to fetch into sysrefcursor.
LEFT JOIN (SELECT T6.ResId, T6.ActionCode
FROM SYS_RoleResource T6
WHERE T6.RoleId IN
(SELECT T7.RoleId
FROM SYS_RoleAdministrator T7, SYS_Role T9
WHERE T9.RoleId = T7.RoleId
AND T9.IsActive = 1
AND T7.AdminId = t6.AdminId)) T8 ON T5.ResId = ------Check which AdminId need to be joined.I joined it with T6
T8.ResId
WHERE T5.IsActive = 1
AND T5.ResId IN (SELECT DISTINCT TT.ResId FROM TT)
ORDER BY T5.OrderNum ASC, T5.ResName ASC ;
END SP_GetAdminResource;

subquery inside INSERT statement

I just recently found out that subqueries are not allowed in INSERT statements that are inside stored procedures. This is my script:
begin
execute immediate 'truncate table itcustadm.GL_DTPJ_TEST2';
insert into GL_DTPJ_TEST2
(rule_no,
posted_by_user_id,
transaction_id,
transaction_sr_no,
dr_amount,
cr_amount,
tran_crncy_code,
bkdt_tran_flg,
bank_desc
)
select
tq.rule_no,
tq.posted_by_user_id,
tq.transaction_id,
tq.transaction_sr_no,
tq.dr_amount,
tq.cr_amount,
tq.tran_crncy_code,
tq.bkdt_tran_flg,
(select ent.bank_desc from crmuser.end ent where ent.bank_id = gam.bank_id);
But since the (select ent.bank_desc from crmuser.end ent where ent.bank_id = gam.bank_id) at the bottom of the SELECT statement is not allowed by Oracle, what's the best way to accomplish this?
I actually have this code right before the INSERT statement, but I don't know how to exactly use it:
get_bank_desc := '(select ent.bank_desc from crmuser.end ent ' ||
'where ent.bank_id = gam.bank_id)';
I am not sure what you are exactly trying for, but below code may be useful for you, you can achieve inserting a SubQuery output into a table using below query sample, but make sure output of the SubQuery is a single row o/p, so that you can escape from "ORA-01427: single-row SubQuery returns more than one row" ERROR.
insert into test_ins1
values(1,(SELECT COL2 FROM TEST_INS WHERE COL1=1 ));
Even then you can use rownum in where condition and take the single value.
Please let me know in case of any doubts
declare
bank_desc_temp bank_desk_type; /* the type defined in crmuser.ent for bank_desc*/
begin
select ent.bank_desc into bank_desc_temp from crmuser.end ent where ent.bank_id = gam.bank_id;
execute immediate 'truncate table itcustadm.GL_DTPJ_TEST2';
insert into GL_DTPJ_TEST2
(rule_no,
posted_by_user_id,
transaction_id,
transaction_sr_no,
dr_amount,
cr_amount,
tran_crncy_code,
bkdt_tran_flg,
bank_desc
)
select
tq.rule_no,
tq.posted_by_user_id,
tq.transaction_id,
tq.transaction_sr_no,
tq.dr_amount,
tq.cr_amount,
tq.tran_crncy_code,
tq.bkdt_tran_flg,
bank_desc_temp;
end;
When you say "not allowed" what do you mean? Did you get an error?
I ask, because subqueries are definitely allowed inside an insert as select statement, providing you have the syntax correct (and the subquery returns at most one row), e.g.:
create table test_tab (col1 number, col2 varchar2(10));
begin
insert into test_tab
select 1,
(select 'Yes' from dual d2 where d.dummy = d2.dummy)
from dual d;
commit;
end;
/
select * from test_tab;
COL1 COL2
---------- ----------
1 Yes
There are some syntax issues with the code you provided - where is the from clause, and where are the tq and gam aliases defined?
There are two syntax you can use in your insert statement:
(I)
INSERT INTO table_name( column1, column2....columnN)
VALUES ( value1, value2....valueN);
(II)
INSERT INTO table (column1, column2, ... )
SELECT expression1, expression2, ...
FROM source_table(s)
WHERE conditions;
In your example, you should choose the second approach:
insert into GL_DTPJ_TEST2 (rule_no,
posted_by_user_id,
transaction_id,
transaction_sr_no,
dr_amount,
cr_amount,
tran_crncy_code,
bkdt_tran_flg,
bank_desc
)
select tq.rule_no,
tq.posted_by_user_id,
tq.transaction_id,
tq.transaction_sr_no,
tq.dr_amount,
tq.cr_amount,
tq.tran_crncy_code,
tq.bkdt_tran_flg,
ent.bank_desc
from crmuser.gam
join crmuser.end ent
on ent.bank_id = gam.bank_id
;
basically, if you want to add records using an insert statement, you should use a full select statement first. Here is how I would do it:
(1)
select *
from table1;
(2)
select column1
,column2
,column3
from table1;
(3)
select t1.column1
,t1.column2
,t1.column3
,t2.column4
,t2.column5
from table1 t1
join table2 t2
on t2.id = t1.id
;
(4)
insert into table3 (col1
,col2
,col3
,col4
,col5)
select t1.column1
,t1.column2
,t1.column3
,t2.column4
,t2.column5
from table1 t1
join table2 t2
on t2.id = t1.id
;

Resources