0% found this document useful (0 votes)
44 views

Logic Works 06

complex pl/sql scripts to address complicated business logic

Uploaded by

JP Vijaykumar
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
44 views

Logic Works 06

complex pl/sql scripts to address complicated business logic

Uploaded by

JP Vijaykumar
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 15

COMPLEX PL/SQL PROCEDURE

I presented this pl/sql script to solve a complex problem in 2010.


This pl/sql script consists many lines of code, and uses a global temporary
table,besides an external table to load the data from a csv file.

I was curious to avoid global temporary table, and make the script concise.

This script is provided for educational purpose only.


This script is specific to the problem mentioned herebelow, and is purely of
academic interest.

I am also presenting a solution in python.

The problem remained the same, but I am presenting 5 different ways to solve.There
are other ways, but these ways increased the complexity and my curiosity to solve
the problem.

CASE LOGIC 05
DATE 01-04-10
*************
--OPTION 01--

PROBLEM DEFINITION
--FOR EACH CODE IN THE FLAT FILE, THERE ARE VALUES.
--THE BUSINESS WANTS TO DISPLAY ONLY THE NON-ZERO VALUES IN ASCENDING ORDER.
--FROM THE DISPLAYED RECORDS DISCARD THE FIRST 1/3 RD RECORDS AND LAST 1/3 RD
RECORDS.
--FIND THE AVERAGE OF THE REMAINING MIDDLE 1/3 RD RECORDS ONLY FOR EACH CODE.

$ cat associate.txt
100|0|0|0|0|0|0|0|0|0|0|55474|0|0|0|47769|0|95915|0|0|61401|0|0|0|0|0|0|0|0|0|0|0|
0|0|0|0
111|0|0|0|0|41165|0|54744|0|0|0|55480|51781|0|0|54000|0|46481|0|0|64051|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0
101|0|0|0|0|0|0|0|0|0|0|0|0|0|0|51804|0|55116|0|0|54100|0|0|0|0|0|0|0|0|0|0|0|0|0|
0|0
104|0|57617|0|0|0|0|0|0|0|0|0|51781|0|49574|45448|0|0|0|0|49557|0|0|0|0|0|0|0|0|0|
0|0|0|0|0|0
114|0|0|0|0|0|0|0|0|0|0|61711|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0
105|0|0|0|0|0|0|0|0|0|0|0|0|0|49574|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0
106|0|0|0|0|0|0|0|0|0|55141|0|0|0|0|0|0|40546|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0
107|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0
108|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0
109|0|0|0|0|45844|0|54015|0|0|0|64480|0|0|44104|54618|0|41449|0|0|48745|0|0|0|0|0|
0|0|0|0|0|0|0|0|0|0

sqlplus / as sysdba

create or replace directory data_pump_dir


as '/home/oracle/jp';

set linesize 120


column directory_path format a60
select directory_name,substr(directory_path,1,60) directory_path from
dba_directories order by 1

grant read,write on directory data_pump_dir to veeksha;


connect veeksha/saketh
drop table xtern_temp_jp;
create table xtern_temp_jp (
code number(10),
col1 number(10),
col2 number(10),
col3 number(10),
col4 number(10),
col5 number(10),
col6 number(10),
col7 number(10),
col8 number(10),
col9 number(10),
col10 number(10),
col11 number(10),
col12 number(10),
col13 number(10),
col14 number(10),
col15 number(10),
col16 number(10),
col17 number(10),
col18 number(10),
col19 number(10),
col20 number(10),
col21 number(10),
col22 number(10),
col23 number(10),
col24 number(10),
col25 number(10),
col26 number(10),
col27 number(10),
col28 number(10),
col29 number(10),
col30 number(10),
col31 number(10),
col32 number(10),
col33 number(10),
col34 number(10),
col35 number(10))
organization external
( default directory data_pump_dir
access parameters
( records delimited by newline
fields terminated by '|'
)
location ('associate.txt')
);

drop table counter_jp;


create global temporary table counter_jp(col1 number)
on commit delete rows;

set serverout on size 1000000


declare

v_ct1 number(10);
v_ct2 number(10);

v_sum number(10);
v_avg number(10);
begin

for c1 in (select * from xtern_temp_jp order by code) loop


begin

v_ct1 :=0;
v_ct2 :=0;

v_sum :=0;
v_avg :=0;

if (c1.col1 >0) then


insert into counter_jp values(c1.col1);
end if;

if (c1.col2 >0) then


insert into counter_jp values(c1.col2);
end if;

if (c1.col3 >0) then


insert into counter_jp values(c1.col3);
end if;

if (c1.col4 >0) then


insert into counter_jp values(c1.col4);
end if;

if (c1.col5 >0) then


insert into counter_jp values(c1.col5);
end if;

if (c1.col6 >0) then


insert into counter_jp values(c1.col6);
end if;

if (c1.col7 >0) then


insert into counter_jp values(c1.col7);
end if;

if (c1.col8 >0) then


insert into counter_jp values(c1.col8);
end if;

if (c1.col9 >0) then


insert into counter_jp values(c1.col9);
end if;

if (c1.col10 >0) then


insert into counter_jp values(c1.col10);
end if;

if (c1.col11 >0) then


insert into counter_jp values(c1.col11);
end if;

if (c1.col12 >0) then


insert into counter_jp values(c1.col12);
end if;
if (c1.col13 >0) then
insert into counter_jp values(c1.col13);
end if;

if (c1.col14 >0) then


insert into counter_jp values(c1.col14);
end if;

if (c1.col15 >0) then


insert into counter_jp values(c1.col15);
end if;

if (c1.col16 >0) then


insert into counter_jp values(c1.col16);
end if;

if (c1.col17 >0) then


insert into counter_jp values(c1.col17);
end if;

if (c1.col18 >0) then


insert into counter_jp values(c1.col18);
end if;

if (c1.col19 >0) then


insert into counter_jp values(c1.col19);
end if;

if (c1.col20 >0) then


insert into counter_jp values(c1.col20);
end if;

if (c1.col21 >0) then


insert into counter_jp values(c1.col21);
end if;

if (c1.col22 >0) then


insert into counter_jp values(c1.col22);
end if;

if (c1.col23 >0) then


insert into counter_jp values(c1.col23);
end if;

if (c1.col24 >0) then


insert into counter_jp values(c1.col24);
end if;

if (c1.col25 >0) then


insert into counter_jp values(c1.col25);
end if;

if (c1.col26 >0) then


insert into counter_jp values(c1.col26);
end if;

if (c1.col27 >0) then


insert into counter_jp values(c1.col27);
end if;

if (c1.col28 >0) then


insert into counter_jp values(c1.col28);
end if;

if (c1.col29 >0) then


insert into counter_jp values(c1.col29);
end if;

if (c1.col30 >0) then


insert into counter_jp values(c1.col30);
end if;

if (c1.col31 >0) then


insert into counter_jp values(c1.col31);
end if;

if (c1.col32 >0) then


insert into counter_jp values(c1.col32);
end if;

if (c1.col33 >0) then


insert into counter_jp values(c1.col33);
end if;

if (c1.col34 >0) then


insert into counter_jp values(c1.col34);
end if;

if (c1.col35 >0) then


insert into counter_jp values(c1.col35);
end if;

execute immediate 'select count(1) from counter_jp' into v_ct1;

if (v_ct1 >= 3) then


v_ct2:= trunc(v_ct1/3);
dbms_output.put_line(c1.code||' '||v_ct1||' '||v_ct2);

execute immediate ' select sum(col1), avg(col1) from ( select rownum rnum , t1.*
from ('||
' select col1 from counter_jp order by col1) t1) t2 where t2.rnum >'
||v_ct2||' and t2.rnum <=('||v_ct1||' - '||v_ct2||') ' into v_sum, v_avg;

dbms_output.put_line(c1.code||' '||v_ct1||' '||v_ct2||' '||v_sum||' '||v_avg);

dbms_output.put_line('-');
end if;

commit;
execute immediate 'truncate table counter_jp';
exception
when others then
dbms_output.put_line(c1.code||' '||sqlerrm);
end;
end loop;
end;
/
100 4 1
100 4 1 116875 58438
-
101 3 1
101 3 1 54100 54100
-
104 5 1
104 5 1 150912 50304
-
109 7 2
109 7 2 148604 49535
-
111 7 2
111 7 2 160525 53508
-

PL/SQL procedure successfully completed.

References:
http://www.adp-gmbh.ch/ora/misc/ext_table_2.html
Acknowledgements:
Thanks Vineela, for designing test case and scripting.

*******************Written 09/14/2018****************

--OPTION 02--

connect veeksha/saketh

set serverout on size 1000000 timing on


declare
begin
for c1 in (select code from xtern_temp_jp order by 1) loop
for c2 in (
with t1 as (
select * from xtern_temp_jp
unpivot(value for value_type in(
COL1,
COL2,
COL3,
COL4,
COL5,
COL6,
COL7,
COL8,
COL9,
COL10,
COL11,
COL12,
COL13,
COL14,
COL15,
COL16,
COL17,
COL18,
COL19,
COL20,
COL21,
COL22,
COL23,
COL24,
COL25,
COL26,
COL27,
COL28,
COL29,
COL30,
COL31,
COL32,
COL33,
COL34,
COL35))),
t2 as (
select value,num_rows,trunc(num_rows/3) num_rows_by_three from t1,
(select count(*) num_rows from t1 where code=c1.code and value>0) t1_sum where
code=c1.code and value>0 order by value),
t3 as (
select rownum row_num,t2.* from t2)
select t3.*,avg_value from t3,(select round(avg(value)) avg_value from t3 where
row_num>num_rows_by_three and row_num<= num_rows - num_rows_by_three)
where row_num>num_rows_by_three and row_num<= num_rows - num_rows_by_three ) loop
if (c2.num_rows >= 3) then
dbms_output.put_line(c1.code||' '||c2.row_num||' '||c2.value||' '||c2.num_rows||'
'||c2.num_rows_by_three||' '||c2.avg_value);
end if;
end loop;
end loop;
end;
/
100 2 55474 4 1 58438
100 3 61401 4 1 58438
101 2 54100 3 1 54100
104 2 49557 5 1 50304
104 3 49574 5 1 50304
104 4 51781 5 1 50304
109 3 45844 7 2 49535
109 4 48745 7 2 49535
109 5 54015 7 2 49535
111 3 51781 7 2 53508
111 4 54000 7 2 53508
111 5 54744 7 2 53508

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.51

References:
http://docshare.tips/logic-works-01_575867e2b6d87fb1268b45f2.html --CASE
LOGIC
05
https://www.scribd.com/doc/59684433/Logic-Works-01 --CASE LOGIC 05

Acknowledgements:
Thanks Vineela, for setting up vbox node and scripting.
*****************Written 09/14/2018***************************
--OPTION 03--

import pandas as pd
import math
#df=pd.DataFrame(pd.read_csv('c:/jpscripts/associate.csv',sep=',',index_col=0,heade
r=None))
df=pd.read_csv('c:/jpscripts/associate.csv',sep=',',index_col=0,header=None)
'''
df[0:1].unstack().value_counts().shape

[y for y in df[:1].unstack() if y != 0]

df[0:1].unstack()[df[0:1].unstack()!=0]

for i in sorted(df[:2].unstack()[df[:2].unstack()!=0]):
print(i)
'''
for i in range(len(df)):
length=df[i:i+1].unstack()[df[i:i+1].unstack()!=0].count()
#print(length)
if (length >=3):
cnt=math.trunc(length/3)
tot=length - 2*cnt
seq=0
val=0
#print(cnt,tot)
print(df.index[i])
#print(df[i:i+1].unstack()[df[i:i+1].unstack()!=0].value_counts())
for i in sorted(df[i:i+1].unstack()[df[i:i+1].unstack()!=0]):
seq +=1
if ((seq > cnt) and seq <= (cnt + tot)):
val += i
print(seq,i)
if (val > 0):
print(length,val,math.trunc(val/tot)) #round(val/tot,2)

100
2 55474
3 61401
4 116875 58437
111
3 51781
4 54000
5 54744
7 160525 53508
101
2 54100
3 54100 54100
104
2 49557
3 49574
4 51781
5 150912 50304
109
3 45844
4 48745
5 54015
7 148604 49534

From the above scripts' output, there are few formatting differences.But the
displayed data confirms to the requirements of the problem's definition.

Happy scripting.
*******************Written 03/08/2019****************
--OPTION 04--

connect veeksha/saketh

--In this script, I used regexp_substr function inplace of unpivot function.


--The results from all the scripts are matching.

set serverout on size 1000000 timing on


declare
begin
for c1 in (select code from xtern_associate_jp order by 1) loop
for c2 in (
with t1 as (select
COL1||','||
COL2||','||
COL3||','||
COL4||','||
COL5||','||
COL6||','||
COL7||','||
COL8||','||
COL9||','||
COL10||','||
COL11||','||
COL12||','||
COL13||','||
COL14||','||
COL15||','||
COL16||','||
COL17||','||
COL18||','||
COL19||','||
COL20||','||
COL21||','||
COL22||','||
COL23||','||
COL24||','||
COL25||','||
COL26||','||
COL27||','||
COL28||','||
COL29||','||
COL30||','||
COL31||','||
COL32||','||
COL33||','||
COL34||','||
COL35 string from xtern_associate_jp where code=c1.code),
t2 as (select * from (
select regexp_substr(string,'[^ ,]+', 1, level) as names from t1
connect by regexp_substr(string, '[^, ]+', 1, level) is not null) where
to_number(names)>0 order by 1),
t3 as (select count(*) cnt,trunc(count(*)/3) cnt3 from t2 ),
t4 as (select round(avg(to_number(names))) avg_val from (select rownum row_num,
t2.* from t2),t3 where row_num >t3.cnt3 and row_num <= (t3.cnt - t3.cnt3))
select names,t4.avg_val,t3.cnt from (select rownum row_num, t2.* from t2),t3,t4
where row_num >t3.cnt3 and row_num <= (t3.cnt - t3.cnt3)) loop
if (c2.cnt >=3) then
dbms_output.put_line(c1.code||' '||c2.names||' '||c2.avg_val);
end if;
end loop;
end loop;
end;
/
100 55474 58438
100 61401 58438
101 54100 54100
104 49557 50304
104 49574 50304
104 51781 50304
109 45844 49535
109 48745 49535
109 54015 49535
111 51781 53508
111 54000 53508
111 54744 53508

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.13

----------------------------------------------------------------
*******************Written 03/08/2019****************

--OPTION 05--

--I was curious, to solve the problem using sql query.

connect veeksha/saketh

set pagesize 20
with
t1 as (
select code,value from xtern_associate_jp
unpivot(value for value_type in(
COL1,
COL2,
COL3,
COL4,
COL5,
COL6,
COL7,
COL8,
COL9,
COL10,
COL11,
COL12,
COL13,
COL14,
COL15,
COL16,
COL17,
COL18,
COL19,
COL20,
COL21,
COL22,
COL23,
COL24,
COL25,
COL26,
COL27,
COL28,
COL29,
COL30,
COL31,
COL32,
COL33,
COL34,
COL35)) where value >0 order by code,value),
t2 as (select code,count(value) cnt, trunc(count(value)/3) bg,(count(value) -
trunc(count(value)/3)) en from t1 group by code having count(value) >=3),
t3 as (select t.code,value,row_num from (select code,value,rank() over(partition
by code order by value) as row_num from t1) t, t2 where t.code=t2.code and
t.row_num>t2.bg and t.row_num <= t2.en),
t4 as (select code,trunc(avg(value)) avg_value from t3 group by code)
select t3.code,t3.value,t4.avg_value from t3,t4 where t3.code = t4.code order by 1

CODE VALUE AVG_VALUE


---------- ---------- ----------
100 61401 58437
100 55474 58437
101 54100 54100
104 49574 50304
104 49557 50304
104 51781 50304
109 45844 49534
109 48745 49534
109 54015 49534
111 51781 53508
111 54000 53508
111 54744 53508

12 rows selected.
--Happy scripting.

You might also like