oraclesql中的子串查询

oraclesql中的子串查询,sql,oracle,split,oracle11g,pivot,Sql,Oracle,Split,Oracle11g,Pivot,我有一个表格,有两列,格式如下。我想从Col1中获取所有字符串,这些字符串作为单独的列从col2的单词末尾开始 例如:如果Col1具有ABC:EFG:MNO:XYZ和Col2具有MNO,则输出将具有L1作为XYZ(字符串Col2和Col1结尾之间的数据) 可乐 可乐 ABC:EFG:MNO:XYZ MNO PQR:NOM:XYN:SDF:RST:EDF 笔名 按照您的说法,这样的查询可以完成这项工作。阅读代码中的注释 SQL> with 2 test (col1, col2) as

我有一个表格,有两列,格式如下。我想从
Col1
中获取所有字符串,这些字符串作为单独的列从
col2
的单词末尾开始

例如:如果
Col1
具有
ABC:EFG:MNO:XYZ
Col2
具有
MNO
,则输出将具有
L1
作为
XYZ
(字符串
Col2
Col1
结尾之间的数据)

可乐 可乐 ABC:EFG:MNO:XYZ MNO PQR:NOM:XYN:SDF:RST:EDF 笔名
按照您的说法,这样的查询可以完成这项工作。阅读代码中的注释

SQL> with
  2  test (col1, col2) as
  3    -- sample data; you already have that in your table; don't type it
  4    (select 'ABC:EFG:MNO:XYZ'        , 'MNO' from dual union all
  5     select 'PQR:NOM:XYN:SDF:RST:EDF', 'NOM' from dual
  6    ),
  7  remainder as
  8    -- fetch part of COL1 that follows the COL2 value
  9    (select col1, col2,
 10       substr(col1, instr(col1, col2) + length(col2) + 1) col
 11     from test
 12    )
 13  -- finally, extract up to 5 "words" (as your example suggests) from COL
 14  select regexp_substr(col, '\w+', 1, 1) l1,
 15         regexp_substr(col, '\w+', 1, 2) l2,
 16         regexp_substr(col, '\w+', 1, 3) l3,
 17         regexp_substr(col, '\w+', 1, 4) l4,
 18         regexp_substr(col, '\w+', 1, 5) l5
 19  from remainder;

L1    L2    L3    L4    L5
----- ----- ----- ----- -----
XYZ
XYN   SDF   RST   EDF

SQL>

在将列
col1
按冒号拆分后,您可以创建一个存储函数,以便通过使用条件聚合动态地透视结果,例如

CREATE OR REPLACE FUNCTION Get_Splitted_Columns RETURN SYS_REFCURSOR IS
  v_recordset SYS_REFCURSOR;
  v_sql       VARCHAR2(32767);
  v_cols      VARCHAR2(32767);
BEGIN
  SELECT LISTAGG('MAX( CASE WHEN rn2 =  '''||level||''' THEN col1 END ) AS "L_'||level||'"' , ',' )
                 WITHIN GROUP ( ORDER BY level )
    INTO v_cols
    FROM dual
   CONNECT BY level <= ( SELECT MAX(REGEXP_COUNT(col1,':',INSTR(col1,col2))) FROM tab );
                            
  v_sql :='WITH t AS
          (
           SELECT REGEXP_SUBSTR(col1,''[^:]+'',1,level) AS col1, col2, level AS rn,
                  DENSE_RANK() OVER (ORDER BY col1) AS dr              
             FROM tab
          CONNECT BY level <= REGEXP_COUNT(col1,'':'')+1
              AND PRIOR SYS_GUID() IS NOT NULL
              AND PRIOR col1 = col1
          ), t2 AS
          (
           SELECT t.*,MAX(CASE WHEN col1=col2 THEN rn END) OVER (PARTITION BY dr) AS max_rn
             FROM t
          ), t3 AS
          (
           SELECT dr, max_rn, rn, rn - max_rn AS rn2, col1, col2 
             FROM t2 tt
            WHERE rn > ( SELECT MAX(max_rn) FROM t2 WHERE dr = tt.dr )
          )
          SELECT '||v_cols||'
            FROM t3
           GROUP BY dr';

  OPEN v_recordset FOR v_sql;
  RETURN v_recordset;
END;
/
从SQL开发人员的命令行


如果没有比L5更大的字母,并且总是有三个字母,只需用第一个匹配的偏移量硬编码即可

WITH tab AS (
  SELECT 'ABC:EFG:MNO:XYZ'         AS Col1, 'MNO' AS Col2 FROM DUAL
  UNION
  SELECT 'PQR:NOM:XYN:SDF:RST:EDF' AS Col1, 'NOM' AS Col2 FROM DUAL
)

SELECT tab.Col1,
       tab.Col2,
       SUBSTR(tab.Col1, NULLIF(INSTR(tab.Col1, tab.Col2), 0) +  4, 3) AS L1,
       SUBSTR(tab.Col1, NULLIF(INSTR(tab.Col1, tab.Col2), 0) +  8, 3) AS L2,
       SUBSTR(tab.Col1, NULLIF(INSTR(tab.Col1, tab.Col2), 0) + 12, 3) AS L3,
       SUBSTR(tab.Col1, NULLIF(INSTR(tab.Col1, tab.Col2), 0) + 16, 3) AS L4,
       SUBSTR(tab.Col1, NULLIF(INSTR(tab.Col1, tab.Col2), 0) + 20, 3) AS L5
FROM   tab;

找到与INSTR的第一个匹配项,并使用一个3字符的子字符串,从match+条目*4开始。如果没有匹配,则为空。

您能用文字说明如何从输入到输出吗?我假设
col1
是一个由冒号分隔的字符串。但为什么输出中会排除某些值?为什么
l1
列中的某些值与
l2
列中的某一列相对?此外,结果集不能有两个同名列,因此不能有四个列全部命名为
l2
。您是否打算将列命名为
l1
l5
?您是否打算将第二行中的
col1
值命名为
PQR:NOW:…
,而不是
PQR:NOM
,以便逻辑是解析出出现在
col2
值之后的前5个
col1
值并显示他们在输出中吗?嗨。我已经补充了细节。是的,第二排还可以。结果中名为
l2
的四列是什么?您是否保证结果集中正好有5列?是的,最多只有5列。有些可能在L1中有数据,有些可能在L5之前有数据,但没有任何内容超过L5
WITH tab AS (
  SELECT 'ABC:EFG:MNO:XYZ'         AS Col1, 'MNO' AS Col2 FROM DUAL
  UNION
  SELECT 'PQR:NOM:XYN:SDF:RST:EDF' AS Col1, 'NOM' AS Col2 FROM DUAL
)

SELECT tab.Col1,
       tab.Col2,
       SUBSTR(tab.Col1, NULLIF(INSTR(tab.Col1, tab.Col2), 0) +  4, 3) AS L1,
       SUBSTR(tab.Col1, NULLIF(INSTR(tab.Col1, tab.Col2), 0) +  8, 3) AS L2,
       SUBSTR(tab.Col1, NULLIF(INSTR(tab.Col1, tab.Col2), 0) + 12, 3) AS L3,
       SUBSTR(tab.Col1, NULLIF(INSTR(tab.Col1, tab.Col2), 0) + 16, 3) AS L4,
       SUBSTR(tab.Col1, NULLIF(INSTR(tab.Col1, tab.Col2), 0) + 20, 3) AS L5
FROM   tab;