How to get Numbers in number range by PL-Sql .? - 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 :-)

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

Nested Loops In Oracle Not Work Correctly (Exceeds the maximum)

I wrote These Two queries For Sample Table to insert into "z_exp14_resualt" From "z_exp14_main ". The First Query Works Correctly (Null) But Second That For rows have due_date on the main table (Not Null), not works correctly! . I Think Problem is The loop. It started before the due date and even goes beyond that . For NOT Nulls Insert must start from today(year and monts from sysdate and day from opening_date) and continue until due_date
Calculate For Null DUE_DATES
declare
i number := 1;
BEGIN
for i in 1..12 loop
insert into z_exp14_resualt
select dep_id,ADD_MONTHS(ADD_MONTHS(opening_date,trunc( months_between (sysdate,opening_date))),i),rate*balance
from z_exp14_main
WHERE due_date IS null;
end loop;
END;
/
And For Not Null Due_dates
DECLARE
diff number;
x number :=1;
BEGIN
for i in (select * FROM z_exp14_main WHERE due_date IS NOT null) loop
diff :=trunc( months_between (sysdate,i.opening_date));
WHILE (ADD_MONTHS(ADD_MONTHS(i.opening_date,diff),x)<i.due_date) LOOP
insert into z_exp14_resualt
select dep_id,ADD_MONTHS(ADD_MONTHS(i.opening_date,diff),x),rate*balance
from z_exp14_main WHERE due_date is not null ;
x :=x+1;
end loop;
end loop;
end;
/
sample Date On Main (z_exp14_main)
--
DEP_ID
DUE_DATE
BALANCE
RATE
OPENING_DATE
--
20056634
null
283428
10
15-SEP-16
--
20056637
null
180222
10
07-NOV-14
--
20056639
null
58741
10
28-AUG-14
--
40000020
27-NOV-21
5000000
22
31-MAR-14
--
40000023
23-APR-21
63000000
22
25-AUG-18
The Problem Is In the "While" condition. I correct it And This Is Final Answer:
DECLARE
diff number;
x number ;
BEGIN
for i in (select * FROM z_exp14_main WHERE due_date IS NOT null) loop
diff :=trunc( months_between (sysdate,i.opening_date));
x:=0;
WHILE (add_months(sysdate,x)<i.due_date) LOOP
insert into z_exp14_resualt values (i.dep_id,ADD_MONTHS(ADD_MONTHS(i.opening_date,diff),x),(i.rate*i.balance) );
x :=x+1;
end loop;
end loop;
end;
/

With statement select and function into cursor

it is possible to use the with structure with a function inside a cursor, I don't know if I am declaring it inappropriately, I am getting the following error using with function inside procedure pl sql statement is not supported
CURSOR c_detail IS
WITH
FUNCTION CALC_NUMBER(FOB_ITEM NUMBER DEFAULT 0,
FOB_TOTAL NUMBER DEFAULT 0,
WEIGHT NUMBER DEFAULT 0) RETURN NUMBER
IS
PESO_BRUTO_ITEM NUMBER :=0;
BEGIN
IF( (FOB_ITEM > 0) AND (FOB_TOTAL > 0 AND WEIGHT> 0 )) THEN
PESO_BRUTO_ITEM := (FOB_ITEM * WEIGHT) / FOB_TOTAL;
END IF;
RETURN PESO_BRUTO_ITEM;
END CALC_NUMBER;
test_data AS
(
SELECT 36.25 AS FOB_I, 12536.36 AS FOB_TOTAL, 362 AS W FROM dual UNION ALL
SELECT 15.36 AS FOB_I, 3678.65 AS FOB_TOTAL, 362 AS W FROM dual UNION ALL
SELECT 878.77 AS FOB_I, 89653.13 AS FOB_TOTAL, 362 AS W FROM dual
)
SELECT TD.FOB_I,
TD.FOB_TOTAL,
CALC_NUMBER(TD.FOB_I, TD.FOB_TOTAL, TD.W) WEIGHT
FROM test_data TD
[TL;DR] You can declare a function in a sub-query factoring clause but (as Justin Cave points out) it only works when you are executing the query as dynamic SQL and support for using functions in static SQL within a cursor may be available in future database versions.
This sub-query factoring clause with a function works outside of a cursor:
WITH
FUNCTION with_function(
p_id IN NUMBER
) RETURN NUMBER
IS
BEGIN
RETURN 42 + p_id;
END;
test_data ( id ) AS (
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3
)
SELECT id,
with_function( id )
FROM test_data;
Trying to put it into a cursor (in Oracle 18c):
DECLARE
p_id NUMBER;
p_fn NUMBER;
CURSOR c_detail IS
WITH
FUNCTION with_function(
p_id IN NUMBER
) RETURN NUMBER
IS
BEGIN
RETURN 42 + p_id;
END;
test_data ( id ) AS (
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3
)
SELECT id,
with_function( id )
FROM test_data;
BEGIN
OPEN c_detail;
LOOP
FETCH c_detail INTO p_id, p_fn;
EXIT WHEN c_detail%NOTFOUND;
DBMS_OUTPUT.PUT_LINE( p_id || ', ' || p_fn );
END LOOP;
CLOSE c_detail;
END;
/
Outputs the error:
ORA-06550: line 7, column 14:
PL/SQL: ORA-00905: missing keyword
ORA-06550: line 6, column 3:
PL/SQL: SQL Statement ignored
ORA-06550: line 13, column 5:
PLS-00103: Encountered the symbol "END" when expecting one of the following:
begin function pragma procedure subtype type <an identifier>
<a double-quoted delimited-identifier> current cursor delete
exists prior
Removing the function then the cursor works:
DECLARE
p_id NUMBER;
CURSOR c_detail IS
WITH
test_data ( id ) AS (
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3
)
SELECT id
FROM test_data;
BEGIN
OPEN c_detail;
LOOP
FETCH c_detail INTO p_id;
EXIT WHEN c_detail%NOTFOUND;
DBMS_OUTPUT.PUT_LINE( p_id );
END LOOP;
CLOSE c_detail;
END;
/
So it is not an issue with using a sub-query factoring clause in a cursor.
Executing the cursor as dynamic SQL query:
DECLARE
p_id NUMBER;
p_fn NUMBER;
c_detail SYS_REFCURSOR;
p_sql VARCHAR2(4000) := 'WITH
FUNCTION with_function(
p_id IN NUMBER
) RETURN NUMBER
IS
BEGIN
RETURN 42 + p_id;
END;
test_data ( id ) AS (
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3
)
SELECT id,
with_function( id )
FROM test_data';
BEGIN
OPEN c_detail FOR p_sql;
LOOP
FETCH c_detail INTO p_id, p_fn;
EXIT WHEN c_detail%NOTFOUND;
DBMS_OUTPUT.PUT_LINE( p_id || ', ' || p_fn );
END LOOP;
CLOSE c_detail;
END;
/
Works and outputs:
1, 43
2, 44
3, 45
So it appears that, yes, you can declare a function in a sub-query factoring clause but it only works when you are executing the query as dynamic SQL.
db<>fiddle here
The error message clearly says you can't do what you are after, but then you will not have to if your cursor looks like following
CURSOR c_detail IS
WITH test_data AS(
SELECT 36.25 AS FOB_I, 12536.36 AS FOB_TOTAL, 362 AS W FROM dual UNION ALL
SELECT 15.36 AS FOB_I, 3678.65 AS FOB_TOTAL, 362 AS W FROM dual UNION ALL
SELECT 878.77 AS FOB_I, 89653.13 AS FOB_TOTAL, 362 AS W FROM dual
)
SELECT TD.FOB_I,
TD.FOB_TOTAL,
CASE WHEN TD.FOB_I > 0 AND TD.FOB_TOTAL > 0 AND TD.W > 0
THEN (TD.FOB_I * TD.W) / TD.FOB_TOTAL
ELSE 0
END WEIGHT
FROM test_data TD
;

character string buffer too small ORA-06502

I am having a problem while concatenating the varchar2 datatype in a cursor loop.
Procedure is iterating in a loop to build the in clause for insert and delete operations in batch.The process will run in batch for every 1000 account numbers.
For small amount of records it works but when it tries to concatenate large amount of records(36451477 in temp table) in a loop it throws.
java.sql.SQLException: ORA-06502: PL/SQL: numeric or value error:
character string buffer too small ORA-06512: at
"QA01BT.LOAD_ITEM_DATA_TO_CONSOLIDATE", line 23 ORA-06512: at line 1
i have put a max limit of search id to 32767 but still it does not work.
is there any other way to achieve this?
create or replace PROCEDURE LOAD_ITEM_DATA_TO_CONSOLIDATE(updatecount OUT NUMBER
)
IS
cnt NUMBER := 0;
c_limit CONSTANT PLS_INTEGER DEFAULT 1000;
search_id varchar2(32727);
TYPE account_array
IS TABLE OF VARCHAR2(255) INDEX BY BINARY_INTEGER;
l_data ACCOUNT_ARRAY;
CURSOR account_cursor IS
SELECT DISTINCT account_no AS account_num
FROM item_temp;
BEGIN
OPEN account_cursor;
LOOP
FETCH account_cursor bulk collect INTO l_data limit c_limit;
search_id := '''';
FOR i IN 1 .. l_data.count LOOP
IF( i != 1 ) THEN
search_id := search_id
|| ','
|| ''''
|| l_data(i)
|| '''';
ELSE
search_id := search_id
|| l_data(i)
|| '''';
END IF;
END LOOP;
BEGIN
SAVEPOINT move_data_to_temp_table;
EXECUTE IMMEDIATE 'delete from item where ACCOUNT_NO IN('||search_id||')';
EXECUTE IMMEDIATE 'insert into item(ID,ACCOUNT_NO,ITEM_ID,ITEM_VALUE) select HIBERNATE_SEQUENCE.nextval,temp.ACCOUNT_NO,temp.ITEM_ID,temp.ITEM_VALUE from item_TEMP temp where ACCOUNT_NO IN('||search_id||')';
cnt := cnt + SQL%rowcount;
COMMIT;
EXCEPTION WHEN OTHERS THEN ROLLBACK to move_data_to_temp_table;
END;
EXIT WHEN account_cursor%NOTFOUND;
END LOOP;
updatecount := cnt;
CLOSE account_cursor;
END LOAD_ITEM_DATA_TO_CONSOLIDATE;
This seems somewhat over-engineered. Why not just this?
create or replace PROCEDURE LOAD_ITEM_DATA_TO_CONSOLIDATE
(updatecount OUT NUMBER)
IS
BEGIN
delete from item
where ACCOUNT_NO IN ( SELECT account_no
FROM item_temp);
insert into item(ID,ACCOUNT_NO,ITEM_ID,ITEM_VALUE)
select HIBERNATE_SEQUENCE.nextval, temp.ACCOUNT_NO, temp.ITEM_ID, temp.ITEM_VALUE
from item_TEMP temp ;
updatecount := SQL%rowcount;
END LOAD_ITEM_DATA_TO_CONSOLIDATE;
If you do decide you need to do this in batches and are worried about that string getting too long or having too many elements in the list (max is 1000), you should try putting your values into an array and then using IN against the array, via a table function or a direct reference to the table.
Extra bonus: no need for dynamic SQL!
Something like this:
CREATE OR REPLACE TYPE strings_t IS TABLE OF VARCHAR2 (255)
/
CREATE OR REPLACE PROCEDURE load_item_data_to_consolidate (
updatecount OUT NUMBER)
IS
cnt NUMBER := 0;
c_limit CONSTANT PLS_INTEGER DEFAULT 1000;
l_data strings_t;
CURSOR account_cursor
IS
SELECT DISTINCT account_no AS account_num FROM item_temp;
BEGIN
OPEN account_cursor;
LOOP
FETCH account_cursor BULK COLLECT INTO l_data LIMIT c_limit;
BEGIN
SAVEPOINT move_data_to_temp_table;
DELETE FROM item
WHERE account_no IN (SELECT COLUMN_VALUE FROM TABLE (l_data));
INSERT INTO item (id,
account_no,
item_id,
item_value)
SELECT hibernate_sequence.NEXTVAL,
temp.account_no,
temp.item_id,
temp.item_value
FROM item_temp temp
WHERE account_no IN (SELECT COLUMN_VALUE FROM TABLE (l_data));
cnt := cnt + SQL%ROWCOUNT;
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
ROLLBACK TO move_data_to_temp_table;
END;
EXIT WHEN account_cursor%NOTFOUND;
END LOOP;
END;

Use an array in a SELECT statement?

I want to use this array in 'select from..where..in(YYY)' statement.
I don't want to iterate through array values, I want to use it whole in my select statement.
Unfortunately, I found only how to iterate it:
1 declare
2 type array is table of varchar2(30) index by binary_integer;
3 a array;
4 procedure p( array_in array )
5 is
6 begin
7 for i in 1..array_in.count loop
8 dbms_output.put_line( array_in(i) );
9 end loop;
10 end;
11 begin
12 a(1) := 'Apple';
13 a(2) := 'Banana';
14 a(3) := 'Pear';
15 p( a );
16 end;
17 /
You can do this by creating a function returning your array. Then you can use it into a select:
Create external types and function
create or replace type t_array is table of varchar2(30);
create or replace function list_of_fruits
return t_array
is
l_ t_array:=t_array();
begin
l_.extend(); l_(l_.COUNT) := 'Apple';
l_.extend(); l_(l_.COUNT) := 'Banana';
l_.extend(); l_(l_.COUNT) := 'Pear';
return l_;
end list_of_fruits;
/
And here is how to use it:
select * from (
select 'Peter' this_and_that from dual
union all select 'Joy' from dual
union all select 'God' from dual
union all select 'Pear' from dual
union all select 'Man' from dual
)
where this_and_that in (
select column_value from (table( list_of_fruits() ))
);
The trick here is to use the table() function to make a SQL usable list for your select; also difficult for me was to discover the name of that column_value... which is some built-in constant from Oracle: how do you guess that?
You can use oracle defined collection to achieve this as well. Please see below and example.
declare
a sys.odcivarchar2list;
begin
a := sys.odcivarchar2list('Apple','Banana','Pear');
for r in ( SELECT m.column_value m_value
FROM table(a) m )
loop
dbms_output.put_line (r.m_value);
end loop;
end;