Is there an oracle spatial function for finding self-intersecting linestrings? - oracle-spatial

I need to find all self-intersecting linestrings in table. SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT finds only self-intersecting polygons, because self-intersecting linestrings are allowed. Any ideas?

It takes a bit of work, but it's doable.
Since Oracle(11.2) failed to provide, the only option we have is to brake the line into segments and use RELATE on the segments' pairs.
Following is my own implementation (used in production code for over 3 years, over millions of geometries). I chose the pipelined approach to cover for overly big or complex geometries.
Prerequisit 1, database type:
CREATE OR REPLACE TYPE ElemGeom as object
(eid integer, egeom mdsys.sdo_geometry, egtype integer, eelemnum integer, evertnum integer, earea number, elength number);
CREATE OR REPLACE TYPE ElemGeomTbl as table of ElemGeom;
Prerequisit 2, splitting function:
create or replace FUNCTION LineSegments (igeom in mdsys.sdo_geometry)
RETURN ElemGeomTbl pipelined
is
seg ElemGeom := ElemGeom(null,null,null,null,null,null,null);
cursor c is select T.id, T.X ,T.Y from table(SDO_UTIL.GETVERTICES(iGEOM)) T order by 1;
type ctbl is table of c%rowtype;
carr ctbl;
seg_geom mdsys.sdo_geometry;
cnt integer:=0;
segid integer; x1 number; y1 number; x2 number; y2 number;
begin
--if igeom.sdo_gtype not in (2002,2006)
--then... if you need to catch non-linears here...
--end if;
open c;
loop
fetch c
bulk collect into carr ;
for i in carr.first .. carr.last -1
loop cnt:=cnt+1;
segid := cnt;
x1 := carr(i).X; y1 := carr(i).Y;
x2 := carr(i+1).X; y2 := carr(i+1).Y;
seg_geom:= (mdsys.sdo_geometry(2002,2100,null
,mdsys.sdo_elem_info_array(1,2,1)
,mdsys.sdo_ordinate_array(x1,y1, x2,y2)));
seg.eid:=segid;
seg.egeom:=seg_geom;
seg.egtype:=seg_geom.sdo_gtype;
pipe row(seg);
end loop;
exit when c%notfound;
end loop;
close c;
end LineSegments;
You can test its output with something like (my GEOMs are SRID 2100):
with t1 as (
select
SDO_GEOMETRY(2002,2100,NULL,
SDO_ELEM_INFO_ARRAY(1,2,1),
SDO_ORDINATE_ARRAY(290161.697,4206385.413, 290161.901,4206388.095, 290162.684,4206385.188, 290163.188,4206388.041,
290163.51,4206385.22, 290164.357,4206388.159, 290166.879,4206387.108, 290161.397,4206387.366,
290166.331,4206386.067, 290165.763,4206388.052))
as G from DUAL
)
select * from t1,table(LineSegments(g));
And the main function:
create or replace FUNCTION validate_Line
(igeom in mdsys.sdo_geometry, itol in number default null)
RETURN varchar2
is
vtol number:= nvl(itol, 1/power(10,6));
verd1 varchar2(256); verd2 varchar2(256); v varchar2(256);
begin
verd1:= sdo_geom.validate_geometry_with_context(igeom,vtol);
for r1 in ( select a.eid seg1, a.egeom geom1, b.eid seg2, b.egeom geom2
from table(LineSegments(igeom)) a, table(LineSegments(igeom)) b
where a.eid < b.eid
order by a.eid, b.eid )
loop
--I hate outputting long words, so:
v:= replace(replace(sdo_geom.relate(r1.geom1,'determine',r1.geom2, vtol)
,'OVERLAPBDYDISJOINT','OVR-BDIS'),'OVERLAPBDYINTERSECT','OVR-BINT');
if instr('EQUAL,TOUCH,DISJOINT',v) = 0 then
verd2:= verd2|| case when verd2 is not null
then ', '||r1.seg1||'-'||r1.seg2||'='||v
else r1.seg1||'-'||r1.seg2||'='||v end;
end if;
end loop;
verd1:= nvl(verd1,'NULL')
|| case when verd1 ='TRUE' and verd2 is null then null
when verd1 ='TRUE' and verd2 is not null then ' *+: '||verd2
end;
return verd1;
end validate_Line;
And its test:
with t1 as (
select
SDO_GEOMETRY(2002,2100,NULL,
SDO_ELEM_INFO_ARRAY(1,2,1),
SDO_ORDINATE_ARRAY(290161.697,4206385.413, 290161.901,4206388.095, 290162.684,4206385.188, 290163.188,4206388.041,
290163.51,4206385.22, 290164.357,4206388.159, 290166.879,4206387.108, 290161.397,4206387.366,
290166.331,4206386.067, 290165.763,4206388.052))
as G from DUAL
)
select t1.*,validate_Line(g) from t1;
This returns:
*TRUE *+: 1-7=OVR-BDIS, 1-8=OVR-BDIS, 2-7=OVR-BDIS, 2-8=OVR-BDIS, 3-7=OVR-BDIS, 3-8=OVR-BDIS, 4-7=OVR-BDIS, 4-8=OVR-BDIS, 5-7=OVR-BDIS, 5-8=OVR-BDIS, 6-9=OVR-BDIS, 7-9=OVR-BDIS*
Of course, you can modify the output to be just a flag or anything else - this is just what suited my own needs.
HTH

Related

How to select only the colums of a table which are the same of another table

info about polymorphic table functions
I'm using a polymorphic function (skip_col_not_in_model ) to remove the columns of a table which are not in another table.
CREATE OR REPLACE PACKAGE pkg_skip_col
AS
FUNCTION skip_col (tab TABLE, col COLUMNS)
RETURN TABLE
PIPELINED ROW POLYMORPHIC USING pkg_skip_col;
FUNCTION describe (tab IN OUT DBMS_TF.TABLE_T, col DBMS_TF.COLUMNS_T)
RETURN DBMS_TF.DESCRIBE_T;
FUNCTION skip_col_not_in_model (tab TABLE, tabk_model TABLE)
RETURN TABLE
PIPELINED ROW POLYMORPHIC USING pkg_skip_col;
FUNCTION describe (tab IN OUT DBMS_TF.TABLE_T,
tab_model IN DBMS_TF.TABLE_T)
RETURN DBMS_TF.DESCRIBE_T;
END;
CREATE OR REPLACE PACKAGE BODY pkg_skip_col
AS
FUNCTION describe (tab IN OUT DBMS_TF.TABLE_T,
tab_model IN DBMS_TF.TABLE_T)
RETURN DBMS_TF.DESCRIBE_T
AS
BEGIN
FOR i IN 1 .. tab.column.COUNT ()
LOOP
FOR j IN 1 .. tab_model.column.COUNT ()
LOOP
tab.column (i).PASS_THROUGH :=
tab.column (i).DESCRIPTION.NAME = tab_model.column (i).DESCRIPTION.NAME ;
EXIT WHEN tab.column (i).PASS_THROUGH;
END LOOP;
END LOOP;
RETURN NULL;
END;
FUNCTION describe (tab IN OUT DBMS_TF.TABLE_T, col DBMS_TF.COLUMNS_T)
RETURN DBMS_TF.DESCRIBE_T
AS
BEGIN
FOR i IN 1 .. tab.column.COUNT ()
LOOP
FOR j IN 1 .. col.COUNT ()
LOOP
tab.column (i).PASS_THROUGH :=
tab.column (i).DESCRIPTION.NAME != col (j);
EXIT WHEN NOT tab.column (i).PASS_THROUGH;
END LOOP;
END LOOP;
RETURN NULL;
END;
END;
/
I can compile the body. But not the spec.
[Error] Compilation (24: 59): PLS-00766: more than one parameter of TABLE type is not allowed
Similary to this query
with a (a1,a2) as (select 1,2 from dual) select * from
pkg_skip_col.skip_col(a,COLUMNS(a1))
a1:1
I would like to do that
with a (a1,a2) as (select 1,2 from dual),
b (a1,a2,a3) as (select 1,2,3 from dual)
select * from pkg_skip_col.skip_col_not_in_model(a,b)
a1:1, a2: 2 (expected resulted only)
The problem comes from the fact that I can't give two table.
to avoid this problem, I can give the table_model as a string, get the column (search in table all_tab_columns ).
But the code is not so easy to understand anymore und doesn't work with common table expression.
Has somebody ideas of improvement?
code
You can do this (from 19.6) using SQL macros. Unlike polymorphic table functions, these allow you to have many dbms_tf.table_t arguments.
SQL macros return the text of a SQL expression which is constructed at parse time. So here you can build the select list similar to the method you've used in the PTF.
For example:
create or replace function skip_col_not_in_model (
query_table dbms_tf.table_t,
ref_table dbms_tf.table_t
)
return clob sql_macro as
select_list clob;
stmt clob;
begin
<<query_cols>>
for i in 1 .. query_table.column.count loop
for j in 1 .. ref_table.column.count loop
if query_table.column(i).description.name = ref_table.column(j).description.name then
select_list :=
select_list
|| query_table.column(i).description.name
|| ',';
continue query_cols;
end if;
end loop;
end loop;
stmt := 'select '
|| rtrim ( select_list, ',' )
|| ' from query_table';
return stmt;
end skip_col_not_in_model;
/
with a (a1,a2) as (
select 1,2 from dual
), b (a1,a2,a3) as (
select 1,2,3 from dual
)
select *
from skip_col_not_in_model ( a, b );
A1 A2
---------- ----------
1 2
You can convert any describe only polymorphic table to a macro.
to avoid this problem, I can give the table_model as a string
In general this doesn't work! As the docs say:
While the constant scalar values are passed as-is to the DESCRIBE
function, all other values are passed as NULLs.
So if you use a bind variable to pass the table name, it's value is null.
In this example, notice that when using :new_col for the string parameter val its null in the describe:
create or replace package ptf_pkg
as
function select_val (tab table, val varchar2)
return table
pipelined row polymorphic using ptf_pkg;
function describe ( tab in out dbms_tf.table_t, val varchar2 )
return dbms_tf.describe_t;
end;
/
create or replace package body ptf_pkg
as
function describe ( tab in out dbms_tf.table_t, val varchar2 )
return dbms_tf.describe_t
as
begin
dbms_tf.trace ( 'val = ' || nvl ( val, 'IT IS NULL' ) );
return null;
end;
end;
/
select * from ptf_pkg.select_val ( dual, 'Y' );
D
-
X
val = Y
var new_col varchar2(30);
exec :new_col := 'NEW_COL';
select * from ptf_pkg.select_val ( dual, :new_col );
D
-
X
val = IT IS NULL

Procedure returns data that satisfies both if and else conditions

I have a procedure like this
PROCEDURE FUNC(A VARCHAR2, B VARCHAR2, C VARCHAR2, CUR OUT) IS
BEGIN
STR_1 := 'OPEN CUR FOR SELECT X, Y, Z FROM TABLE_T WHERE';
STR_2 := 'TABLE_T.A=' || A;
STR_3 := 'TABLE_T.B=' || B || ' AND TABLE_T.C=' || 'C';
IF (A IS NOT NULL) THEN
STR_4 = STR_1 || STR_2;
ELSE
STR_4 = STR_1 || STR_3;
END IF;
EXECUTE IMMEDIATE STR_4;
END FUNC;
This is not the exact procedure but a brief example of the same. When I run this procedure, I get results that satisfy both IF and ELSE condition ....But I want to execute either IF or ELSE statement
Where is the error??
There are many errors:
You do not give a data type for the CUR argument.
You do not declare the STR_1, STR_2, STR_3 or STR_4 local variables.
You are building the SQL via string concatenation rather than using bind variables (assuming that you want to pass in the values of the input arguments rather than use the input variables as column names).
If you are passing values then you may find issues from having the function arguments having the same identifiers as the columns.
You filter on TABLE_T.C='||'C' which is the same as C=C or 1=1 and will always be true.
You use EXECUTE IMMEDIATE and the SQL you are trying to run is not either a complete SQL statement or a complete PL/SQL block.
One solution may be something like:
CREATE PROCEDURE FUNC(
I_A IN TABLE_T.A%TYPE,
I_B IN TABLE_T.B%TYPE,
I_C IN TABLE_T.C%TYPE,
O_CUR OUT SYS_REFCURSOR
)
IS
BEGIN
IF i_a IS NOT NULL THEN
OPEN o_cur FOR
SELECT X, Y, Z
FROM TABLE_T
WHERE A = I_A;
ELSE
OPEN o_cur FOR
SELECT X, Y, Z
FROM TABLE_T
WHERE B = I_B
AND C = I_C;
END IF;
END FUNC;
/
Then, if you have the sample data:
CREATE TABLE table_t(x,y,z,a,b,c) AS
SELECT 1.1, 1.2, 1.3, 'xyz', 'xxx', 'yyy' FROM DUAL UNION ALL
SELECT 2.1, 2.2, 2.3, 'abc', 'aaa', 'bbb' FROM DUAL;
Then:
DECLARE
v_cur SYS_REFCURSOR;
x table_t.x%TYPE;
y table_t.y%TYPE;
z table_t.z%TYPE;
BEGIN
func('xyz', NULL, NULL, v_cur);
LOOP
FETCH v_cur INTO x, y, z;
EXIT WHEN v_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(x ||', '|| y || ', ' || z);
END LOOP;
END;
/
Outputs:
1.1, 1.2, 1.3
db<>fiddle here

Generate sequence of alphabets in using oracle SQL query

I have got a task in which I have to generate alphabet sequence on execution of the same oracle query every time.
Example:
When I execute a query first time, it has to generate A
When I execute the same query second time, it has to generate B.
So, it should generate A,B,C,D......Z. Once it reaches Z, it has to generate AA,AB,AC.....AZ
How to compose a query?
Using alphabetical representation for sequence is not a good idea, you should use numerical values. Oracle provides IDENTITY COLUMN for this purpose.
If you actually need an alphabet sequence, then you could create a simple SEQUENCE and a FUNCTION that returns the alphabet sequence of your choice. Using Tom's query in the function:
CREATE SEQUENCE s;
CREATE OR REPLACE FUNCTION get_alpha_seq (seq_val IN NUMBER)
RETURN VARCHAR2 AS
v_val NUMBER;
alpha_seq VARCHAR2(4000);
BEGIN
v_val := seq_val;
SELECT case when i3 > 0 then chr(i3+ascii('A')-1) end ||
case when i2 > 0 then chr(i2+ascii('A')-1) end ||
chr(i1+ascii('A')-1)
INTO alpha_seq
FROM (
SELECT mod((v_val-1),27) i1,
mod( trunc((v_val-0.1)/27), 27) i2,
mod( trunc((v_val-0.1)/27/27), 27 ) i3,
v_val l
FROM dual
)
WHERE i1 <> 0
AND NOT( l>= 27*27 and i2 = 0)
AND NOT( l>= 27*27*27 and i3 = 0);
RETURN alpha_seq;
END;
/
Now call this function with the sequence NEXTVAL as input:
SELECT get_alpha_seq(s.nextval) FROM dual;
Demo:
SQL> SELECT get_alpha_seq(s.nextval) FROM dual;
GET_ALPHA_SEQ(S.NEXTVAL)
------------------------------
A
SQL> SELECT get_alpha_seq(s.nextval) FROM dual;
GET_ALPHA_SEQ(S.NEXTVAL)
------------------------------
B
Similarly, it can return alphabetical sequence like below:
A
B
C
...
Z
AA
AB
...
AY
AZ
BA
BB
...
ZZ
AAA
AAB
...
ZZZ
To make sure only this query uses the sequence we can use the below
create table alpha_seq( t number);
insert into alpha_seq values(0);
then use the below function
CREATE OR REPLACE FUNCTION get_alpha_sequence
RETURN VARCHAR2 AS
pragma autonomous_transaction;
v_value NUMBER;
alpha_seq VARCHAR2(4000);
begin
update alpha_seq set t=t+1;
select t into v_value from alpha_seq;
commit;
with
letters
as
(select chr( ascii('A')+level-1 ) letter
from dual
connect by level <= 26
),
alpha as (select *
from letters
union all
select l1.letter || l2.letter
from letters l1, letters l2
union all
select l1.letter || l2.letter || l3.letter
from letters l1, letters l2, letters l3)
select letter into alpha_seq from (select a.*,rownum rw1 from alpha a
where rownum <=v_value) where rw1=v_value;
return alpha_seq;
END;
Output:-
select get_alpha_sequence from dual;
A
select get_alpha_sequence from dual;
B

How to do calculate more fields with another row in oracle

One table1 has 8 fields, its like formula
code code1 calc1 perc1 calc code2 calc2 perc2
a aa * 20% + bb * 10%
b a * - cc *
c b * 2% + dd * 10%
formula is
c=(2%(20%aa+10%bb-cc))+(10%dd)
so only i used tis kind of format.
Table2 has 2 fields.
field value
aa 50
bb 20
cc 10
dd 20
these values should be used in table1 format
calculation format is like this
a = (aa*20%) + (bb*10%)
b = (a ) - (cc )
c = (b*2% ) + (dd*10%)
ex:
a =(50*20%) + (20*10%)
b =(12 ) - (10 )
c =(2 *2% ) + (20*10%)
that means
a = (10)+(2)
b = (12)-(10)
c = (0.04)+(2)
I need return answer is '2.04'.
did u understand my task.
How do I do it?
Table 1 you mentioned as expression, and the other table as code.
select
exp.No,
exp.Code,
NVL(code1.value,exp.code1) as Code1,
exp.calc,
NVL(code2.value,exp.code2) as Code2,
case
when exp.calc = '+' then
NVL(code1.value,exp.code1) + NVL(code2.value,exp.code2)
when exp.calc = '-' then
NVL(code1.value,exp.code1) - NVL(code2.value,exp.code2)
when exp.calc = '*' then
NVL(code1.value,exp.code1) * NVL(code2.value,exp.code2)
when exp.calc = '/' then
NVL(code1.value,exp.code1) / NVL(code2.value,exp.code2)
end as result
from expression exp, code code1, code code2
where exp.code1=code1.filed(+)
and exp.code2=code2.filed(+);
Expanding Werfried's Answer:
DECLARE
Formula VARCHAR2(100);
res NUMBER;
isFirst BOOLEAN := TRUE;
code1 VARCHAr2(10);
code2 VARCHAr2(10);
calc VARCHAr2(1);
BEGIN
FOR aCode IN (SELECT NO, code, code1,calc,code2 FROM table_a ORDER BY NO) LOOP
code1 := NULL;
code2 := NULl;
calc := NULL;
FOR aExp IN (SELECT * FROM table_b WHERE filed in (to_char(aCode.code1),to_char(aCode.code2))) LOOP
IF(code1 is NULL and aCode.code1 = aExp.filed)
THEN
IF(isFirst = TRUE) THEN
code1 := aExp.value;
isFirst := FALSE;
ELSE
code1 := res;
END IF;
END IF;
IF(code2 is NULL AND aCode.code2 = aExp.filed)
THEN
code2 := aExp.value;
END IF;
calc := aCode.calc;
if(code1 is NOT NULL AND code2 is NOT NULL) THEN
EXIT;
end if;
END LOOP;
Formula := NVL(code1,res)||calc||NVL(code2,aCode.code2);
EXECUTE IMMEDIATE 'begin :res := '||Formula||';end;' USING OUT res;
DBMS_OUTPUT.PUT_LINE ( aCode.NO ||': '|| Formula || ' = ' || res);
END LOOP;
END;
/
Your starting position is really ugly. Try this:
CREATE GLOBAL TEMPORARY TABLE TABLE_TEMP
(
CODE VARCHAR2(10),
formula VARCHAR2(1000)
) ON COMMIT PRESERVE ROWS;
DECLARE
res NUMBER;
BEGIN
DELETE FROM TABLE_TEMP;
INSERT INTO TABLE_TEMP
SELECT code, CODE1||CALC1||NVL(PERC1, 1)||CALC||CODE2||CALC2||NVL(PERC2, 1)
FROM TABLE1;
-- Replace placeholder with code from other lines
FOR aLine IN (SELECT code, code1 FROM TABLE1 t ORDER BY code) LOOP
FOR aExp IN (SELECT code, Formula FROM TABLE_TEMP WHERE code = aLine.CODE1) LOOP
UPDATE TABLE_TEMP SET Formula = REPLACE(Formula, aExp.code, '('||aExp.Formula||')') WHERE code = aLine.CODE;
END LOOP;
END LOOP;
-- Replace placeholder with values
FOR aLine IN (SELECT * FROM TABLE_TEMP) LOOP
FOR aExp IN (SELECT * FROM TABLE2) LOOP
UPDATE TABLE_TEMP SET Formula = REPLACE(Formula, aExp.field, aExp.VALUE);
END LOOP;
END LOOP;
END;
/
For the rest you should be able to do according my first answer.
You can use a PL/SQL function:
CREATE TABLE table_a (
NO NUMBER,
code NUMBER,
code1 VARCHAR2(10),
calc VARCHAR2(10),
code2 VARCHAR2(10));
CREATE TABLE table_b (
filed VARCHAR2(10),
VALUE NUMBER);
INSERT INTO table_a VALUES (1,1, 'a', '+', 'b');
INSERT INTO table_a VALUES (2,3, '1', '-', 'c');
INSERT INTO table_a VALUES (3,3, '2', '+', 'd');
INSERT INTO table_b VALUES ('a', 20);
INSERT INTO table_b VALUES ('b', 10);
INSERT INTO table_b VALUES ('c', 10);
INSERT INTO table_b VALUES ('d', 50);
DECLARE
Formula VARCHAR2(100);
res NUMBER;
BEGIN
FOR aCode IN (SELECT NO, code, code1||calc||code2 AS Formula FROM table_a) LOOP
Formula := aCode.Formula;
FOR aExp IN (SELECT * FROM table_b) LOOP
Formula := REPLACE(Formula, aExp.filed, aExp.VALUE);
END LOOP;
EXECUTE IMMEDIATE 'begin :res := '||Formula||';end;' USING OUT res;
DBMS_OUTPUT.PUT_LINE ( aCode.NO ||': '|| Formula || ' = ' || res);
END LOOP;
END;
Output would be this one:
1: 20+10 = 30
2: 1-10 = -9
3: 2+50 = 52
In case you need the result from SELECT, you can create a function:
CREATE OR REPLACE FUNCTION EVAL_FORMULA(Formula IN VARCHAR2) RETURN NUMBER AS
res NUMBER;
v_Formula VARCHAR2(100) := Formula;
BEGIN
FOR aExp IN (SELECT * FROM table_b) LOOP
v_Formula := REPLACE(v_Formula, aExp.filed, aExp.VALUE);
END LOOP;
EXECUTE IMMEDIATE 'begin :res := '||v_Formula||';end;' USING OUT res;
RETURN res;
END;
SELECT NO, code, EVAL_FORMULA(code1||calc||code2) AS result
FROM table_a;

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 :-)