I'm planning to write a pl sql
pseudo code
procedure a gets all records from emp and is passed to procedure b
procedure b getting called inside from a , is transforming the data from emp , each emp record is traversed 6 times and storing the data in an array
inside procedure b , i used varray (size of 2000 to 2 mil) and also associative array , but it is always failing after handling 10 -15 records .i.e. after handling it for 60 -90 records
i am reusing the same index for storing my data but nothing worked i.e. 1 to 6
I am not sure what i am doing wrong , please assist .
create or replace PROCEDURE query_emp ( bill_cycle_ip in number,
no_of_recs in number ) AS
TYPE billAcnt IS REF CURSOR RETURN emp%ROWTYPE;
ba_rec emp%ROWTYPE;
b_a billAcnt;
total_num_of_rec number ;
BEGIN
OPEN b_a FOR
select * from
(select * from emp where bill_cycle =1 and ban_status in ('O' ,'S')
union
select * from emp where bill_cycle =1 and ban_status in ('N')
and STATUS_LAST_DATE >= sysdate -30
)
where ban not in (Select ban from temp_ba)
and rownum <no_of_recs;
LOOP
FETCH b_a INTO ba_rec;
EXIT WHEN b_a%NOTFOUND;
create_final_snapshot (ba_rec);
END LOOP;
CLOSE b_a;
END;
create or replace procedure create_final_snapshot ( ba_rec IN emp%ROWTYPE ) AS
cur_bl_seq_number number ;
bl_seq_number_minus_six number ;
type total_due_amt is table of number INDEX BY PLS_INTEGER ;
nt total_due_amt := total_due_amt();
b number := 1 ;
BEGIN
cur_bl_seq_number :=ba_rec.BL_CUR_BILL_SEQ_NO ;
if ba_rec.BL_CUR_BILL_SEQ_NO-5 <=1 then
bl_seq_number_minus_six:=1;
else
bl_seq_number_minus_six:=ba_rec.BL_CUR_BILL_SEQ_NO-5;
end if ;
for a in bl_seq_number_minus_six..cur_bl_seq_number
LOOP
--nt.extend();
select xyz into nt(b) from emp_2 where ban =ba_rec.ban and bill_seq_no =a ;
dbms_output.put_line('total_due_amt_list sun= ' ||ba_rec.ban ||' ' || nt(b) || ' bill seq no ' ||a ||' b ' || b );
b := b +1;
END loop;
b:=1;
insert into temp_ba
values (nt(1) ,nt(2), nt(3),nt(4),nt(5),nt(6) );
END;
Related
I am looking for a way to create collection that would have one additional column that the source table/view columns referred by %ROWTYPE for simplicity.
TYPE my_type IS RECORD ( mode varchar2(10), some_table%ROWTYPE );
Goal:
I want to compare a table and its source view with a two-way MINUS operation and read source view once:
select * from (
select 'INSERT' as mode, a.* from (
select ... from aView
MINUS
select ... from aTable ) a
UNION ALL
select 'DELETE' as mode, b.* from (
select ... from aTable
MINUS
select ... from aView) b
)
I am starting to think of creating global temporary tables (GTTs) for INSERT and DELETE mode performed with INSERT ALL, but I have 25 tables, so that will add 50 GTTs :-/
INSERT ALL
WHEN mode=INSERT THEN INTO inserts_GTT01_tbl VALUES(...)
WHEN mode=DELETE THEN INTO deletes_GTT01_tbl VALUES(...)
SELECT * FROM two-way-minus-view
I hope I understood correctly your predicament, although it is possible I might miss something. If I got your idea correctly, you want to operate over a set of tables and generate the records to delete and the ones to insert based on the minus-two-way.
I would go for global temporary tables, using commit on preserve rows, but I would centralize the code in a procedure
First a control table
create table control_process
( id_time timestamp default systimestamp ,
source_table varchar2(128),
source_view varchar2(128),
rows_processed number,
exit_code number,
error_message varchar2(400)
);
Procedure for GTTs using input parameters table and view
It is very important that you adapt the code below to your needs:
If the columns in the table and view are not the same, you must adapt the query behind the GTT creation to use whatever is necessary. If you want to do it dynamically, you must create another cursor to match the columns between the table and view used as input parameter.
In order to delete/insert you have to compare the GTT with the original table. That is why I have the cursor with the columns that I use to create the dynamic construction, both for insert and delete.
I did not have a place to verify the code, so please be aware that could be typos or errors on it.
The control table is optional, but allows you to have a kind of place where you stored the results for each table/view
This solution allows you to use it for all the tables and views in one single place.
the materialize hint will help you as you read once the view and the table in the construction of the minus-two-way.
Having said that, that could be an approach ( you have to adapt it to your needs )
create or replace procedure pr_generate_rows ( psourcetable in varchar2 , psourceview in varchar2 )
is
vddl clob;
vdml clob;
vsql clob;
vcode pls_integer;
verrm varchar2(300);
out_string varchar2(128);
cursor c_tab_columns
is
select column_name, count(*) over () tot_rows
from all_tab_columns where table_name = psourcetable and owner = 'MY_SCHEMA'
order by column_id;
begin
vddl := ' create global temporary table gtt_'||psourcetable||' on commit preserve rows
as
with x
as
( select /*+materialize */ * from '||psourceview||'
),
y as
( select /*+materialize */ * from '||psourcetable||'
)
select * from (
select ''INSERT'' as mode, a.* from
(
select ... from x
MINUS
select ... from y
) a
UNION ALL
select ''DELETE'' as mode, b.* from (
select ... from y
MINUS
select ... from x ) b
)
' ;
execute immediate vddl;
vdml := ' insert into control_process ( source_table, source_view , rows_processed , exit_code )
select '''||psourcetable||''' as source_table ,
'''||psourceview||''' as source_view ,
( select count(*) from gtt_'||psourcetable||' ) as rows_processed ,
0 as exit_code from dual
' ;
execute immediate vdml ;
-- Perform Insert and Delete over final table
-- Delete
vsql := ' delete from '||psourcetable||'
where exists
( select 1 from gtt_'||psourcetable||' a join '||psourcetable||' b
on ( ';
for item in c_tab_columns
loop
out_string := item.COLUMN_NAME;
if c_tab_columns%rowcount = 1
then
vexpression := ' a.'||out_string||' = b.'||out_string||' and ' ;
dbms_lob.append(vsql,vexpression);
dbms_lob.append(vsql,''||chr(10)||'');
elsif c_tab_columns%rowcount < item.tot_rows then
vexpression := ' a.'||out_string||' = b.'||out_string||' and ' ;
dbms_lob.append(vsql,vexpression);
dbms_lob.append(vsql,''||chr(10)||'');
else
vexpression := ' a.'||out_string||' = b.'||out_string||' ' ;
dbms_lob.append(vsql,vexpression);
dbms_lob.append(vsql,''||chr(10)||'');
end if;
end loop;
dbms_lob.append ( vsql, ')' );
dbms_lob.append(vsql,''||chr(10)||'');
dbms_lob.append ( vsql, ' where a.mode = ''DELETE'' ');
dbms_lob.append ( vsql, ' ) ');
execute immediate vsql;
-- Insert
vsql := ' insert /*+ append */ into '||psourcetable||' a
select ' ;
for item in c_tab_columns
loop
out_string := item.COLUMN_NAME;
if c_tab_columns%rowcount = 1
then
vexpression := ' b.'||out_string||' , ' ;
dbms_lob.append(vsql,vexpression);
dbms_lob.append(vsql,''||chr(10)||'');
elsif c_tab_columns%rowcount < item.tot_rows then
vexpression := ' b.'||out_string||' , ' ;
dbms_lob.append(vsql,vexpression);
dbms_lob.append(vsql,''||chr(10)||'');
else
vexpression := ' b.'||out_string||' ' ;
dbms_lob.append(vsql,vexpression);
dbms_lob.append(vsql,''||chr(10)||'');
end if;
end loop;
dbms_lob.append(vsql,'from gtt_'||psourcetable||' b where b.mode = ''INSERT'' ');
execute immediate vsql;
exception
when others then
vcode := sqlcode;
verrm := substr(sqlerrm, 1, 300);
vdml := ' insert into control_process ( source_table, source_view , rows_processed , exit_code , error_message )
values (
'''||psourcetable||''' ,
'''||psourceview||''' ,
0,
vcode,
'''||verrm||'''
) ' ;
execute immediate vdml;
commit;
raise;
end;
/
Any questions, let me know.
I have a procedure which is defined in oracle with some set of queries. Now i need to add a cursor in between. i have already defined cursor in the procedure and it compiled successfully. But whenever i am going to run the procedure, it only runs the query part till the cursor came.
Plz anyone help me
Thanks in advance.
Here is the Prcedure:
create or replace PROCEDURE REPORT_GENERATE_PROC_REG
(
reportyyyymm number,
report_region VARCHAR2,--NR/WR/ETC
meetingdate date,--date
meetingdesc VARCHAR2,--desc
generateby VARCHAR2,--userid
companycode VARCHAR2,--userid
ret_int OUT INT, --- 0 if error report number if success
ret_msg OUT VARCHAR2 -- error message if error blank if success
)
AS
final_report_id INTEGER :=0;
total_column_no INTEGER :=0;
V_NO INTEGER;
BEGIN
select max(rid) into final_report_id from hindi_report_gen_new; --max rid is stored in final_report_id
final_report_id := final_report_id + 1;
insert into hindi_report_gen_new
values(final_report_id,
'Report generated for '|| companycode ||'for Quarter ending in '||
reportyyyymm ||'\n Meeting Held on '|| meetingdate||'\n Desc is '||
meetingdesc, reportyyyymm,meetingdate,meetingdesc, generateby,sysdate,
companycode);
commit;
-- inserting the data for reports
DECLARE
CURSOR ADI_CURR IS
SELECT DISTINCT ROW_NO FROM HINDI_TEST WHERE REPORT_NO=final_report_id and row_no not in(0,37,38);
BEGIN
OPEN ADI_CURR;
LOOP
FETCH ADI_CURR INTO V_NO;
insert into hindi_region_report
select a.report_no,a.row_no,c.STATE_CORD,a.value from hindi_test a join hindi_trn_report_header b on a.column_no=b.REPORT_NO join hindi_mst_cost_centre c on c.cost_centre_code=b.COST_CENTRE_CODE where row_no=v_no and a.report_no=final_report_id;
EXIT WHEN ADI_CURR%NOTFOUND;
END LOOP;
CLOSE ADI_CURR;
END;
-- inserting sum of all rows
insert into test_test
select final_report_id,row_no,column_no,sum(value) from hindi_region_report where report_no=final_report_id and row_no not in(0,22,25,28,37,38)
group by final_report_id,row_no,column_no;
commit;
END REPORT_GENERATE_PROC_REG;
You haven't given any indication of error message or any details that could help pinpoint your issue. I'm not sure why you have an embedded DECLARE...BEGIN...END, why you need a COMMIT in the middle of the code or why you have a COMMIT at all - it should be committed by the calling procedure.
I think the following 'feels' more correct to me;
CREATE OR REPLACE PROCEDURE report_generate_proc_reg(
reportyyyymm NUMBER
, report_region VARCHAR2
, --NR/WR/ETC
meetingdate DATE
, --date
meetingdesc VARCHAR2
, --desc
generateby VARCHAR2
, --userid
companycode VARCHAR2
, --userid
ret_int OUT INT
, --- 0 if error report number if success
ret_msg OUT VARCHAR2 -- error message if error blank if success
)
AS
final_report_id INTEGER := 0;
total_column_no INTEGER := 0;
CURSOR adi_curr
IS
SELECT DISTINCT row_no
FROM hindi_test
WHERE report_no = final_report_id
AND row_no NOT IN (0, 37, 38);
v_no INTEGER;
BEGIN
SELECT MAX(rid) INTO final_report_id FROM hindi_report_gen_new; --max rid is stored in final_report_id
final_report_id := final_report_id + 1;
INSERT INTO hindi_report_gen_new
VALUES ( final_report_id
, 'Report generated for '
|| companycode
|| 'for Quarter ending in '
|| reportyyyymm
|| '\n Meeting Held on '
|| meetingdate
|| '\n Desc is '
|| meetingdesc
, reportyyyymm
, meetingdate
, meetingdesc
, generateby
, SYSDATE
, companycode);
-- inserting the data for reports
FOR rec_adi_curr IN adi_curr
LOOP
INSERT INTO hindi_region_report
SELECT a.report_no
, a.row_no
, c.state_cord
, a.VALUE
FROM hindi_test a
JOIN hindi_trn_report_header b ON a.column_no = b.report_no
JOIN hindi_mst_cost_centre c
ON c.cost_centre_code = b.cost_centre_code
WHERE row_no = v_no
AND a.report_no = final_report_id;
END LOOP;
-- inserting sum of all rows
INSERT INTO test_test
SELECT final_report_id
, row_no
, column_no
, SUM(VALUE)
FROM hindi_region_report
WHERE report_no = final_report_id
AND row_no NOT IN (0 , 22 , 25 , 28 , 37 , 38)
GROUP BY final_report_id, row_no, column_no;
COMMIT;
END report_generate_proc_reg;
The cursor loop could probably use BULK COLLECT but with no indication of data volumes I can't say if it's worth it.
I have problem with the compilation of my stored procedure.
create or replace type CartLine as object (
offeringId OfferingIdList
,productLine varchar2(50)
,equipment char(1)
,installment CHAR(1)
,cartItemProcess varchar2(50)
,minimalPrice decimal
);
create or replace type CartLineType is table of CartLine;
create or replace PROCEDURE GetOfferingRecommendation (
cartLineList IN CartLineType,
user IN UserType,
customer IN CustomerType,
processContext IN ProcessContextType,
recommendation out SYS_REFCURSOR )
IS
prodLine VARCHAR2(20);
prodPrice NUMBER(5,0);
BEGIN
FOR i IN cartLineList.FIRST .. cartLineList.LAST
LOOP
SELECT productLine, minimalPrice
INTO prodLine, prodPrice
FROM TABLE(cartLineList(i));
OPEN recommendation FOR
SELECT CAST(REKOM_ID_SEQ.NEXTVAL AS VARCHAR(10))
||'_'||cp.ID_REKOM_OFERTA
||'_'||TO_CHAR(SYSDATE, 'yyyymmdd') AS recommendationId
,cp.ID_REKOM_OFERTA AS offeringId
,cp.PRIORYTET AS priority
FROM REKOM_CROSS_PROM cp
WHERE cp.LINIA_PROD = prodLine
AND prodPrice BETWEEN cp.CENA_MIN AND cp.CENA_MAX
;
END LOOP;
END GetOfferingRecommendation;
It is not getting compiled cause the following statement is wrong:
SELECT productLine, minimalPrice
INTO prodLine, prodPrice
FROM TABLE(cartLineList(i));
I want to select only single value every all new iteration of my loop.
Can somebody help me to resolve my problem?
-- EDIT 1/9/2018 4:26 PM
According to topic:
How to return result of many select statements as one custom table
I tried to rebuild my procedure.
I created types for test:
create or replace TYPE tst AS OBJECT (
rekom_id varchar2(50)
,rekom_priorytet number(5,4)
);
/
create or replace TYPE tst_list IS TABLE OF tst;
After that, I changed my procedure like below:
CREATE OR REPLACE PROCEDURE GetOfferingRecommendation (cartLineList IN CartLineType, recommendation out SYS_REFCURSOR )
IS
CURSOR CUR_TAB IS SELECT productLine, minimalPrice FROM TABLE(cartLineList);
v_tst tst_list;
BEGIN
FOR i IN CUR_TAB
LOOP
EXECUTE IMMEDIATE 'SELECT tst_list(
CAST(REKOM_ID_SEQ.NEXTVAL AS VARCHAR(10))||''_''||cp.ID_REKOM_OFERTA||''_''||TO_CHAR(SYSDATE, ''yyyymmdd'')
,cp.PRIORYTET)
FROM REKOM_CROSS_PROM cp
WHERE cp.LINIA_PROD ='||i.productLine||' AND '||i.minimalPrice||' BETWEEN cp.CENA_MIN AND cp.CENA_MAX'
BULK COLLECT INTO v_tst;
EXIT WHEN CUR_TAB%NOTFOUND;
FOR REC IN 1 .. v_tst.COUNT
LOOP
PIPE ROW (v_tst(REC));
END LOOP;
END LOOP;
OPEN recommendation FOR SELECT * FROM TABLE(v_tst);
END IF;
END GetOfferingRecommendation;
But I can't compile because error occured: PLS-00629
Would you please told me what I do wrong?
You cannot assign variables using a select statement from a collection in a loop like below.
SELECT productLine, minimalPrice
INTO prodLine, prodPrice
FROM TABLE(cartLineList(i));
The collection elements cannot be referred inside a SELECT statement 1 by 1 using a loop. You can loop through the collection as
For i in 1..collection.count
loop
...
..
End loop;
Collection has a number of rows and when you do so, you try to assign many rows to a single variable, which is wrong. You can do either of the below explained. There relevant explanation is inline.
CREATE OR REPLACE PROCEDURE GETOFFERINGRECOMMENDATION (
CARTLINELIST IN CARTLINETYPE,
RECOMMENDATION OUT SYS_REFCURSOR)
IS
TYPE V_PRODLINE IS TABLE OF VARCHAR2 (20)
INDEX BY PLS_INTEGER;
TYPE V_PRODPRICE IS TABLE OF NUMBER (5, 0)
INDEX BY PLS_INTEGER;
PRODLINE V_PRODLINE;
PRODPRICE V_PRODPRICE;
BEGIN
--Putting the collection result to another collection
SELECT PRODUCTLINE,
MINIMALPRICE
BULK COLLECT INTO PRODLINE,
PRODPRICE
FROM TABLE (CARTLINELIST);
-- Assuming number of elements will be same in both prodLine, prodPrice colection, loop can be iterated as below
FOR I IN 1 .. PRODLINE.LAST
LOOP
OPEN RECOMMENDATION FOR
SELECT CAST (REKOM_ID_SEQ.NEXTVAL AS VARCHAR (10) )
|| '_'
|| CP.ID_REKOM_OFERTA
|| '_'
|| TO_CHAR (SYSDATE, 'yyyymmdd') AS RECOMMENDATIONID,
CP.ID_REKOM_OFERTA AS OFFERINGID,
CP.PRIORYTET AS PRIORITY
FROM REKOM_CROSS_PROM CP
WHERE CP.LINIA_PROD = PRODLINE (I)
AND PRODPRICE (I) BETWEEN CP.CENA_MIN AND CP.CENA_MAX;
END LOOP;
END GETOFFERINGRECOMMENDATION;
OR as per #krokodilko.. You can do as below:
CREATE OR REPLACE PROCEDURE GETOFFERINGRECOMMENDATION (
CARTLINELIST IN CARTLINETYPE,
RECOMMENDATION OUT SYS_REFCURSOR)
IS
PRODLINE VARCHAR2 (20);
PRODPRICE NUMBER (5, 0);
BEGIN
FOR I IN 1 .. CARTLINELIST.LAST
LOOP
--Assign the values of the collection to the variable declared.
PRODUCTLINE := CARTLINELIST (I).PRODUCTLINE;
MINIMALPRICE := CARTLINELIST (I).MINIMALPRICE;
OPEN RECOMMENDATION FOR
SELECT CAST (REKOM_ID_SEQ.NEXTVAL AS VARCHAR (10) )
|| '_'
|| CP.ID_REKOM_OFERTA
|| '_'
|| TO_CHAR (SYSDATE, 'yyyymmdd') AS RECOMMENDATIONID,
CP.ID_REKOM_OFERTA AS OFFERINGID,
CP.PRIORYTET AS PRIORITY
FROM REKOM_CROSS_PROM CP
WHERE CP.LINIA_PROD = PRODLINE
AND PRODPRICE BETWEEN CP.CENA_MIN AND CP.CENA_MAX;
END LOOP;
END GETOFFERINGRECOMMENDATION;
Demo:
SQL> CREATE OR REPLACE TYPE CARTLINE AS OBJECT (
2 PRODUCTLINE VARCHAR2 (50),
3 MINIMALPRICE DECIMAL
4 );
5 /
Type created.
SQL> CREATE OR REPLACE TYPE CARTLINETYPE IS TABLE OF CARTLINE;
2 /
Type created.
SQL> CREATE OR REPLACE PROCEDURE GETOFFERINGRECOMMENDATION (
2 CARTLINELIST IN CARTLINETYPE)
3 IS
4 TYPE V_PRODLINE IS TABLE OF VARCHAR2 (20)
5 INDEX BY PLS_INTEGER;
6
7 TYPE V_PRODPRICE IS TABLE OF NUMBER (5, 0)
8 INDEX BY PLS_INTEGER;
9
10 PRODLINE V_PRODLINE;
11 PRODPRICE V_PRODPRICE;
12 BEGIN
13 SELECT PRODUCTLINE,
14 MINIMALPRICE
15 BULK COLLECT INTO PRODLINE,
16 PRODPRICE
17 FROM TABLE (CARTLINELIST);
18
19 FOR I IN 1 .. PRODLINE.COUNT
20 LOOP
21 DBMS_OUTPUT.PUT_LINE ( 'Prod Line '
22 || PRODLINE (I)
23 || ' Prod Price '
24 || PRODPRICE (I) );
25 END LOOP;
26 END GETOFFERINGRECOMMENDATION;
27 /
Procedure created.
Output:
SQL> DECLARE
2 VAR CARTLINETYPE := CARTLINETYPE ();
3 BEGIN
4 --Popuating the collection
5 VAR.EXTEND (2);
6 VAR (1) := CARTLINE ('TypeA', 6.0);
7 VAR (2) := CARTLINE ('TypeB', 7.1);
8
9 --Calling the procedure
10 GETOFFERINGRECOMMENDATION (CARTLINELIST => VAR);
11 END;
12 /
Prod Line TypeA Prod Price 6
Prod Line TypeB Prod Price 7
PL/SQL procedure successfully completed.
SQL>
Use simple assignments instead of SELECT ... FROM TABLE(cartLineList(i));:
LOOP
/* SELECT productLine, minimalPrice INTO prodLine, prodPrice FROM TABLE(cartLineList(i)); */
productLine := cartLineList(i).productLine;
minimalPrice := cartLineList(i).minimalPrice;
.....
.....
END LOOP;
I have a SQL statement with the PIVOT operation. The maximum number of columns I will have in PIVOT is 5, but I can have less, 4, 3, 2.
How can I read these columns in my cursor and assign (fetch .. into...) to the fixed variables, without occurring the error ORA-01007.
...
sql_stmt := 'select * from
(select codcoligada,
idprd,
codcfo,
valnegociado
from tcitmorcamento
where codcoligada = ' || p_codcoligada || '
and codcotacao = ' || '''' || p_codcotacao || '''' || ')
pivot
(
sum(valnegociado) for codcfo in (' || pivot_clause || ')
)';
ret := t_tab_sesa_cotacao();
open vCursor for sql_stmt;
loop
/* If my cursor returns less than 5 columns in PIVOT the error occurs ORA-01007 */
fetch vCursor into vCodColigada, vIdProduto, vValor01, vValor02, vValor03, vValor04, vValor05;
exit when vCursor%NOTFOUND;
ret.extend;
ret(ret.count) := t_type_sesa_cotacao(vCodColigada, vIdProduto, vValor01, vValor02, vValor03, vValor04, vValor05);
end loop;
close vCursor;
...
If I return less than 5 colums, I want to fill in the remainder of the variables with a value of 0 or null.
The variables vCodColigada and vIdProduto are identified, only PIVOT columns that can vary between 1 and 5 (vValor1, vValor2, vValor3, vValor4, vValor5)
Result PIVOT SQL:
CODCOLIGADA IDPRD '000125' '002272' '002342'
----------------- ---------------- ---------------- ---------------- ----------------
1 15464 45 300 30
1 18460 35 200 20
1 57492 20 100 10
-------- End of Data --------
Example:
If the cursor returns 3 values in the PIVOT (above), the variables vValor01, vValor02, vValor03 will be filled in, and the variables vValor04, vValor05 must be 0 or null.
Example:
CODCOLIGADA IDPRD VALOR01 VALOR02 VALOR03 VALOR04 VALOR05
----------------- ---------------- ---------------- ---------------- ---------------- ---------------- ----------------
1 15464 45 300 30 0 0
1 18460 35 200 20 0 0
1 57492 20 100 10 0 0
-------- End of Data --------
As I only have 3 columns in PIVOT, and I have 5 variables, the ORA-01007 error occurs in (fetch .. into ...).
Hope this below snippet helps. What the basic understanding is we need to add excess variable as null or blank to make this work.
SET serveroutput ON;
DECLARE
lv_pivot VARCHAR2(100):='''Y'',''N''';
TYPE lv
IS
RECORD
(
flg_y VARCHAR2(100),
flg_n VARCHAR2(100),
flg_e VARCHAR2(100));
type lv_tab
IS
TABLE OF lv;
lv_num lv_tab;
lv_check VARCHAR2(1000);
BEGIN
lv_check :=regexp_count(lv_pivot,',',1);
IF lv_check < 3 THEN
FOR z IN 1..(2-lv_check)
LOOP
lv_pivot:=lv_pivot||',null as val'||z;
END LOOP;
ELSE
lv_pivot:=lv_pivot;
END IF;
dbms_output.put_line(lv_pivot);
EXECUTE IMMEDIATE ' SELECT * FROM
(SELECT col1 FROM <table> )
pivot ( COUNT(1) FOR col1 IN ('||lv_pivot||'))' BULK COLLECT INTO lv_num;
END;
---------------------------Refactoring in Function------------------------------
--Create Object Type
CREATE OR REPLACE TYPE lv_obj IS OBJECT
(
flg_y VARCHAR2(100),
flg_n VARCHAR2(100),
flg_e VARCHAR2(100)
);
--Create Table Type
CREATE OR REPLACE TYPE lv_tab IS TABLE OF lv_obj;
--Create Function
CREATE OR REPLACE
FUNCTION test_func
RETURN lv_tab
AS
lv_pivot VARCHAR2(100):='''Y'',''N''';
lv_num lv_tab;
lv_check VARCHAR2(1000);
BEGIN
lv_check :=regexp_count(lv_pivot,',',1);
IF lv_check < 3 THEN
FOR z IN 1..(2-lv_check)
LOOP
lv_pivot:=lv_pivot||',null as val'||z;
END LOOP;
ELSE
lv_pivot:=lv_pivot;
END IF;
dbms_output.put_line(lv_pivot);
EXECUTE IMMEDIATE ' SELECT * FROM
(SELECT col1 FROM <table> )
pivot ( COUNT(1) FOR col1 IN ('||lv_pivot||'))' BULK COLLECT INTO lv_num;
RETURN lv_tab;
END;
-------------------------------------------------Output-----------------------------------------------
SELECT * FROM TABLE(test_func);
-------------------------------------------------------------------------------------------------------
Try padding the results in the query. Add a join to a select from dual statement that passes back all five columns.
Hope someone can help. When I tried to insert something into a table it give me error saying the primary key is already existed. So I need to reset my sequence so that it is always max(id)+1.
The table is called 'People' with 2 columns (ID, Name). The sequence is called SEQ.
I am thinking of doing a loop. To run select SEQ.nextval from dual for n times. this n= max(id)-SEQ.currval
Wwill this work? and how Can I put it into the syntax?
Thanks a lot.
declare
l_MaxVal pls_integer;
l_Currval pls_integer default - 1;
begin
select max(id)
into l_MaxVal
from people;
while l_Currval < l_Maxval
loop
select my_seq.nextval
into l_Currval
from dual;
end loop;
end;
If this is a one off, you can use the alter sequence
alter sequence sequenceName increment by val ;
whereas val is +1 to the maximum
then call get nextVal, then set the increment back to 1.
I threw the below together to show you how it can be done without looping.
create sequence changeValue start with 18 increment by 1 nocache ;
select changeValue.nextval from dual ;
/
NEXTVAL
----------------------
18
set serveroutput on
declare
maxVal number := 24 ;
curVal number ;
diffVal number ;
incrementVal number ;
procedure alterSequence(seqName in varchar2, incVal in number) as
s varchar2(500);
begin
s := 'alter sequence ' || seqName || ' increment by ' || incVal ;
dbms_output.put_line(s);
execute immediate s;
end alterSequence;
begin
--(done in 11gr2 so if in earlier version select into)
curVal := changeValue.currval ;
dbms_output.put_line('curValue=>' || curVal );
diffVal := maxVal - curVal ;
dbms_output.put_line('diffVal=>' || diffVal );
alterSequence ( 'changeValue' , diffVal + 1 );
incrementVal := changeValue.nextval ;
dbms_output.put_line('incrementVal=>' || incrementVal );
alterSequence ( 'changeValue' , 1 );
curVal := changeValue.currval ;
dbms_output.put_line('curValue=>' || curVal );
end ;
/
curValue=>18
diffVal=>6
alter sequence changeValue increment by 7
incrementVal=>25
alter sequence changeValue increment by 1
curValue=>25
or better yet, as #Dave suggests, just drop and recreate the sequence with the acceptable Start With value.
With this one you can synchronize the sequence whatever it is forward or behind the max of the ID.
Just need to change the parameters in the final of the code.
declare
procedure SYNC_SEQUENCE
( P_IN_SEQ in varchar2
, P_IN_TABLE in varchar2
, P_IN_ID in varchar2
)
is
LV_MAXVAL number := 0;
LV_CURRVAL number := -1;
LV_AUX NUMBER;
begin
execute immediate
'select max('||P_IN_ID||')
from '||P_IN_TABLE into LV_MAXVAL;
execute immediate
'select '||P_IN_SEQ||'.nextval
from dual ' into LV_CURRVAL;
if LV_MAXVAL < LV_CURRVAL then
LV_AUX := (LV_CURRVAL - LV_MAXVAL);
execute immediate
'ALTER SEQUENCE '||P_IN_SEQ||' INCREMENT BY -'||LV_AUX;
execute immediate
'SELECT '||P_IN_SEQ||'.NEXTVAL FROM dual' INTO LV_AUX;
execute immediate
'ALTER SEQUENCE '||P_IN_SEQ||' INCREMENT BY 1';
end if;
while LV_CURRVAL < LV_MAXVAL
loop
execute immediate
'select '||P_IN_SEQ||'.nextval
from dual ' into LV_CURRVAL;
end loop;
end SYNC_SEQUENCE;
begin
SYNC_SEQUENCE('MY_SEQUENCIE_NAME','MY_TABLE_NAME','MY_FIELD_ID_NAME');
end;
/