从数字字符串生成行
Generate rows from string of numbers
我有一个 Oracle 18c table,它有这样的字符串:
select
'((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))' as multipart_lines
--There are more rows in the actual table.
from
dual
MULTIPART_LINES
-------------------------------------------------------------
((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))
-- v1 v2 v3 v4 v5
-- | part 1 | | part 2 |
- 各个坐标以空格分隔。
- 顶点(X Y Z 坐标)以逗号分隔。
- 行部分用括号括起来并用逗号分隔。
在查询中,我想为每个顶点生成行:
PART_NUM VERTEX_NUM X Y Z
---------- ---------- ---------- ---------- ----------
1 1 0 5 0
1 2 10 10 11.18
1 3 30 0 33.54
2 1 50 10 33.54
2 2 60 10 43.54
- 我想在查询中执行此操作。我不想将行插入 table.
- 很遗憾,我在数据库中没有 CREATE TYPE 权限。但是我可以创建函数(当然,内联函数也是一种选择)。
如何从字符串中的数字(顶点)生成行?
如果输入是某种标准格式 - 例如 JSON,那就简单多了。那么这项任务将是微不足道的。你有什么权力吗?
如果不是,您可以将输入转换为正确的 JSON(或类似的),或者您可以直接解决问题。我在下面说明后者,假设 Oracle 12.1 或更高版本。
with
inputs (id, multipart_lines) as (
select 2810,
'((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))'
from dual union all
select 7284, '((-2.3 0.2 3))' from dual
)
select id, part_num, vertex_num, x, y, z
from inputs
cross join lateral
( select level as part_num,
regexp_substr(multipart_lines,
'\(([^()]+)\)', 1, level, null, 1) as part
from dual
connect by level <= regexp_count(multipart_lines, '\(') - 1
)
cross join lateral
(
select level as vertex_num,
regexp_substr(part, '[^,]+', 1, level) as vertex
from dual
connect by level <= regexp_count(part, ',') + 1
)
cross join lateral
(
select to_number(regexp_substr(vertex, '[^ ]+', 1, 1)) as x,
to_number(regexp_substr(vertex, '[^ ]+', 1, 2)) as y,
to_number(regexp_substr(vertex, '[^ ]+', 1, 3)) as z
from dual
)
order by id, part_num, vertex_num -- if needed
;
输出(来自我在查询中包含的示例输入):
ID PART_NUM VERTEX_NUM X Y Z
---------- ---------- ---------- ---------- ---------- ----------
2810 1 1 0 5 0
2810 1 2 10 10 11.18
2810 1 3 30 0 33.54
2810 2 1 50 10 33.54
2810 2 2 60 10 43.54
7284 1 1 -2.3 .2 3
我看到那个数学家到了那里。我正在使用 from json_table
但我不能一次取消嵌套 2 行,否则我基本上会在那里第二次使用 row_number() 超过(按 Paru_num 分区)。
create table sample(value varchar(100));
✓
insert into sample values
('((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))')
1 行受影响
with a as (
select '["'|| regexp_replace(value,'\(|\)','') ||'"]' a from sample
),
b as (
select regexp_replace(a,', ?','","') b from a
),
c as (
SELECT value c
FROM json_table( (select b from b) , '$[*]'
COLUMNS (value PATH '$')
)
),
d as (
SELECT
c d,
instr(c,' ') s1,
instr(c,' ',instr(c,' ')+1) s2
from c)
select
substr(d,0,s1) x,
substr(d,s1+1,s2-s1) y,
substr(d,s2+1) z
from d
X | Y | Z
:-- | :-- | :----
0 | 5 | 0
10 | 10 | 11.18
30 | 0 | 33.54
50 | 10 | 33.54
60 | 10 | 43.54
with a as (
select '["'|| regexp_replace(value,'\(+|\)+','"') ||'"]' a from sample
),
b as(
select replace(a,'""','"')b from a
),
c as (
SELECT
row_number() over (order by 'zero') pn,
value c
FROM json_table( (select b from b) , '$[*]'
COLUMNS (value PATH '$')
) ),
d as (
select '["'|| pn ||' '|| regexp_replace(c,', ?','","'||pn||' ')||'"]' d from c
)
select * from d
| D |
| :----------------------------------------- |
| ["1 0 5 0","1 10 10 11.18","1 30 0 33.54"] |
| ["2 50 10 33.54","2 60 10 43.54"] |
with a as (
select '["'|| regexp_replace(value,'\(+|\)+','"') ||'"]' a from sample
),
b as(
select replace(a,'""','"')b from a
),
c as (
SELECT
row_number() over (order by 'zero') pn,
value c
FROM json_table( (select b from b) , '$[*]'
COLUMNS (value PATH '$')
) ),
d as (
select '["'|| pn ||' '|| regexp_replace(c,', ?','","'||pn||' ')||'"]' d from c
),
e as (
SELECT
row_number() over (order by 'zero') pn,
value c
FROM json_table( (select d from d) , '$[*]'
COLUMNS (value PATH '$')
) )
select * from e
ORA-01427: single-row subquery returns more than one row
db<>fiddle here
作为替代方案 - 以下是您可以如何处理输入字符串以将它们转换为正确的 JSON 字符串;那么任务就变得微不足道了。首先单独显示 JSON-ization,因为它确实是该解决方案中有意义的部分;然后在显示查询和结果后,我将通过添加 JSON 操作来完成解决方案。
with
inputs (id, multipart_lines) as (
select 2810,
'((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))'
from dual union all
select 7284, '((-2.3 0.2 3))' from dual
)
, j (id, ml) as (
select id,
regexp_replace(
regexp_replace(
regexp_replace(
regexp_replace(
regexp_replace(multipart_lines
, '\(\s*\(\s*', '[[[')
, '\s*\)\s*\)', ']]]')
, '\s*\)\s*,\s*\(\s*', '],[')
, '\s*,\s*', '],[')
, '\s+', ',')
from inputs
)
select * from j;
ID ML
----- --------------------------------------------------------------------
2810 [[[0,5,0],[10,10,11.18],[30,0,33.54]],[[50,10,33.54],[60,10,43.54]]]
7284 [[[-2.3,0.2,3]]]
您的输入应该看起来像我的子查询 j
中列 ml
中的字符串 - 然后您可以像这样处理它们:
with
inputs (id, multipart_lines) as (
........
)
, j (id, ml) as (
........
)
select id, part_num, vertex_num, x, y, z
from j,
json_table(ml, '$[*]'
columns (
part_num for ordinality,
nested path '$[*]'
columns (
vertex_num for ordinality,
x number path '$[0]',
y number path '$[1]',
z number path '$[2]'
)
)
)
order by id, part_num, vertex_num -- if needed
;
输出与我的其他答案相同。
@SolomonYakobson 在 Oracle Community post.
中提供了这个答案
with sample as (
select '((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))' as multipart_lines
--There are more rows in the actual table.
from dual
)
select part_num,
vertex_num,
to_number(regexp_substr(vertex,'[^ ]+')) x,
to_number(regexp_substr(vertex,'[^ ]+',1,2)) y,
to_number(regexp_substr(vertex,'[^ ]+',1,3)) z
from sample,
lateral(
select level part_num,
regexp_substr(multipart_lines,'\(([^()]+)',1,level,null,1) part
from dual
connect by level < regexp_count(multipart_lines,'\(')
),
lateral(
select level vertex_num,
regexp_substr(part,'[^,]+',1,level) vertex
from dual
connect by level <= regexp_count(part,',') + 1
)
/
PART_NUM VERTEX_NUM X Y Z
---------- ---------- ---------- ---------- ----------
1 1 0 5 0
1 2 10 10 11.18
1 3 30 0 33.54
2 1 50 10 33.54
2 2 60 10 43.54
以及 Oracle 19c 解决方案:
create or replace
function split_multipart_line(
p_line varchar2
)
return varchar2
sql_macro
is
begin
return q'[
select part_num,
vertex_num,
to_number(regexp_substr(vertex,'[^ ]+')) x,
to_number(regexp_substr(vertex,'[^ ]+',1,2)) y,
to_number(regexp_substr(vertex,'[^ ]+',1,3)) z
from dual,
lateral(
select level part_num,
regexp_substr(p_line,'\(([^()]+)',1,level,null,1) part
from dual
connect by level < regexp_count(p_line,'\(')
),
lateral(
select level vertex_num,
regexp_substr(part,'[^,]+',1,level) vertex
from dual
connect by level <= regexp_count(part,',') + 1
)]';
end;
/
Function created.
SQL> with sample as (
2 select '((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))' multipart_lines from dual union all
3 select '((1 2 3, 4 5 6, 7 8 9, 10 11 12),(22 33 44, 55 66 77))' multipart_lines from dual
4 )
5 select l.*
6 from sample,
7 lateral(
8 select *
9 from split_multipart_line(multipart_lines)
10 ) l
11 /
PART_NUM VERTEX_NUM X Y Z
---------- ---------- ---------- ---------- ----------
1 1 0 5 0
1 2 10 10 11.18
1 3 30 0 33.54
2 1 50 10 33.54
2 2 60 10 43.54
1 1 1 2 3
1 2 4 5 6
1 3 7 8 9
1 4 10 11 12
2 1 22 33 44
2 2 55 66 77
11 rows selected.
SQL>
你可以只用简单的字符串函数(比正则表达式快得多)和递归 sub-queries:
WITH line_bounds (id, multipart_lines, line_no, spos, epos) AS (
SELECT id,
multipart_lines,
1,
2,
INSTR(multipart_lines, ')', 2)
FROM table_name
UNION ALL
SELECT id,
multipart_lines,
line_no + 1,
epos + 2,
INSTR(multipart_lines, ')', epos + 2)
FROM line_bounds
WHERE epos > 0
)
SEARCH DEPTH FIRST BY id SET line_order,
row_bounds (id, line, line_no, row_no, spos, epos) AS (
SELECT id,
SUBSTR(multipart_lines, spos + 1, epos - spos - 1),
line_no,
1,
1,
INSTR(
SUBSTR(multipart_lines, spos + 1, epos - spos - 1),
',',
1
)
FROM line_bounds
WHERE epos > 0
UNION ALL
SELECT id,
line,
line_no,
row_no + 1,
epos + 2,
INSTR(line, ',', epos + 2)
FROM row_bounds
WHERE epos > 0
)
SEARCH DEPTH FIRST BY id, line_no SET row_order,
column_bounds (id, line, line_no, row_no, xpos, ypos, zpos, epos) AS (
SELECT id,
line,
line_no,
row_no,
spos,
INSTR(line, ' ', spos, 1),
INSTR(line, ' ', spos, 2),
epos
FROM row_bounds
)
SELECT id,
line_no,
row_no,
SUBSTR(line, xpos + 0, ypos - xpos) AS x,
SUBSTR(line, ypos + 1, zpos - ypos - 1) AS y,
CASE epos
WHEN 0
THEN SUBSTR(line, zpos + 1)
ELSE SUBSTR(line, zpos + 1, epos - zpos - 1)
END AS z
FROM column_bounds
其中,对于示例数据:
CREATE TABLE table_name (id, multipart_lines) AS
SELECT 1, '((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))' FROM DUAL UNION ALL
SELECT 2, '((0 1 0, 0 2 0, 0 3 0),(0 4 0, 0 5 0))' FROM DUAL;
输出:
ID
LINE_NO
ROW_NO
X
Y
Z
1
1
1
0
5
0
1
1
2
10
10
11.18
1
1
3
30
0
33.54
1
2
1
50
10
33.54
1
2
2
60
10
43.54
2
1
1
0
1
0
2
1
2
0
2
0
2
1
3
0
3
0
2
2
1
0
4
0
2
2
2
0
5
0
db<>fiddle here
使用SDO_GEOMETRY
解析字符串然后使用SDO_UTIL.EXTRACT
获取每个元素然后SDO_UTIL.GETVERTICES
获取顶点:
SELECT t.id,
e.elem_no,
v.id AS coord_id,
x,
y,
z
FROM ( SELECT id,
SDO_GEOMETRY('MULTILINESTRING '||multipart_lines) AS shape
FROM table_name
) t
CROSS JOIN LATERAL (
SELECT LEVEL AS elem_no,
SDO_UTIL.EXTRACT(t.shape, LEVEL) AS elem
FROM DUAL
CONNECT BY LEVEL <= SDO_UTIL.GETNUMELEM(t.shape)
) e
CROSS APPLY TABLE(SDO_UTIL.GETVERTICES(e.elem)) v
其中,对于示例数据:
CREATE TABLE table_name (id, multipart_lines) AS
SELECT 1, '((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))' FROM DUAL UNION ALL
SELECT 2, '((0 1 0, 0 2 0, 0 3 0),(0 4 0, 0 5 0))' FROM DUAL;
输出:
ID
ELEM_NO
COORD_ID
X
Y
Z
1
1
1
0
5
0
1
1
2
10
10
11.18
1
1
3
30
0
33.54
1
2
1
50
10
33.54
1
2
2
60
10
43.54
2
1
1
0
1
0
2
1
2
0
2
0
2
1
3
0
3
0
2
2
1
0
4
0
2
2
2
0
5
0
db<>fiddle here
我有一个 Oracle 18c table,它有这样的字符串:
select
'((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))' as multipart_lines
--There are more rows in the actual table.
from
dual
MULTIPART_LINES
-------------------------------------------------------------
((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))
-- v1 v2 v3 v4 v5
-- | part 1 | | part 2 |
- 各个坐标以空格分隔。
- 顶点(X Y Z 坐标)以逗号分隔。
- 行部分用括号括起来并用逗号分隔。
在查询中,我想为每个顶点生成行:
PART_NUM VERTEX_NUM X Y Z
---------- ---------- ---------- ---------- ----------
1 1 0 5 0
1 2 10 10 11.18
1 3 30 0 33.54
2 1 50 10 33.54
2 2 60 10 43.54
- 我想在查询中执行此操作。我不想将行插入 table.
- 很遗憾,我在数据库中没有 CREATE TYPE 权限。但是我可以创建函数(当然,内联函数也是一种选择)。
如何从字符串中的数字(顶点)生成行?
如果输入是某种标准格式 - 例如 JSON,那就简单多了。那么这项任务将是微不足道的。你有什么权力吗?
如果不是,您可以将输入转换为正确的 JSON(或类似的),或者您可以直接解决问题。我在下面说明后者,假设 Oracle 12.1 或更高版本。
with
inputs (id, multipart_lines) as (
select 2810,
'((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))'
from dual union all
select 7284, '((-2.3 0.2 3))' from dual
)
select id, part_num, vertex_num, x, y, z
from inputs
cross join lateral
( select level as part_num,
regexp_substr(multipart_lines,
'\(([^()]+)\)', 1, level, null, 1) as part
from dual
connect by level <= regexp_count(multipart_lines, '\(') - 1
)
cross join lateral
(
select level as vertex_num,
regexp_substr(part, '[^,]+', 1, level) as vertex
from dual
connect by level <= regexp_count(part, ',') + 1
)
cross join lateral
(
select to_number(regexp_substr(vertex, '[^ ]+', 1, 1)) as x,
to_number(regexp_substr(vertex, '[^ ]+', 1, 2)) as y,
to_number(regexp_substr(vertex, '[^ ]+', 1, 3)) as z
from dual
)
order by id, part_num, vertex_num -- if needed
;
输出(来自我在查询中包含的示例输入):
ID PART_NUM VERTEX_NUM X Y Z
---------- ---------- ---------- ---------- ---------- ----------
2810 1 1 0 5 0
2810 1 2 10 10 11.18
2810 1 3 30 0 33.54
2810 2 1 50 10 33.54
2810 2 2 60 10 43.54
7284 1 1 -2.3 .2 3
我看到那个数学家到了那里。我正在使用 from json_table
但我不能一次取消嵌套 2 行,否则我基本上会在那里第二次使用 row_number() 超过(按 Paru_num 分区)。
create table sample(value varchar(100));
✓
insert into sample values ('((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))')
1 行受影响
with a as ( select '["'|| regexp_replace(value,'\(|\)','') ||'"]' a from sample ), b as ( select regexp_replace(a,', ?','","') b from a ), c as ( SELECT value c FROM json_table( (select b from b) , '$[*]' COLUMNS (value PATH '$') ) ), d as ( SELECT c d, instr(c,' ') s1, instr(c,' ',instr(c,' ')+1) s2 from c) select substr(d,0,s1) x, substr(d,s1+1,s2-s1) y, substr(d,s2+1) z from d
X | Y | Z :-- | :-- | :---- 0 | 5 | 0 10 | 10 | 11.18 30 | 0 | 33.54 50 | 10 | 33.54 60 | 10 | 43.54
with a as ( select '["'|| regexp_replace(value,'\(+|\)+','"') ||'"]' a from sample ), b as( select replace(a,'""','"')b from a ), c as ( SELECT row_number() over (order by 'zero') pn, value c FROM json_table( (select b from b) , '$[*]' COLUMNS (value PATH '$') ) ), d as ( select '["'|| pn ||' '|| regexp_replace(c,', ?','","'||pn||' ')||'"]' d from c ) select * from d
| D | | :----------------------------------------- | | ["1 0 5 0","1 10 10 11.18","1 30 0 33.54"] | | ["2 50 10 33.54","2 60 10 43.54"] |
with a as ( select '["'|| regexp_replace(value,'\(+|\)+','"') ||'"]' a from sample ), b as( select replace(a,'""','"')b from a ), c as ( SELECT row_number() over (order by 'zero') pn, value c FROM json_table( (select b from b) , '$[*]' COLUMNS (value PATH '$') ) ), d as ( select '["'|| pn ||' '|| regexp_replace(c,', ?','","'||pn||' ')||'"]' d from c ), e as ( SELECT row_number() over (order by 'zero') pn, value c FROM json_table( (select d from d) , '$[*]' COLUMNS (value PATH '$') ) ) select * from e
ORA-01427: single-row subquery returns more than one row
db<>fiddle here
作为替代方案 - 以下是您可以如何处理输入字符串以将它们转换为正确的 JSON 字符串;那么任务就变得微不足道了。首先单独显示 JSON-ization,因为它确实是该解决方案中有意义的部分;然后在显示查询和结果后,我将通过添加 JSON 操作来完成解决方案。
with
inputs (id, multipart_lines) as (
select 2810,
'((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))'
from dual union all
select 7284, '((-2.3 0.2 3))' from dual
)
, j (id, ml) as (
select id,
regexp_replace(
regexp_replace(
regexp_replace(
regexp_replace(
regexp_replace(multipart_lines
, '\(\s*\(\s*', '[[[')
, '\s*\)\s*\)', ']]]')
, '\s*\)\s*,\s*\(\s*', '],[')
, '\s*,\s*', '],[')
, '\s+', ',')
from inputs
)
select * from j;
ID ML
----- --------------------------------------------------------------------
2810 [[[0,5,0],[10,10,11.18],[30,0,33.54]],[[50,10,33.54],[60,10,43.54]]]
7284 [[[-2.3,0.2,3]]]
您的输入应该看起来像我的子查询 j
中列 ml
中的字符串 - 然后您可以像这样处理它们:
with
inputs (id, multipart_lines) as (
........
)
, j (id, ml) as (
........
)
select id, part_num, vertex_num, x, y, z
from j,
json_table(ml, '$[*]'
columns (
part_num for ordinality,
nested path '$[*]'
columns (
vertex_num for ordinality,
x number path '$[0]',
y number path '$[1]',
z number path '$[2]'
)
)
)
order by id, part_num, vertex_num -- if needed
;
输出与我的其他答案相同。
@SolomonYakobson 在 Oracle Community post.
中提供了这个答案with sample as (
select '((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))' as multipart_lines
--There are more rows in the actual table.
from dual
)
select part_num,
vertex_num,
to_number(regexp_substr(vertex,'[^ ]+')) x,
to_number(regexp_substr(vertex,'[^ ]+',1,2)) y,
to_number(regexp_substr(vertex,'[^ ]+',1,3)) z
from sample,
lateral(
select level part_num,
regexp_substr(multipart_lines,'\(([^()]+)',1,level,null,1) part
from dual
connect by level < regexp_count(multipart_lines,'\(')
),
lateral(
select level vertex_num,
regexp_substr(part,'[^,]+',1,level) vertex
from dual
connect by level <= regexp_count(part,',') + 1
)
/
PART_NUM VERTEX_NUM X Y Z
---------- ---------- ---------- ---------- ----------
1 1 0 5 0
1 2 10 10 11.18
1 3 30 0 33.54
2 1 50 10 33.54
2 2 60 10 43.54
以及 Oracle 19c 解决方案:
create or replace
function split_multipart_line(
p_line varchar2
)
return varchar2
sql_macro
is
begin
return q'[
select part_num,
vertex_num,
to_number(regexp_substr(vertex,'[^ ]+')) x,
to_number(regexp_substr(vertex,'[^ ]+',1,2)) y,
to_number(regexp_substr(vertex,'[^ ]+',1,3)) z
from dual,
lateral(
select level part_num,
regexp_substr(p_line,'\(([^()]+)',1,level,null,1) part
from dual
connect by level < regexp_count(p_line,'\(')
),
lateral(
select level vertex_num,
regexp_substr(part,'[^,]+',1,level) vertex
from dual
connect by level <= regexp_count(part,',') + 1
)]';
end;
/
Function created.
SQL> with sample as (
2 select '((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))' multipart_lines from dual union all
3 select '((1 2 3, 4 5 6, 7 8 9, 10 11 12),(22 33 44, 55 66 77))' multipart_lines from dual
4 )
5 select l.*
6 from sample,
7 lateral(
8 select *
9 from split_multipart_line(multipart_lines)
10 ) l
11 /
PART_NUM VERTEX_NUM X Y Z
---------- ---------- ---------- ---------- ----------
1 1 0 5 0
1 2 10 10 11.18
1 3 30 0 33.54
2 1 50 10 33.54
2 2 60 10 43.54
1 1 1 2 3
1 2 4 5 6
1 3 7 8 9
1 4 10 11 12
2 1 22 33 44
2 2 55 66 77
11 rows selected.
SQL>
你可以只用简单的字符串函数(比正则表达式快得多)和递归 sub-queries:
WITH line_bounds (id, multipart_lines, line_no, spos, epos) AS (
SELECT id,
multipart_lines,
1,
2,
INSTR(multipart_lines, ')', 2)
FROM table_name
UNION ALL
SELECT id,
multipart_lines,
line_no + 1,
epos + 2,
INSTR(multipart_lines, ')', epos + 2)
FROM line_bounds
WHERE epos > 0
)
SEARCH DEPTH FIRST BY id SET line_order,
row_bounds (id, line, line_no, row_no, spos, epos) AS (
SELECT id,
SUBSTR(multipart_lines, spos + 1, epos - spos - 1),
line_no,
1,
1,
INSTR(
SUBSTR(multipart_lines, spos + 1, epos - spos - 1),
',',
1
)
FROM line_bounds
WHERE epos > 0
UNION ALL
SELECT id,
line,
line_no,
row_no + 1,
epos + 2,
INSTR(line, ',', epos + 2)
FROM row_bounds
WHERE epos > 0
)
SEARCH DEPTH FIRST BY id, line_no SET row_order,
column_bounds (id, line, line_no, row_no, xpos, ypos, zpos, epos) AS (
SELECT id,
line,
line_no,
row_no,
spos,
INSTR(line, ' ', spos, 1),
INSTR(line, ' ', spos, 2),
epos
FROM row_bounds
)
SELECT id,
line_no,
row_no,
SUBSTR(line, xpos + 0, ypos - xpos) AS x,
SUBSTR(line, ypos + 1, zpos - ypos - 1) AS y,
CASE epos
WHEN 0
THEN SUBSTR(line, zpos + 1)
ELSE SUBSTR(line, zpos + 1, epos - zpos - 1)
END AS z
FROM column_bounds
其中,对于示例数据:
CREATE TABLE table_name (id, multipart_lines) AS
SELECT 1, '((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))' FROM DUAL UNION ALL
SELECT 2, '((0 1 0, 0 2 0, 0 3 0),(0 4 0, 0 5 0))' FROM DUAL;
输出:
ID LINE_NO ROW_NO X Y Z 1 1 1 0 5 0 1 1 2 10 10 11.18 1 1 3 30 0 33.54 1 2 1 50 10 33.54 1 2 2 60 10 43.54 2 1 1 0 1 0 2 1 2 0 2 0 2 1 3 0 3 0 2 2 1 0 4 0 2 2 2 0 5 0
db<>fiddle here
使用SDO_GEOMETRY
解析字符串然后使用SDO_UTIL.EXTRACT
获取每个元素然后SDO_UTIL.GETVERTICES
获取顶点:
SELECT t.id,
e.elem_no,
v.id AS coord_id,
x,
y,
z
FROM ( SELECT id,
SDO_GEOMETRY('MULTILINESTRING '||multipart_lines) AS shape
FROM table_name
) t
CROSS JOIN LATERAL (
SELECT LEVEL AS elem_no,
SDO_UTIL.EXTRACT(t.shape, LEVEL) AS elem
FROM DUAL
CONNECT BY LEVEL <= SDO_UTIL.GETNUMELEM(t.shape)
) e
CROSS APPLY TABLE(SDO_UTIL.GETVERTICES(e.elem)) v
其中,对于示例数据:
CREATE TABLE table_name (id, multipart_lines) AS
SELECT 1, '((0 5 0, 10 10 11.18, 30 0 33.54),(50 10 33.54, 60 10 43.54))' FROM DUAL UNION ALL
SELECT 2, '((0 1 0, 0 2 0, 0 3 0),(0 4 0, 0 5 0))' FROM DUAL;
输出:
ID ELEM_NO COORD_ID X Y Z 1 1 1 0 5 0 1 1 2 10 10 11.18 1 1 3 30 0 33.54 1 2 1 50 10 33.54 1 2 2 60 10 43.54 2 1 1 0 1 0 2 1 2 0 2 0 2 1 3 0 3 0 2 2 1 0 4 0 2 2 2 0 5 0
db<>fiddle here