Response time of the query is too high in oracle 11g - performance

SELECT /*+first_rows */ PPS_ID,TOTAL_WEIGHT from
(SELECT PPS_ID,TOTAL_WEIGHT ,row_number() over (order by total_weight desc) row_num
FROM (SELECT pps_id,round((((60 * name_pct_match / 100) + prs_weight + year_weight + dt_weight +
case
when mother_name_pct_match = -1
then
0
else
(10 * mother_name_pct_match / 100)
end)/decode(mother_name_pct_match,-1,(total_attrib_weight - mother_weight),total_attrib_weight)) * 100) total_weight
FROM (SELECT pps_id,
round(func_compare_name('MUHAMMAD YASIN MUHAMMAD ASHRAF',upper(name_en),' ',60)) name_pct_match,
decode(prs_nationality_id, 271, 15, 0) prs_weight,
case
when upper(mother_name_en) in ('MR','MRS','MISS','NISA','M','X')
then -1
else
round(func_compare_name(upper('.'), upper(mother_name_en), ' ',60))
end mother_name_pct_match, 10 mother_weight,
100 total_attrib_weight,
case when to_number(to_char(birth_date, 'yyyy')) = 2007 then 5 else 0 end year_weight,
case when to_char(to_date('01/01/2007','DD-MM-RRRR'), 'dd') = to_char(birth_date, 'dd')
and to_char(to_date('01/01/07','DD-MM-RRRR'), 'mm') = to_char(birth_date, 'mm') then 10
when to_date('01/01/2007','DD-MM-RRRR') between birth_date-6 and birth_date+6 then 8
when to_date('01/01/2007','DD-MM-RRRR') between birth_date-28 and birth_date+28 then 5
when to_date('01/01/2007','DD-MM-RRRR') between birth_date-90 and birth_date+90 then 3
else 0
end dt_weight
FROM INDIV_PROF_sub_test123
WHERE birth_date = '01/01/2007'
AND IS_ACTIVE = 1
AND gender_id = 1
AND round(func_compare_name('MUHAMMAD YASIN MUHAMMAD ASHRAF',upper(name_en),' ',60)) > 20
)
)
WHERE TOTAL_WEIGHT >= 70
)
where row_num <= 10
CREATE OR REPLACE FUNCTION VISION_APP.func_compare_name(p_name_str IN VARCHAR2,
p_str IN VARCHAR2,
p_delim IN VARCHAR2,
p_relev_thresh IN NUMBER)
RETURN NUMBER DETERMINISTIC AS
l_str LONG;
l_n NUMBER;
TYPE mytabletype IS TABLE OF VARCHAR2(255);
l_name_data mytabletype := mytabletype();
l_data mytabletype := mytabletype();
v_name_cnt NUMBER := 0;
v_pct_per_name NUMBER := 0;
v_pct_match NUMBER := 0;
v_flag NUMBER;
v_jaro_pct NUMBER := 0;
v_highest_jaro_pct NUMBER := 0;
v_match_exact NUMBER := 0;
v_match_exact_res NUMBER := 0;
BEGIN
l_str := p_name_str || p_delim;
LOOP
l_n := instr(l_str, p_delim);
EXIT WHEN(nvl(l_n, 0) = 0);
-- condition to check if only space
IF l_n <> 1 THEN
l_name_data.extend;
l_name_data(l_name_data.count) := ltrim(rtrim(substr(l_str,
1,
l_n - 1)));
v_name_cnt := v_name_cnt + 1;
END IF;
l_str := substr(l_str, l_n + length(p_delim));
END LOOP;
v_pct_per_name := 100 / v_name_cnt;
l_str := p_str || p_delim;
LOOP
l_n := instr(l_str, p_delim);
EXIT WHEN(nvl(l_n, 0) = 0);
l_data.extend;
l_data(l_data.count) := ltrim(rtrim(substr(l_str, 1, l_n - 1)));
l_str := substr(l_str, l_n + length(p_delim));
END LOOP;
FOR nme IN 1 .. l_name_data.count LOOP
v_flag := 0;
v_highest_jaro_pct := 0;
FOR i IN 1 .. l_data.count LOOP
v_jaro_pct := utl_match.jaro_winkler_similarity(l_name_data(nme),
l_data(i));
IF soundex(l_name_data(nme)) = soundex(l_data(i)) AND
v_jaro_pct >= p_relev_thresh THEN
IF v_jaro_pct > v_highest_jaro_pct THEN
v_highest_jaro_pct := v_jaro_pct;
END IF;
END IF;
END LOOP;
v_pct_match := v_pct_match +
(v_pct_per_name * v_highest_jaro_pct / 100);
END LOOP;
SELECT utl_match.edit_distance_similarity(p_name_str, p_str)
INTO v_match_exact
FROM dual;
if (trunc(v_match_exact) =100 ) then
return trunc(v_pct_match, 2);
else
if( v_match_exact <> 0) then
v_match_exact_res := (5 / v_match_exact) * 100;
v_pct_match := v_pct_match - v_match_exact_res;
end if;
if v_pct_match >20 then
RETURN trunc(v_pct_match, 2);
end if;
end if;
END;
This query is fetching result from INDIV_PROF_sub_test123 table this is having data of 35 million and partitioned.
i found the problematic area in query func_compare_name we are using it having name similarity checking function
Adding explain plan:
Plan
SELECT STATEMENT HINT: FIRST_ROWS
Cost: 262
Bytes: 4,056
Cardinality: 104 4
VIEW VC_CLONE.
Cost: 262
Bytes: 4,056
Cardinality: 104 3
WINDOW SORT PUSHED RANK
Cost: 262
Bytes: 5,512
Cardinality: 104 2
TABLE ACCESS BY GLOBAL INDEX ROWID TABLE VC_CLONE.INDIV_PROF_ONE_MONTH_1
Cost: 261
Bytes: 5,512
Cardinality: 104
Partition #: 3
Partitions accessed #1672 1
INDEX RANGE SCAN INDEX VC_CLONE.IDX_BIRTH_DT_INVID
Cost: 4
Cardinality: 1 –

Related

PLS-00103: Encountered the symbol "+" when expecting one of the following: (

declare
i number;
sum number;
begin
i:=1;
sum:=0;
for i in 1..100 loop
if MOD(i,2) != 0 then
sum:= sum + i;
dbms_output.put_line(i);
end if;
end loop;
dbms_output.Put_line(sum);
end;
sum is a reserved word, reserved for built-in function. Rename variable to v_sum (for example).
SQL> set serveroutput on
SQL>
SQL> DECLARE
2 v_sum NUMBER;
3 BEGIN
4 v_sum := 0;
5 FOR i IN 1 .. 100 LOOP
6 IF MOD (i, 2) != 0 THEN
7 v_sum := v_sum + i;
8 -- DBMS_OUTPUT.put_line (i);
9 END IF;
10 END LOOP;
11 DBMS_OUTPUT.Put_line (v_sum);
12 END;
13 /
2500
PL/SQL procedure successfully completed.
SQL>

TOP with index variable oracle

I was trying to use the variable 'ind' to get the ROWNUM on my select, but everytime I try to use the variable there I get a error like:
ORA-01008: not all variables bound
or these two:
ORA-01403: no data found
ORA-06512: at line 10
DECLARE
texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
LOOP
ind := ind + 1;
IF ind > 3 THEN
EXIT;
END IF;
SELECT TEXTO_LOG
INTO texto
from table WHERE REGEXP_LIKE(TEXTO_LOG, 'Alteração') AND ROWNUM >= :ind AND ROWNUM <= :ind ;
dbms_output.put_line(substr(trim(texto), 1, instr(texto, ' ')));
dbms_output.put_line(substr(texto, 0, 100));
END LOOP;
END;
/
I searched and found someone telling that not all variables bound is a bug, I'm not sure if it's.
I tried the ROWNUM with different opperators. Any suggestions?
Usage of rownum is the problem here apart from other issues already answered. a predicate like rownum>=2 and rownum <=2 (and so on...till exit point) yield no result and thus no_data_found error
But as workaround put the rownum inside from clause and restrict it outside should work,
DECLARE
texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
LOOP
ind := ind + 1;
IF ind > 3
THEN
EXIT;
END IF;
SELECT texto_log
INTO texto
FROM (SELECT texto_log
,rownum myrownum
FROM TABLE
WHERE regexp_like(texto_log
,'Alteração'))
WHERE myrownum >= ind
AND myrownum <= ind;
dbms_output.put_line(substr(TRIM(texto)
,1
,instr(texto
,' ')));
dbms_output.put_line(substr(texto
,0
,100));
END LOOP;
END;
/
The error is that you are referring to :ind as if it were a bind variable, when it is not. It is in fact a variable declared in your DECLARE section.
You might try this
** Update **
As your query looks like it is faling, the reason perhaps is in the query itself. Run this and get the output and running separately
set serveroutput on size unlimited
DECLARE
texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
for r in 1..4
LOOP
ind := r + 1;
dbms_output.put_line ( q'[SELECT TEXTO_LOG
INTO texto
from table WHERE REGEXP_LIKE(TEXTO_LOG, 'Alteração') AND ROWNUM <= ind ;
dbms_output.put_line(substr(trim(texto), 1, instr(texto, ' ')));
dbms_output.put_line(substr(texto, 0, 100));]');
exit when ind > 3;
END LOOP;
END;
/
Although you can build it like this which is easier
DECLARE
texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
for r in 1..4
LOOP
ind := r + 1;
dbms_output.put_line(ind);
SELECT TEXTO_LOG
INTO texto
from table WHERE REGEXP_LIKE(TEXTO_LOG, 'Alteração') AND ROWNUM >= ind AND ROWNUM <= ind ;
dbms_output.put_line(substr(trim(texto), 1, instr(texto, ' ')));
dbms_output.put_line(substr(texto, 0, 100));
exit when ind > 3;
END LOOP;
END;
/
EXample
SQL> DECLARE
texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
for r in 1..4
LOOP
ind := r + 1;
dbms_output.put_line(ind);
exit when ind > 3;
END LOOP;
END;
/ 2 3 4 5 6 7 8 9 10 11 12
2
3
4
PL/SQL procedure successfully completed.
SQL>
With implicit cursor
DECLARE
-- texto VARCHAR2(255);
ind NUMBER := 0;
BEGIN
FOR i IN (SELECT TEXTO_LOG FROM table WHERE REGEXP_LIKE(TEXTO_LOG, 'Alteração'))
LOOP
ind:=ind+1;
EXIT WHEN ind >3;
DBMS_OUTPUT.PUT_LINE(SUBSTR(TRIM(i.TEXTO_LOG), 1, INSTR(i.TEXTO_LOG, ' ')));
DBMS_OUTPUT.PUT_LINE(SUBSTR(i.TEXTO_LOG, 0, 100));
END LOOP;
END;

PL/SQL function returns minimal number

How to write a simple function that returns minimal salary. IN parameters are salary1,salary2,salary3 (9240, 9750, 8320) and it is forbidden to use finished functions.
I have no code whatsoever. I am very new at this and am trying to learn something.
The function is already exists in PLSQL. Check it out here. There are good examples of how to use it
UPD: As per Pavel mentioned here is a pair of examples
SQL:
select least(100, 1, 200) from dual; -- returns 1
PLSQL:
create or replace procedure get_min(s1 number, s2 number, s3 number)
is
min_sal number := 0;
begin
min_sal := least(s1, s2, s3);
dbms_output.put_line('min: ' || min_sal);
end;
if it is not allowed to use existing functions, you can do it using If then else
create or replace function get_min(s1 number, s2 number, s3 number)
return number
is
min_sal number := 0;
begin
---------------------
-- if the first value is less than the second value
-- , then the first value is the min of both values
-- otherwise the second value is min
if s1 < s2 then
min_sal := s1;
else
min_sal := s2;
end if;
------------------------------------
-- now you check the new min value against the third value.
if s3 < min_sal then
min_sal := s3;
end if;
dbms_output.put_line('min: ' || min_sal);
return min_sal;
end;
/
DECLARE
v_result NUMBER;
BEGIN
v_result := GET_MIN(1, 1, 1);
v_result := GET_MIN(2, 1, 1);
v_result := GET_MIN(1, 2, 1);
v_result := GET_MIN(1, 1, 2);
v_result := GET_MIN(2, 2, 1);
v_result := GET_MIN(1, 2, 2);
v_result := GET_MIN(1, 2, 3);
v_result := GET_MIN(2, 1, 3);
v_result := GET_MIN(3, 2, 1);
END;
/
Result:
dbms_output:
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
db<>fiddle here

Looping in PL/SQL variable is not a cursor

I'm trying to run the following loop:
DECLARE
v_banknumber varchar2(9) := '123456789';
v_counter number := 9;
v_result number;
begin
for i in v_banknumber
loop
v_result := v_counter * TO_NUMBER(i) + v_result;
v_counter := v_counter - 1;
end loop;
end;
I'm getting a error at line 2:
Error report -
ORA-06550: line 6, column 10:
PLS-00456: item 'V_BANKNUMBER' is not a cursor
ORA-06550: line 6, column 1:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
If I read this well, it seems like it should work. Anyone here that can explain me why it's not working?
The first digit must be multiplied by 9, the second with 8, the third with 7, and so on and save the sum of it in a result variable.
At a guess, what you want to do is
DECLARE
v_banknumber varchar2(9) := '123456789';
v_counter number := 9;
v_result number := 0;
begin
for i in 1..LENGTH(v_banknumber)
loop
v_result := v_counter * TO_NUMBER(SUBSTR(v_banknumber, i, 1)) + v_result;
v_counter := v_counter - 1;
end loop;
end;
This gives a result of 165.
Best of luck.
EDIT
Or you could really use a cursor:
DECLARE
v_banknumber varchar2(9) := '123456789';
v_counter number := 9;
v_result number := 0;
begin
for aRow in (SELECT LEVEL AS I FROM DUAL CONNECT BY LEVEL <= LENGTH(v_banknumber))
loop
v_result := v_counter * TO_NUMBER(SUBSTR(v_banknumber, aRow.I, 1)) + v_result;
v_counter := v_counter - 1;
end loop;
end;
Produces 165 as the result.
EDIT #2
Or, because there's no kill like overkill, you could just do it all in SQL:
WITH cteBank_number AS (SELECT '123456789' AS BANK_NUMBER FROM DUAL),
cteI AS (SELECT LEVEL AS I
FROM DUAL d
CROSS JOIN cteBank_number b
CONNECT BY LEVEL <= LENGTH(b.BANK_NUMBER)),
cteNums AS (SELECT TO_NUMBER(SUBSTR(b.BANK_NUMBER, LENGTH(b.BANK_NUMBER)-i.I+1, 1)) AS DIGIT,
i.I AS I,
TO_NUMBER(SUBSTR(b.BANK_NUMBER, LENGTH(b.BANK_NUMBER)-i.I+1, 1)) * i.I AS NUM
FROM cteBank_number b
CROSS JOIN cteI i)
SELECT SUM(NUM)
FROM cteNums n;
Still produces 165 as the result.
Your v_banknumber variable is a string not a cursor. You need to loop over each character in that string, and treat that character as a digit.
You could do this as:
set serveroutput on
declare
v_banknumber varchar2(9) := '123456789';
v_result number := 0;
begin
for v_counter in reverse 1..length(v_banknumber)
loop
v_result := v_result
+ (v_counter * to_number(substr(v_banknumber, -v_counter, 1)));
end loop;
dbms_output.put_line('The result is: ' || v_result);
end;
/
The result is: 165
PL/SQL procedure successfully completed.
With extra debugs to try to show what is happening on each iteration:
declare
v_banknumber varchar2(9) := '123456789';
v_result number := 0;
begin
dbms_output.put_line('length(v_banknumber) is: ' || length(v_banknumber));
for v_counter in reverse 1..length(v_banknumber)
loop
dbms_output.put_line('v_counter is: ' || v_counter);
dbms_output.put_line(' Digit is substr(v_banknumber, v_counter, 1): '
|| substr(v_banknumber, -v_counter, 1));
dbms_output.put_line(' Calculation for digit is: '
|| v_counter * to_number(substr(v_banknumber, -v_counter, 1)));
v_result := v_result
+ (v_counter * to_number(substr(v_banknumber, -v_counter, 1)));
dbms_output.put_line(' Running total: ' || v_result);
end loop;
dbms_output.put_line('The result is: ' || v_result);
end;
/
length(v_banknumber) is: 9
v_counter is: 9
Digit is substr(v_banknumber, v_counter, 1): 1
Calculation for digit is: 9
Running total: 9
v_counter is: 8
Digit is substr(v_banknumber, v_counter, 1): 2
Calculation for digit is: 16
Running total: 25
v_counter is: 7
Digit is substr(v_banknumber, v_counter, 1): 3
Calculation for digit is: 21
Running total: 46
v_counter is: 6
Digit is substr(v_banknumber, v_counter, 1): 4
Calculation for digit is: 24
Running total: 70
v_counter is: 5
Digit is substr(v_banknumber, v_counter, 1): 5
Calculation for digit is: 25
Running total: 95
v_counter is: 4
Digit is substr(v_banknumber, v_counter, 1): 6
Calculation for digit is: 24
Running total: 119
v_counter is: 3
Digit is substr(v_banknumber, v_counter, 1): 7
Calculation for digit is: 21
Running total: 140
v_counter is: 2
Digit is substr(v_banknumber, v_counter, 1): 8
Calculation for digit is: 16
Running total: 156
v_counter is: 1
Digit is substr(v_banknumber, v_counter, 1): 9
Calculation for digit is: 9
Running total: 165
The result is: 165
Did you mean to do it as an array?
DECLARE
type array_t is varray(9) of number;
a_banknumber array_t := array_t (1,2,3,4,5,6,7,8,9);
v_counter number := a_banknumber.count;
v_result number := 0;
begin
for i in 1..a_banknumber.count
loop
v_result := v_counter * a_banknumber(i) + v_result;
v_counter := v_counter - 1;
end loop;
dbms_output.put_line('Result: ' || v_result);
end;

How write a PL/SQL program that prints out string which looking like xml format

Input String : “a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k3g5g5k3w2”
I tried this code as first step:
declare
word varchar2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num number := length(word)/2;
name_array dbms_sql.varchar2_table;
begin
dbms_output.put_line(word);
FOR i IN 1..num LOOP
name_array(i) := substr(word, -2*i, 2);
END LOOP;
FOR i IN name_array.FIRST .. name_array.LAST LOOP
dbms_output.put_line(name_array(i));
END LOOP;
end;
This code creates only an array of string. Not xml format. I need this output:
Which SQL functions,conditional clauses... do I need to use?
Oracle Setup:
CREATE OR REPLACE TYPE CHARS_TABLE IS TABLE OF CHAR(2);
/
CREATE OR REPLACE TYPE INTEGERS_TABLE IS TABLE OF INTEGER;
/
PL/SQL:
This assumes a well-formed set of character pairs and just indents each pair to the appropriate level:
DECLARE
word VARCHAR2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num PLS_INTEGER := LENGTH( word ) / 2;
name_array CHARS_TABLE := CHARS_TABLE();
depth_array INTEGERS_TABLE := INTEGERS_TABLE();
open_array INTEGERS_TABLE := INTEGERS_TABLE();
BEGIN
name_array.EXTEND( num );
depth_array.EXTEND( num );
open_array.EXTEND( num );
name_array(1) := SUBSTR( word, 1, 2 );
depth_array(1) := 1;
open_array(1) := 1;
FOR i IN 2 .. num LOOP
name_array(i) := SUBSTR( word, 2*i - 1, 2 );
open_array(i) := 1;
FOR j IN 1 .. i-1 LOOP
IF name_array(j) = name_array(i) THEN
open_array(i) := -open_array(i);
END IF;
END LOOP;
depth_array(i) := depth_array(i-1) + open_array(i);
END LOOP;
FOR i IN 1 .. num LOOP
FOR j IN 2 .. depth_array(i) + CASE open_array(i) WHEN 1 THEN 0 ELSE 1 END LOOP
DBMS_OUTPUT.PUT( ' ' );
END LOOP;
DBMS_OUTPUT.PUT_LINE( name_array(i) );
END LOOP;
END;
/
Output:
a4
b4
c2
d9
d9
c2
e6
e6
b4
s2
o1
o1
s2
a4
w2
r8
r8
k2
g5
g5
k2
w2
Update - Simpler Stack-Based Version:
DECLARE
word CONSTANT VARCHAR2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num CONSTANT PLS_INTEGER := LENGTH( word ) / 2;
name_array CHARS_TABLE := CHARS_TABLE();
depth PLS_INTEGER := 0;
name CHAR(2);
PROCEDURE indent( depth PLS_INTEGER, name CHAR )
IS
BEGIN
FOR j IN 2 .. depth LOOP
DBMS_OUTPUT.PUT( ' ' );
END LOOP;
DBMS_OUTPUT.PUT_LINE( name );
END;
BEGIN
name_array.EXTEND( num );
FOR i IN 1 .. num LOOP
name := SUBSTR( word, 2*i - 1, 2 );
IF depth > 0 AND name = name_array(depth) THEN
indent(depth,name);
depth := depth - 1;
ELSE
depth := depth - 1;
name_array(depth) := name;
indent(depth,name);
END IF;
END LOOP;
END;
/
DECLARE
vs_CurrentChar VARCHAR2(1);
vs_NextChar VARCHAR2(1);
vs_TempText VARCHAR2(100);
vs_InputText VARCHAR2(100) := 'abcdffdcba';
vn_LengthOfText NUMBER := 1;
vn_WhileIndex NUMBER := 1;
vs_Spaces VARCHAR(100);
BEGIN
vs_TempText := NULL;
vs_CurrentChar := substr(vs_InputText, vn_WhileIndex, vn_LengthOfText);
dbms_output.put_line(vs_CurrentChar);
WHILE vn_WhileIndex < length(vs_InputText) - 1 LOOP
vs_NextChar := substr(vs_InputText, vn_WhileIndex + 1, vn_LengthOfText);
EXIT WHEN vs_CurrentChar = vs_NextChar;
vs_TempText := vs_TempText || vs_CurrentChar;
vs_CurrentChar := vs_NextChar;
vs_Spaces := NULL;
FOR i IN 1 .. vn_WhileIndex LOOP
vs_Spaces := vs_Spaces || chr(9); --'*';
END LOOP;
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
vn_WhileIndex := vn_WhileIndex + 1;
END LOOP;
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
FOR i IN 1 .. length(vs_TempText) LOOP
vs_Spaces := substr(vs_Spaces, vn_LengthOfText, length(vs_Spaces) - 1);
vs_CurrentChar := substr(vs_TempText, -i, vn_LengthOfText);
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
END LOOP;
END;
/
And output:
a
b
c
d
f
f
d
c
b
a
even, if you put '*'; instead of chr(9); then output will look like as:
a
*b
**c
***d
****f
****f
***d
**c
*b
a

Resources