pl/sql multiple if or multiple begin when data found block - sql

I am new to writing stored procedures. I want to see which is the best way to execute a functionality like below:
cursor c1 is select * from cases where caseid = '2332534534';
begin
for t_case in c1
loop
/..///
if t_case.oCode is not null then
select id into v_ao from lkp_ao where upper(descr) = upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode));
if v_ao = 0 then
select id into v_ao from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,5)||'%' and rownum=1;
if v_ao = 0 then
select id into v_ao from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,4)||'%' and rownum=1;
if v_ao = 0 then
select id into v_ao from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,3)||'%' and rownum=1;
if v_ao = 0 then
v_ao := '';
end if;
end if;
end if;
end if;
else
v_ao := '';
end if;
/..///
end loop;
commit;
exception
when others then
log_error(0, 'INSERT_case - exception outside' || SQLERRM || ' code ' || SQLCODE, 1);
commit;
end;
This part of code would not work. so instead of select id into v_ao , i will have to check
select count(*) into v_ao from lkp_ao where upper(descr) = upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode));
if v_ao = 0 then
//do something
else
select id into v_ao from lkp_ao where upper(descr) = upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode));
end if;
So i am executing 1 query for count and 1 query to get the actual id in the else part.
The other way of doing is using begin when no data found execute the 2nd query and inside that no data found open another begin and so on. So basically there will be 5 begin and exception when no data found block which i feel is huge code.
Whats the simplest way to do this kind of condition in oracle stored procedure? thanks in advance.

Get all the matching rows in a single statement (so you do not have to query the table multiple times) and then use a CASE statement to order the rows and only get the best match:
DECLARE
v_substr CASES.OCODE%TYPE;
cursor c1 is select * from cases where caseid = '2332534534';
BEGIN
for t_case in c1
loop
/* ... */
if t_case.oCode is not null then
IF instr(t_case.oCode,',') = 0 THEN
v_substr := UPPER(t_case.oCode);
ELSE
v_substr := UPPER(SUBSTR(t_case.oCode, 1, INSTR(t_case.oCode,',')-1));
END IF;
v_ao := ''; -- same as NULL
BEGIN
SELECT id
INTO v_ao
from lkp_ao
WHERE substr(upper(descr),1,3) = SUBSTR(v_substr, 1, 3)
ORDER BY
CASE
WHEN upper(descr) = v_substr
THEN 1
WHEN substr(upper(descr),1,5) = SUBSTR(v_substr, 1, 5)
THEN 2
WHEN substr(upper(descr),1,4) = SUBSTR(v_substr, 1, 4)
THEN 3
WHEN substr(upper(descr),1,3) = SUBSTR(v_substr, 1, 3)
THEN 4
END
FETCH FIRST ROW ONLY;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END IF;
/* .. */
end loop;
commit;
exception
when others then
log_error(0, 'INSERT_case - exception outside' || SQLERRM || ' code ' || SQLCODE, 1);
commit; -- Really commit after an exception?
END;
/
If you want to make it even more efficient then do all the matching in the cursor query (then you do not have to context switch in every iteration of the cursor between PL/SQL and SQL):
DECLARE
v_substr CASES.OCODE%TYPE;
CURSOR c1 IS
SELECT c.*,
l.id AS ao
FROM (
SELECT c.*,
CASE INSTR(oCode, ',')
WHEN 0 THEN oCode
ELSE SUBSTR(oCode, 1, INSTR(oCode, ',') - 1)
END AS term
FROM cases c
) c
LEFT OUTER JOIN LATERAL (
SELECT id
from lkp_ao a
WHERE substr(upper(a.descr),1,3) = SUBSTR(c.term, 1, 3)
ORDER BY
CASE
WHEN upper(a.descr) = c.term
THEN 1
WHEN substr(upper(a.descr),1,5) = SUBSTR(c.term, 1, 5)
THEN 2
WHEN substr(upper(a.descr),1,4) = SUBSTR(c.term, 1, 4)
THEN 3
WHEN substr(upper(a.descr),1,3) = SUBSTR(c.term, 1, 3)
THEN 4
END
FETCH FIRST ROW ONLY
) l
ON (1 = 1)
WHERE caseid = '2332534534';
BEGIN
for t_case in c1
loop
/* ... */
v_ao := t_case.ao;
/* .. */
end loop;
commit;
exception
when others then
log_error(0, 'INSERT_case - exception outside' || SQLERRM || ' code ' || SQLCODE, 1);
commit; -- Really commit after an exception?
END;
/

Well,
if you declare additional variables and run all select statements (each of them returning its own v_ao# value) and
apply the max function so that query wouldn't end up with NO_DATA_FOUND; it'll return NULL instead
SQL> select 1 from dual where 1 = 2;
no rows selected
SQL> select max(1) From dual where 1 = 2;
MAX(1)
----------
SQL>
then you wouldn't have to enclose each select into its own begin-exception-end block
use nested CASE expressions to return the final result
Something like this:
declare
v_ao1 number;
v_ao2 number;
v_ao3 number;
v_ao4 number;
v_ao number;
begin
for t_case in c1 loop
select max(id) into v_ao1 from lkp_ao where upper(descr) = upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode));
select max(id) into v_ao2 from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,5)||'%' and rownum=1;
select max(id) into v_ao3 from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,4)||'%' and rownum=1;
select max(id) into v_ao4 from lkp_ao where substr(upper(descr),1,5) like substr(upper(nvl(substr(t_case.oCode, 1, instr(t_case.oCode,',')-1), t_case.oCode)),1,3)||'%' and rownum=1;
v_ao := case when nvl(v_ao1, 0) = 0 then
case when nvl(v_ao2, 0) = 0 then
case when nvl(v_ao3, 0) = 0 then
case when nvl(v_ao4, 0) = 0 then null
else v_ao4
end
else v_ao3
end
else v_ao2
end
else v_ao1
end;
end loop;
end;

Sometimes, in such cases, it's better to get length of longest match. One of the variants for this is to use UTL_RAW.BIT_XOR:
with
t(s) as (
select 'AAAABB' from dual union all
select 'AAAABC' from dual union all
select 'AAABBC' from dual union all
select 'BBBXXXX' from dual union all
select 'BBBYYY' from dual
)
,search_strings(str) as (
select 'AAAAB' from dual union all
select 'BBBZZZ' from dual
)
select
t.s,s.str,
utl_raw.bit_xor(utl_raw.cast_to_raw(t.s),utl_raw.cast_to_raw(s.str)) s_xor,
length(
regexp_substr(
utl_raw.bit_xor(utl_raw.cast_to_raw(t.s),utl_raw.cast_to_raw(s.str))
,'^(00)+'
)
)/2 as n_matches
from t, search_strings s
;
Results:
S STR S_XOR N_MATCHES
------- ------ ------------------------------ ----------
AAAABB AAAAB 000000000042 5
AAAABC AAAAB 000000000043 5
AAABBC AAAAB 000000030043 3
BBBXXXX AAAAB 030303191A5858
BBBYYY AAAAB 030303181B59
AAAABB BBBZZZ 0303031B1818
AAAABC BBBZZZ 0303031B1819
AAABBC BBBZZZ 030303181819
BBBXXXX BBBZZZ 00000002020258 3
BBBYYY BBBZZZ 000000030303 3
10 rows selected.
As you can see, BIT_XOR returns '00' for equal chars so you can count a number of 00 in it. Column n_matches returns this number of matched symbols

Related

Too many values inside a Where

I want to get all products inside an order. I give to my procedure the ID of the order and then I want to list all of them.
Here is the database scheme of the tables I use in this procedure:
And here is the procedure:
CREATE OR REPLACE PROCEDURE exercitiu6(v_ID_Comanda Comanda.ID_Comanda%TYPE) AS
TYPE produse IS TABLE OF Produs%ROWTYPE INDEX BY PLS_INTEGER;
p produse;
TYPE imbricat IS TABLE OF ProduseComanda.ID_Produs%TYPE;
imbricat_produse imbricat:= imbricat();
prod Produs%ROWTYPE;
i number:=0;
j number:=0;
BEGIN
SELECT ID_Produs BULK COLLECT INTO imbricat_produse FROM ProduseComanda
WHERE ID_Comanda = v_ID_Comanda;
FOR i IN imbricat_produse.FIRST..imbricat_produse.LAST LOOP
SELECT ID_Produs, nume, pret INTO prod FROM Produs
WHERE ID_Produs = imbricat_produse(i);
p(j):= prod;
j:= j + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE('Comanda cu ID-ul ' || v_ID_Comanda || ' contine urmatoarele produse: ');
FOR j IN p.FIRST..p.LAST LOOP
DBMS_OUTPUT.PUT_LINE(p(j).nume);
END LOOP;
END;
I get the Error Sql Statement ignored; too many values on this line:
WHERE ID_Comanda = v_ID_Comanda;
How do I solve this error?
Use a JOIN and a single cursor:
CREATE OR REPLACE PROCEDURE exercitiu6(
v_ID_Comanda ProduseComanda.ID_Comanda%TYPE
)
AS
BEGIN
DBMS_OUTPUT.PUT_LINE('Comanda cu ID-ul ' || v_ID_Comanda || ' contine urmatoarele produse: ');
FOR i IN (
SELECT p.nume
FROM ProduseComanda c
INNER JOIN Produs p
ON p.ID_Produs = c.ID_Produs
WHERE c.ID_Comanda = v_ID_Comanda
)
LOOP
DBMS_OUTPUT.PUT_LINE(i.nume);
END LOOP;
END;
/
Then, for the sample data:
CREATE TABLE produsecomanda (ID_Produs, ID_Comanda) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL;
CREATE TABLE produs (ID_Produs, nume) AS
SELECT 1, 'Alice' FROM DUAL UNION ALL
SELECT 2, 'Beryl' FROM DUAL UNION ALL
SELECT 3, 'Carol' FROM DUAL;
Then:
BEGIN
DBMS_OUTPUT.ENABLE();
exercitiu6(1);
END;
/
Outputs:
Comanda cu ID-ul 1 contine urmatoarele produse:
Alice
Beryl
Carol
If you want to fix your code then the error is not with the WHERE clause but with the mismatch between the number of columns in the SELECT clause and the INTO clause. To fix it you need to use SELECT * INTO ... rather than naming all the columns when you are working with %ROWTYPE variables:
CREATE OR REPLACE PROCEDURE exercitiu6(
v_ID_Comanda ProduseComanda.ID_Comanda%TYPE
)
AS
TYPE produse IS TABLE OF Produs%ROWTYPE INDEX BY PLS_INTEGER;
p produse;
TYPE imbricat IS TABLE OF ProduseComanda.ID_Produs%TYPE;
imbricat_produse imbricat;
i number:=0;
j number:=0;
BEGIN
SELECT ID_Produs
BULK COLLECT INTO imbricat_produse
FROM ProduseComanda
WHERE ID_Comanda = v_ID_Comanda;
FOR i IN imbricat_produse.FIRST..imbricat_produse.LAST LOOP
j:= j + 1;
SELECT *
INTO p(j)
FROM Produs
WHERE ID_Produs = imbricat_produse(i);
END LOOP;
DBMS_OUTPUT.PUT_LINE('Comanda cu ID-ul ' || v_ID_Comanda || ' contine urmatoarele produse: ');
FOR j IN p.FIRST..p.LAST LOOP
DBMS_OUTPUT.PUT_LINE(p(j).nume);
END LOOP;
END;
/
fiddle

Converting Merge clause with Bulk collect/FORALL in pl/sql

I wrote a procedure where the data gets updated/inserted simultaneously to the destination table from source table. The procedure is working fine for less no of records, but when i try to execute more records its taking more time to perform the operation.
Can we convert merge clause with bulk collect where the logic remains same ? i dint find any useful resources.
I have attached my merge procedure .
create or replace PROCEDURE TEST1 (
p_array_size IN NUMBER
) IS
CURSOR dtls IS SELECT DISTINCT
account_num
FROM
table1
WHERE
rprtd_till_dt = (
SELECT
dt - 1
FROM
dates
WHERE
id = 'odc'
);
TYPE data_tbl IS TABLE OF dtls%rowtype;
data data_tbl;
BEGIN
DECLARE
v_noofDays NUMBER:=0;
currentDt DATE;
BEGIN
SELECT dt INTO currentDt FROM dates WHERE id = 'odc';
BEGIN
OPEN dtls;
LOOP
FETCH dtls BULK COLLECT INTO data LIMIT p_array_size;
EXIT WHEN data.COUNT = 0;
FOR i IN 1..data.COUNT
LOOP
IF(TRUNC(data(i).creation_dt,'MM') = TRUNC(currentDt,'MM')) THEN
v_noofDays := currentDt - 1 - data(i).creation_dt;
ELSE
v_noofDays := currentDt - TRUNC(currentDt,'MM');
END IF;
MERGE INTO table1 updtbl USING ( SELECT
d.*
FROM
table2 d,
(
SELECT
b.prdct_id,
FROM
table3 a,
table2 b
WHERE
a.ir_id = b.ir_id
AND a.price_component_id = b.price_component_id
AND a.financial_institution_id = b.financial_institution_id
GROUP BY
b.prdct_id,
) e
WHERE
d.prdct_id = e.prdct_id
AND d.bndng_typ = data(i).bndng_typ
AND d.bndng_val = data(i).bndng_val
AND d.financial_institution_id = data(i).financial_institution_id
AND d.prdct_id = data(i).prdct_id
AND d.prdct_sub_id = data(i).prdct_sub_id
AND d.instrmnt_id = data(i).instrmnt_id
)
inp ON (
updtbl.POS_NUM = data(i).POS_NUM
AND updtbl.POS_TYPE = data(i).POS_TYPE
AND updtbl.PRICE_COMPONENT_ID = inp.PRICE_COMPONENT_ID
AND updtbl.RPRTD_TILL_DT = data(i).RPRTD_TILL_DT
)
WHEN NOT MATCHED THEN
INSERT VALUES (
data(i).loan_account_num,
inp.ir_id,
inp.price_component_id,
)
WHEN MATCHED THEN
update SET SEQ_NUM=1,
NET_INTRST_AMT=round(data(i).curr_loan_bal*inp.price_component_value*v_noofDays/36000,2),
DM_BTID=200
WHERE SEQ_NUM=2;
COMMIT;
END LOOP;
END LOOP;
CLOSE dtls;
END;
END;
END TEST1;
/
If anyone can help me to guide the syntax on how to achieve the above procedure using bulk collect will be helpful.
I know its a bit late, but use the following for future if you haven't solved it yet
drop table projects;
create table projects (
proj_id integer not null primary key,
proj_title varchar2(20)
);
insert into projects (proj_id, proj_title) values (1, 'Project One');
insert into projects (proj_id, proj_title) values (2, 'Project Two');
commit;
select *
from projects;
declare
type varray_t is varray(2) of projects%rowtype;
arr varray_t;
begin
with test_data as (select 2 as proj_id, 'New Project Two' as proj_title from dual
union all select 3 as proj_id, 'New Project Three' as proj_title from dual)
select proj_id, proj_title
bulk collect into arr
from test_data;
forall i in arr.first .. arr.last
merge into projects
using (select arr(i).proj_id as proj_id,
arr(i).proj_title as proj_title
from dual) mrg
on (projects.proj_id = mrg.proj_id)
when matched then update set projects.proj_title = mrg.proj_title
when not matched then insert (proj_id, proj_title) values (mrg.proj_id, mrg.proj_title);
dbms_output.put_line(sql%rowcount || ' rows merged');
commit;
end;
I hope this will give you kind of idea. Avoid the copy and paste and check the syntax.
create or replace PROCEDURE TEST1 (
p_array_size IN NUMBER
) IS
CURSOR dtls IS SELECT DISTINCT
account_num
FROM
table1
WHERE
rprtd_till_dt = (
SELECT
dt - 1
FROM
dates
WHERE
id = 'odc'
);
TYPE data_tbl IS TABLE OF dtls%rowtype;
data data_tbl;
BEGIN
DECLARE
v_noofDays NUMBER:=0;
currentDt DATE;
BEGIN
SELECT dt INTO currentDt FROM dates WHERE id = 'odc';
BEGIN
OPEN dtls;
LOOP
FETCH dtls BULK COLLECT INTO data LIMIT p_array_size;
EXIT WHEN data.COUNT = 0;
FORALL rec in data.first .. data.last
MERGE INTO table1 updtbl USING (
SELECT
d.* FROM
table2 d,(
SELECT
b.prdct_id
FROM
table3 a,
table2 b
WHERE
a.ir_id = b.ir_id
AND a.price_component_id = b.price_component_id
AND a.financial_institution_id = b.financial_institution_id
GROUP BY
b.prdct_id
) e
WHERE
d.prdct_id = e.prdct_id
AND d.bndng_typ = data(rec).bndng_typ
AND d.bndng_val = data(rec).bndng_val
AND d.financial_institution_id = data(rec).financial_institution_id
AND d.prdct_id = data(rec).prdct_id
AND d.prdct_sub_id = data(rec).prdct_sub_id
AND d.instrmnt_id = data(rec).instrmnt_id
)
inp ON (
updtbl.POS_NUM = data(rec).POS_NUM
AND updtbl.POS_TYPE = data(rec).POS_TYPE
AND updtbl.PRICE_COMPONENT_ID = data(rec).PRICE_COMPONENT_ID
AND updtbl.RPRTD_TILL_DT = data(rec).RPRTD_TILL_DT
)
WHEN NOT MATCHED THEN
INSERT VALUES (
data(rec)
)
WHEN MATCHED THEN
update SET SEQ_NUM=1,
NET_INTRST_AMT=round(data(rec).curr_loan_bal*inp.price_component_value*v_noofDays/36000,2),
DM_BTID=200
WHERE SEQ_NUM=2;
END LOOP;
CLOSE dtls;
END;
END;
END TEST1;
Merge is always better than forall for atomic updates.
A simplistic use case is
https://ograycoding.wordpress.com/2012/10/13/oracle-merge-v-bulk-collect-and-forall/

How to properly use an oracle in the request, the "If" and "LIKE" condition

i need help with the code, i have two pieces of code that write two variables: v_responsible_job and v_responsible.I would like to combine these queries into one and add a condition if.
If the answer to this query is "ABC", then the first query must be completed, and if not,the second query:
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=4ce28fad7e33f00c5538e49943ae7dechere is the demo data
SELECT NAME FROM data_separators WHERE id = way.DS_ID
example answer:
NAME
ABC Info
Scool
offise
ABC SHOP
first call :
BEGIN
SELECT full_name, job INTO v_responsible, job_id
FROM physical_persons
WHERE id IN (SELECT physical_person
FROM data_separators WHERE id = way.DS_ID) AND rownum = 1;
SELECT name
INTO v_responsible_job
FROM jobs
WHERE id = job_id AND
rownum = 1;
if v_responsible_job is not null then
if length(v_responsible_job) > 0 then
v_responsible_job := ', '|| v_responsible_job;
end if;
end if;
EXCEPTION WHEN OTHERS THEN
v_responsible_job := '';
END;
second call :
BEGIN
SELECT full_name, job
INTO v_responsible, job_id
FROM physical_persons
WHERE id IN (SELECT RESPONSIBLE
FROM ADRESSES
WHERE name = p1.name) AND
rownum = 1;
SELECT name
INTO v_responsible_job
FROM jobs
WHERE id = job_id AND
rownum = 1;
if v_responsible_job is not null then
if length(v_responsible_job) > 0 then
v_responsible_job := ', '|| v_responsible_job;
end if;
end if;
EXCEPTION WHEN OTHERS THEN
v_responsible_job := '';
END;
my varianr answer , but his don't work(
BEGIN
select count(*) into v_count_word FROM data_separators WHERE id = way.DS_ID and NAME LIKE '%ABC%';
select count(RESPONSIBLE) into v_count_respon FROM ADRESSES WHERE name = p1.name;
if v_count_word > 0 then
SELECT full_name, job INTO v_responsible, job_id FROM physical_persons WHERE id IN (SELECT physical_person FROM data_separators WHERE id = way.DS_ID) AND rownum = 1;
SELECT name INTO v_responsible_job FROM jobs WHERE id = job_id AND rownum = 1;
if v_responsible_job is not null then
if length(v_responsible_job) > 0 then
v_responsible_job := ', '|| v_responsible_job;
end if;
end if;
end if;
if v_count_respon > 0 and v_count_word = 0 then
SELECT full_name, job INTO v_responsible, job_id FROM physical_persons WHERE id IN (SELECT RESPONSIBLE
FROM ADRESSES
WHERE name = p1.name) AND rownum = 1;
SELECT name INTO v_responsible_job FROM jobs WHERE id = job_id AND rownum = 1;
if v_responsible_job is not null then
if length(v_responsible_job) > 0 then
v_responsible_job := ', '|| v_responsible_job;
end if;
end if;
end if;
if v_count_respon = 0 and v_count_word = 0 then
SELECT full_name, job INTO v_responsible, job_id FROM physical_persons WHERE id IN (SELECT physical_person FROM data_separators WHERE id = way.DS_ID) AND rownum = 1;
SELECT name INTO v_responsible_job FROM jobs WHERE id = job_id AND rownum = 1;
if v_responsible_job is not null then
if length(v_responsible_job) > 0 then
v_responsible_job := ', '|| v_responsible_job;
end if;
end if;
end if;
EXCEPTION WHEN OTHERS THEN
v_responsible_job := '-';
END;
Combine the first two queries using EXISTS and then use LISTAGG to aggregate the values:
DECLARE
v_responsible_jobs VARCHAR2(4000);
BEGIN
SELECT LISTAGG( name, ',' ) WITHIN GROUP ( ORDER BY name )
INTO v_responsible_jobs
FROM jobs j
WHERE EXISTS(
SELECT 1
FROM physical_persons pp
WHERE pp.job_id = j.job_id
AND id IN (
SELECT physical_person
FROM data_separators
WHERE id = way.DS_ID
)
);
END;
/
An other way would be to use BULK COLLECT INTO a collection data type and then to iterate over the collection concatenating the string; but that is more complicated than using LISTAGG to do all the work for you.

using dynamic sql to create column for select statement

I'm writing a stored procedure for paginated results and this result can be ordered by certain values. I did have a switch case in a select statement but because it was trying to do an orderby on rownum it was very slow.
Now I am trying to use dyanmic sql to build the query outside the select but I don't know if what I am doing is possible.
Here is my SQL in Oracle SQL Developer:
create or replace PROCEDURE Sp_tsa_trainees_pagination (
schemeid IN INT,
searchval IN VARCHAR2,
pagesize IN INT DEFAULT 20,
currentpage IN INT DEFAULT 1,
--orderby IN VARCHAR2,
cursor_ OUT SYS_REFCURSOR)
AS
-- LOCAL VARIABLES
totalcount INT;
numberofpages INT;
startposition NUMBER;
endposition NUMBER;
orderby VARCHAR2(100) := 'surname asc' ;
dynamic_query VARCHAR(255) := 'row_number() over (order by t.SURNAME DESC, t.FORENAMES DESC) AS rnum';
BEGIN
-- Get total number of trainees in scheme
select COUNT(t.ORG_REGISTRATION_ID)
into totalcount FROM v_trainee t
where t.ORG_REGISTRATION_ID = schemeid
AND t.status = 'A' and LOWER(t.trainee_name) like '%' || LOWER(searchval) || '%';
-- calculate number of pages in the pagination by dividing total number of records by how many to display for each page
numberofpages := totalcount / pagesize;
-- get start position by multiplying number of records to display for each page by current page
startposition := pagesize *( currentpage-1);
-- add calculated start position by number of records to display to get end position
endposition := startposition + pagesize;
CASE orderby
WHEN 'surname desc' THEN dynamic_query := 'row_number() over (order by t.SURNAME DESC, t.FORENAMES DESC) AS rnum';
WHEN 'surname asc' THEN dynamic_query := 'row_number() over (order by t.SURNAME ASC, t.FORENAMES ASC) AS rnum';
END CASE;
OPEN cursor_ FOR
Select * from
(
SELECT
-- order by based on selection
dynamic_query rnum,
t.ORG_REGISTRATION_ID SearchId,
t.FORENAMES Forenames,
t.FORENAME Forename,
t.SURNAME Surname,
t.person_id PersonId,
t.trainee_name TraineeName,
t.STATUS Status,
t.IPD_ANNUAL_REVIEW_DATE AnnualReviewDate,
t.ANNUAL_REVIEW_STATUS AnnualReviewStatus,
t.payment_received PaymentRecieved,
t.TRAINEE_ID TraineeId,
t.IPD_SIGNUP_DATE IpdSignupDate,
t.START_DATE StartDate,
t.END_DATE EndDate,
t.LENGTH_ON_SCHEME LengthOnScheme,
t.EMPLOYEE_NUMBER EmploymentNumber,
t.SELECTED_LEVEL SelectedLevel,
t.SELECTED_LEVEL_DESCRIPTION SelectedLevelDescription,
t.ELIGIBLE_LEVEL EligibleLevel,
t.ELIGIBLE_LEVEL_DESCRIPTION EligibleLevelDescription,
sce.FORENAMES SceForenames,
sce.FORENAME SceForename,
sce.SURNAME SceSurname,
sce.mentor_name SceName,
sce.EMPLOYEE_NUMBER SceEmployeeNumber,
de.FORENAMES DeForenames,
de.FORENAME DeForename,
de.SURNAME DeSurname,
de.mentor_name DeName,
de.EMPLOYEE_NUMBER DeEmployeeNumber,
t.COMPLETED_ATTRIBUTE_LEVELS CompletedAttributeLevels,
t.ATTRIBUTE_LEVEL_COUNT AttributeLevelCount,
-- get percentage
CASE t.ATTRIBUTE_LEVEL_COUNT
WHEN 0 THEN 0
ELSE
COMPLETED_ATTRIBUTE_LEVELS / t.ATTRIBUTE_LEVEL_COUNT * 100
END percentage,
DECODE(F_ISTRAINEEGROUPMEMBER(t.ORG_REGISTRATION_ID, 'S', t.person_id),'Y','N','Y') WithoutTsaGroup,
orr.status SchemeStatus,
(select count(*) from TRAINING_GROUP_TRAINEE tgt where tgt.trainee_id = t.TRAINEE_ID) NUMBER_OF_GROUPS,
TotalCount
FROM v_trainee t
INNER JOIN org_registration orr ON t.ORG_REGISTRATION_ID = orr.id
LEFT OUTER JOIN v_mentor sce ON t.sce_id = sce.MENTOR_ID
LEFT OUTER JOIN v_mentor de ON t.de_id = de.MENTOR_ID
where t.ORG_REGISTRATION_ID = schemeid AND t.status = 'A'
and LOWER(t.trainee_name) like '%' || LOWER(searchval) || '%'
)
where rnum >= startposition and rnum <= endposition;
END;
I want to use this variable with the assigned sql:
dynamic_query rnum,
But when I execute the stored procedure I get this error:
ORA-01722: invalid number ORA-06512: at
"db.SP_TSA_TRAINEES_PAGINATION", line 46 ORA-06512: at line 13
So basically my question is can I assign a SQL to VARCHAR2 and then use it in a select statement dynamically.
You may need dynamic SQL for this. For example:
create or replace procedure testDyn(n in number, C OUT SYS_REFCURSOR) is
vDynamicPart varchar2(1000);
vSQl varchar2(1000);
begin
--
if (n = 1) then
vDynamicPart := 'count(1)';
else
vDynamicPart := 'count(null)';
end if;
--
vSQl := 'select ' || vDynamicPart || ' from dual';
open C for vSQl;
end;
If you call it
declare
n1 number;
n2 number;
C1 SYS_REFCURSOR;
C2 SYS_REFCURSOR;
begin
testDyn(1, C1);
testDyn(2, C2);
fetch C1 into n1;
fetch C2 into n2;
dbms_output.put_line('n1: ' || n1);
dbms_output.put_line('n2: ' || n2);
end;
you get:
n1: 1
n2: 0

How to get Numbers in number range by PL-Sql .?

here is my serial table.it has more than 1000 records.its with start number and end number.but between numbers not exist.
i need to add all number [start/between & end numbers] records in another temp table number by number
like below
EXIST TABLE
select concat(CARD_BULK_CODE,start_serial) startserial,concat(CARD_BULK_CODE,end_serial) endserial
from TSR_BULK_CARD_SERIALS
---------------------------
STARTSERIAL ENDSERIAL |
---------------------------
18126944 18126946 |
18141101 18141122 |
15150722 15150729 |
19069303 19069317 |
---------------------------
REQUIRED TABLE
-----------
SERIAL_NO |
-----------
18126944
18126945
18141101
18141102
....
-----------
seem its need pl-sql to implement this.
please help me to sort out this issue
I tried with below query with the help of dual.
but its very slow and not yet got results :-) running more than 1 Hour
select distinct concat(t.CARD_BULK_CODE,t.START_SERIAL)+level-1 SERIAL
from TSR_BULK_CARD_SERIALS t, dual
connect by level-1<=(concat(t.CARD_BULK_CODE,t.END_SERIAL ))-concat(t.CARD_BULK_CODE,t.START_SERIAL)
order by 1
EDIT :
Dear Alen & Dba.i tried with your ones and below error occured.
DECLARE
l_st NUMBER;
l_en NUMBER;
BEGIN
FOR rec IN (select concat(card_bulk_code, start_serial) startserial,concat(card_bulk_code, end_serial) endserial from tsr_bulk_card_serials)
LOOP
l_st := rec.startserial;
l_en := rec.endserial;
FOR rec1 IN l_st..l_en
LOOP
INSERT INTO temp(serial_no) values(rec1);
END LOOP;
END LOOP;
COMMIT;
END;
Error at line 1
ORA-01426: numeric overflow
ORA-06512: at line 9
Script Terminated on line 1.
One way to do it without resorting to plsql
WITH ranges AS
(
SELECT CONCAT(CARD_BULK_CODE, start_serial) startserial,
CONCAT(CARD_BULK_CODE, end_serial) endserial
FROM TSR_BULK_CARD_SERIALS
),
numbers(n) AS (
SELECT 0 n
FROM dual
UNION ALL
SELECT n + 1
FROM numbers
WHERE n <=
(
SELECT MAX(endserial - startserial)
FROM ranges
)
)
SELECT t.startserial + n.n SERIAL_NO
FROM ranges t JOIN numbers n
ON n.n <= t.endserial - t.startserial
ORDER BY SERIAL_NO
Here is SQLFiddle demo
Just write some PL/SQL - iterate through your table and insert rows in the temp table.
declare
l_start number;
l_end number;
begin
for r_rec in (select to_number(concat(card_bulk_code, start_serial)) startserial
, to_number(concat(card_bulk_code, end_serial)) endserial
from tsr_bulk_card_serials )
loop
l_start := r_rec.startserial;
l_end := r_rec.endserial;
for l_i in l_start..l_end loop
insert into your_temp_table;
end loop;
end loop;
end;
Try like this,
WITH t(ST, EN) AS
(
SELECT 18126944, 18126946 FROM dual
UNION
SELECT 18141101, 18141122 FROM dual
UNION
SELECT 15150722, 15150729 FROM dual
UNION
SELECT 19069303 , 19069317 FROM dual
)
SELECT DISTINCT st + LEVEL -1
FROM t
CONNECT BY LEVEL <= (SELECT en - st + 1 FROM DUAL)
ORDER BY 1;
/
Try something like this for PL/SQL,
DECLARE
l_st NUMBER;
l_en NUMBER;
BEGIN
FOR rec IN (SELECT * FROM t)
LOOP
l_st := rec.st;
l_en := rec.en;
FOR rec1 IN l_st..l_en
LOOP
INSERT INTO <your_tab>;
END LOOP;
END LOOP;
COMMIT;
END;
DECLARE
l_st NUMBER (20);
l_en NUMBER (20);
testnum NUMBER (4);
BEGIN
FOR rec IN (SELECT CONCAT (card_bulk_code, start_serial) startserial,CONCAT (card_bulk_code, end_serial) endserial FROM tsr_bulk_card_serials)
LOOP
l_st := TO_NUMBER (rec.startserial);
l_en := TO_NUMBER (rec.endserial);
testnum := l_en - l_st;
DBMS_OUTPUT.put_line (l_st);
DBMS_OUTPUT.put_line (l_en);
IF l_st < l_en
THEN
FOR rec1 IN 0 .. testnum
LOOP
l_st := l_st + 1;
INSERT INTO temp(serial_no) VALUES (l_st);
END LOOP;
END IF;
END LOOP;
COMMIT;
END;
above code helped me to sorted my issue
thanks all :-)