如何使用光标创建流水线函数并返回表?
Posted
技术标签:
【中文标题】如何使用光标创建流水线函数并返回表?【英文标题】:How can I create pipelined function with cursor, and return table? 【发布时间】:2019-07-16 08:16:00 【问题描述】:我有一个类似的表(这里有 9 列,带有 ';'。这是示例表):
create table mytable as (
select
1 ID,
'T1;T2;T3' column_1,
'B1;B5;B10;B13' column_2
from dual
union all
select
2 ID,
'T7;T8;T9;T10,T11',
'B2;B3;B5'
from dual
)
我需要像这样的目标表:
ID column_1 column_2
1 T1 B1
1 T1 B5
1 T1 B10
1 T1 B13
1 T2 B1
1 T2 B5
1 T2 B10
1 T2 B13
1 T3 B1
1 T3 B5
1 T3 B10
1 T3 B13
2 T7 B2
2 T7 B3
2 T7 B5
2 T8 B2
2 T8 B3
2 T8 B5
2 T9 B2
2 T9 B3
2 T9 B5
2 T10 B2
2 T10 B3
2 T10 B5
2 T11 B2
2 T11 B3
2 T11 B5
我找到了以下链接: pipelined function with cursor parameter oracle 但我无法定期创建函数。我只为一列创建函数但不能循环,也不能调用表。这是我的功能:
create or replace function fun_pipelined(i_str in varchar2)
RETURN sys.odcivarchar2list PIPELINED
IS
v_arr dbms_sql.varchar2_table;
v_i long;
v_cnt number;
i number;
begin
v_arr := pl.split(nvl(i_str,' ,'),',');
v_cnt := regexp_count(nvl(i_str,','), ',') + 1;
i := 1;
loop
exit when i > v_cnt;
v_i := trim(v_arr(i));
pipe row (v_i);
i := i+1;
end loop;
end;
你能给我一些建议吗?谢谢
【问题讨论】:
您使用的是哪个版本的 Oracle? 我使用的是 Oracle 11g 将数据存储为分隔符分隔值是一种不好的做法,应该避免。规范化您的数据模型并分别存储值。阅读this了解更多 请将您需要的输出作为文本发布。许多人不能或不会尝试访问图像转储站点中的屏幕截图。 谢谢 APC。我将输出更改为文本。 【参考方案1】:您可以尝试以下查询:
WITH DATAA AS (
SELECT DISTINCT
ID,
REGEXP_SUBSTR(COLUMN_1, '[^;]+', 1, LEVEL) COLUMN_1,
REGEXP_SUBSTR(COLUMN_2, '[^;]+', 1, LEVEL) COLUMN_2
FROM
MYTABLE
CONNECT BY REGEXP_SUBSTR(COLUMN_1, '[^;]+', 1, LEVEL) IS NOT NULL
OR REGEXP_SUBSTR(COLUMN_2, '[^;]+', 1, LEVEL) IS NOT NULL
)
SELECT
ID,
COLUMN_1,
COLUMN_2
FROM
(
SELECT DISTINCT
D1.ID,
D1.COLUMN_1,
D2.COLUMN_2
FROM
DATAA D1
JOIN DATAA D2 ON ( D1.ID = D2.ID )
)
WHERE
( COLUMN_1 IS NOT NULL
AND COLUMN_2 IS NOT NULL )
ORDER BY
ID,
COLUMN_1;
db<>fiddle demo
干杯
【讨论】:
谢谢,但我的原始表中有 9 列。因此我尝试用光标创建一个函数。抱歉忘记写了。我正在编辑我的帖子 通过使用 PL/SQL 函数,您会降低查询的性能。如果您有 9 个这样的列,那么您也可以在上述查询中写入所有列。是的,这不是像这样存储数据的好习惯。您必须采取措施改进数据库的设计。 很遗憾,我无法更改此表,因为业务部门管理该表。我已经尝试创建上述函数很长时间了:) 因此我想了解如何创建这个 再次,如果您将创建函数,您将增加上下文切换,您的查询将无法正常执行.. 性能将下降。关于数据库设计,如果它不在你的盘子里也没关系。 :)【参考方案2】:比起 Oracle 的 connect by,我更喜欢公用表表达式。这是您使用 CTE 得到的结果。
WITH
mytable AS
(SELECT 1 id, 'T1;T2;T3' column_1, 'B1;B5;B10;B13' column_2 FROM DUAL
UNION ALL
SELECT 2 id, 'T7;T8;T9;T10;T11', 'B2;B3;B5' FROM DUAL),
mytable2 AS( SELECT id, column_1 || ';' AS column_1, column_2 || ';' AS column_2 FROM mytable ),
splitset1 ( id
, column_1
, column_2
, REMAINDER ) AS
(SELECT id
, SUBSTR( column_1
, 1
, INSTR( column_1, ';' ) - 1 ) AS column1
, column_2
, SUBSTR( column_1, INSTR( column_1, ';' ) + 1 ) AS REMAINDER
FROM mytable2
UNION ALL
SELECT id
, SUBSTR( REMAINDER
, 1
, INSTR( REMAINDER, ';' ) - 1 )
, column_2
, SUBSTR( REMAINDER, INSTR( REMAINDER, ';' ) + 1 )
FROM splitset1
WHERE REMAINDER IS NOT NULL),
splitset2 ( id
, column_1
, column_2
, REMAINDER ) AS
(SELECT id
, column_1
, SUBSTR( column_2
, 1
, INSTR( column_2, ';' ) - 1 ) AS column2
, SUBSTR( column_2, INSTR( column_2, ';' ) + 1 ) AS REMAINDER
FROM splitset1
UNION ALL
SELECT id
, column_1
, SUBSTR( REMAINDER
, 1
, INSTR( REMAINDER, ';' ) - 1 )
, SUBSTR( REMAINDER, INSTR( REMAINDER, ';' ) + 1 )
FROM splitset2
WHERE REMAINDER IS NOT NULL)
SELECT id
, column_1
, column_2
FROM splitset2
ORDER BY id
, CAST( SUBSTR( column_1, 2 ) AS NUMBER )
, CAST( SUBSTR( column_2, 2 ) AS NUMBER )
如果您拥有 Oracle 12,则可以使用 SQL 函数使您的 SQL 非常易读,但会产生一些开销:
WITH
FUNCTION after( p_value IN VARCHAR2, p_separator IN VARCHAR2 DEFAULT ';' )
RETURN VARCHAR2 AS
l_pos INTEGER;
BEGIN
l_pos := INSTR( p_value, p_separator );
RETURN CASE WHEN l_pos > 0 THEN SUBSTR( p_value, l_pos + 1 ) ELSE NULL END;
END after;
FUNCTION before( p_value IN VARCHAR2, p_separator IN VARCHAR2 DEFAULT ';' )
RETURN VARCHAR2 AS
l_pos INTEGER;
BEGIN
l_pos := INSTR( p_value, p_separator );
RETURN CASE
WHEN l_pos > 0
THEN
SUBSTR( p_value
, 1
, l_pos - 1 )
ELSE
p_value
END;
END before;
mytable AS
(SELECT 1 id, 'T1;T2;T3' column_1, 'B1;B5;B10;B13' column_2 FROM DUAL
UNION ALL
SELECT 2 id, 'T7;T8;T9;T10;T11', 'B2;B3;B5' FROM DUAL),
mytable2 AS( SELECT id, column_1 || ';' AS column_1, column_2 || ';' AS column_2 FROM mytable ),
splitset1 ( id
, column_1
, column_2
, REMAINDER ) AS
(SELECT id
, before( column_1 ) AS column1
, column_2
, after( column_1 ) AS REMAINDER
FROM mytable2
UNION ALL
SELECT id
, before( REMAINDER )
, column_2
, after( REMAINDER )
FROM splitset1
WHERE REMAINDER IS NOT NULL),
splitset2 ( id
, column_1
, column_2
, REMAINDER ) AS
(SELECT id
, column_1
, before( column_2 ) AS column2
, after( column_2 ) AS REMAINDER
FROM splitset1
UNION ALL
SELECT id
, column_1
, before( REMAINDER )
, after( REMAINDER )
FROM splitset2
WHERE REMAINDER IS NOT NULL)
SELECT id
, column_1
, column_2
FROM splitset2
ORDER BY id
, CAST( SUBSTR( column_1, 2 ) AS NUMBER )
, CAST( SUBSTR( column_2, 2 ) AS NUMBER )
【讨论】:
以上是关于如何使用光标创建流水线函数并返回表?的主要内容,如果未能解决你的问题,请参考以下文章