如何解析varchar2并将其分成多行?

时间:2016-04-08 14:21:32

标签: sql database oracle

这是这个问题的后续问题:How do I need to change my sql to get what I want in this case?

我有两张表如下:

      Table 1
id  id2    date
1    2   2015-01-10
2    5   2015-06-13
3    9   2015-09-05
4    10  2015-02-11
5    26  2015-01-10
6    65  2015-01-25

      Table 2
id  id2    data(varchar2)
1    2       A
2    5       A
3    9       A
4    10      B
5    26      B
6    65      B

Table 2中的数据属于varchar2类型,其中包含N个数字,由,分隔,其中N可能依赖于id2。例如,A可能是这样的:

1.0,1.1,1.2,1.3,1.4,1.5,2.6,2.7,2.8,2.9, ...(ommitted)..., 9.5,9.9

我想编写一个查询,返回data中唯一Table 2的{​​{1}},其最大日期为Table 1,如上表所示:

id2    date         number
2   2015-01-10        1.0
2   2015-01-10        1.1
2   2015-01-10        1.2
2   2015-01-10        1.3
      ...
2   2015-01-10        9.5
2   2015-01-10        9.9
10  2015-02-11        ***
10  2015-02-11        ***
      ...
10  2015-02-11        ***

每个唯一id2在查询输出中显示N次。

我能够根据模糊的答案得到唯一的id2,如下所示:

select * from (
    select 
        t2.id2, t1.date, t2.data, 
        row_number() over (partition by t2.data order by t1.date desc) rn
    from table1 t1
    join table2 t2 on t1.id2 = t2.id2
) t where rn = 1;

但我不知道如何从那里继续。非常感谢。

2 个答案:

答案 0 :(得分:0)

您可以使用管道功能:<​​/ p>

CREATE or replace TYPE test_type AS TABLE OF varchar2(40)

CREATE or replace FUNCTION test_func (d VARCHAR2)
   RETURN test_type
   PIPELINED
IS
BEGIN
   FOR C1 IN (    SELECT REGEXP_SUBSTR (d, '[^,]+', 1, LEVEL) x
                    FROM DUAL
              CONNECT BY REGEXP_SUBSTR (d, '[^,]+', 1, LEVEL) IS NOT NULL)
   LOOP
      PIPE ROW (c1.x);
   END LOOP;
END;
/

WITH test
     AS ( your_query_here )
SELECT x.id2, x.mydate, y.*
  FROM test x, TABLE (test_func (x.d)) y

我在这里使用mydate而不是date(保留字)和d而不是数据。样品:

WITH test
     AS (SELECT 2 AS id2, SYSDATE AS mydate, '1.0,1.1,1.2,11,1.4,1.5,2.6,2.7,2.8,2.9,44,55' AS d FROM DUAL
         UNION ALL
         SELECT 3 AS id2, SYSDATE + 1 AS mydate, '19.5,19.9,11.5,11.1,21.2,33,1.4,1.5,2.6,2.7,2.8,2.9' AS d
           FROM DUAL
         UNION ALL
         SELECT 4 AS id2, SYSDATE + 1 AS mydate, '9.5,9.9,1.5,1.1,1.2,66,1.4,1.5,2.6,2.7,2.8,2.9' AS d
           FROM DUAL)
SELECT x.id2, x.mydate, y.*
  FROM test x, TABLE (test_func (x.d)) y

一些链接:

How to split comma separated string and pass to IN clause of select statement

Using Pipelined and Parallel Table Functions

答案 1 :(得分:0)

就像其他人所说的那样,如果桌子设计得当,你就不必这样做了。如果这是一个经常使用的对象,那么最好解决实际问题。但是,如果这只是一次性的事情,您可以使用递归查询。这在Teradata中有效,Oracle可能有类似的模拟。

WITH RECURSIVE processDelimited(ID2,dataVarchar2,parsedElement,indexElement) AS (
    SELECT 
     ID2
    ,dataVarchar2
    ,strtok(dataVarchar2,',',1)
    ,0
    FROM tableWithDelimitedColumns
    UNION ALL
    SELECT 
     ID2
    ,dataVarchar2
    ,strtok(dataVarchar2,',',indexElement + 1)    
    ,indexElement + 1
    FROM processDelimited
    WHERE indexElement < 120
    )

SELECT 
 processDelimited.ID2
,parsedElement
,indexElement
FROM processDelimited
INNER JOIN (
    SELECT 
     ID2
    ,DATE_COL
    FROM table1
    QUALIFY row_number() OVER(PARTITION BY ID2 ORDER BY DATE_COL DESC) = 1) AS id2MaxDate
    ON processDelimited.ID2 = id2MaxDate.ID2
WHERE parsedElement IS NOT NULL
ORDER BY processDelimited.ID2

基本上你只是从空字符串开始并循环追加行。

使用的TD函数只是从分隔字段中提取索引。实施例

strtok('1,2,3,4,5',',',2) = 2

当然,这在计算上非常昂贵,因此对于大型桌子来说并不是很好。