Oracle 如何使用游标和返回表创建流水线函数?
我有这样一个表(这里有9列带“;”。这是示例表): 我需要像这样的目标表:Oracle 如何使用游标和返回表创建流水线函数?,oracle,plsql,pipelined-function,Oracle,Plsql,Pipelined Function,我有这样一个表(这里有9列带“;”。这是示例表): 我需要像这样的目标表: 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
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
我找到了以下链接:
但我不能定期创建函数。我只为一列创建函数,但不能循环,也不能调用表。以下是我的功能:
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;
你能给我一些建议吗?谢谢您可以尝试以下查询:
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;
干杯与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 )
谢谢,但我的原始表中有9列。因此,我尝试创建一个带有游标的函数。对不起,忘了写了。我现在正在编辑我的文章,通过使用PL/SQL函数,您将降低查询的性能。如果您有9个这样的列,那么您也可以在上面的查询中写入所有列。是的,这样存储数据不是一个好的做法。您必须采取措施改进数据库的设计。不幸的是,我无法更改此表,因为业务部门正在管理该表。我已经尝试创建上面的函数很久了:)因此我想学习如何再次创建此函数,如果您将创建函数,您将增加上下文切换,并且您的查询将无法很好地执行。。性能将降低。关于DB设计,如果它不在你的盘子里,也没关系。:)您使用的是哪个版本的Oracle?我使用Oracle 11g存储数据作为分隔符分隔的值是一种不好的做法,应该避免。规范化数据模型并单独存储值。阅读以了解更多请以文本形式发布所需输出。很多人无法或不会尝试访问图像转储站点中的屏幕截图。谢谢APC。我将输出更改为文本。
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 )