2016-04-08 62 views
0

這是種這個的後續問題:How do I need to change my sql to get what I want in this case?如何解析varchar2並將其變爲多行?

兩個表我有如下:

 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 

我想要寫在Table 2已在Table 1最大的日期,這將是上表如下返回獨特data查詢:

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 S作爲如下:

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; 

但我不知道如何從那裏繼續。非常感謝。

+0

80是一個固定值,或者它可以從行更改爲行? – Aleksej

+0

@Aleksej我已更新問題描述以使其更清晰。 – Dainy

+2

問題是這個可怕的桌子設計。沒關係,只要'data'只包含一些字符串給你。但這種情況並非如此;你對存儲在'data'中的子串感興趣。但是,如果您對單獨的數據感興趣,那麼將其分開存儲!不要寫一個查詢來混淆這個混亂;改變你的數據庫。 –

回答

0

您可以使用管道功能:

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代替日期(保留字)和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

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 

當然,這在計算上非常昂貴,所以對於大型表格來說並不是很好。

0

甲骨文設置

CREATE TYPE VARCHAR2_TABLE AS TABLE OF VARCHAR2(4000); 
/

CREATE OR REPLACE FUNCTION split_String(
    i_str IN VARCHAR2, 
    i_delim IN VARCHAR2 DEFAULT ',' 
) RETURN VARCHAR2_TABLE DETERMINISTIC 
AS 
    p_result  VARCHAR2_TABLE := VARCHAR2_TABLE(); 
    p_start  NUMBER(5) := 1; 
    p_end   NUMBER(5); 
    c_len CONSTANT NUMBER(5) := LENGTH(i_str); 
    c_ld CONSTANT NUMBER(5) := LENGTH(i_delim); 
BEGIN 
    IF c_len > 0 THEN 
    p_end := INSTR(i_str, i_delim, p_start); 
    WHILE p_end > 0 LOOP 
     p_result.EXTEND; 
     p_result(p_result.COUNT) := SUBSTR(i_str, p_start, p_end - p_start); 
     p_start := p_end + c_ld; 
     p_end := INSTR(i_str, i_delim, p_start); 
    END LOOP; 
    IF p_start <= c_len + 1 THEN 
     p_result.EXTEND; 
     p_result(p_result.COUNT) := SUBSTR(i_str, p_start, c_len - p_start + 1); 
    END IF; 
    END IF; 
    RETURN p_result; 
END; 
/

CREATE TABLE Table1 (id, id2, "date") AS 
SELECT 1, 2, DATE '2015-01-10' FROM DUAL UNION ALL 
SELECT 2, 5, DATE '2015-06-13' FROM DUAL UNION ALL 
SELECT 3, 9, DATE '2015-09-05' FROM DUAL UNION ALL 
SELECT 4, 10, DATE '2015-02-11' FROM DUAL UNION ALL 
SELECT 5, 26, DATE '2015-01-10' FROM DUAL UNION ALL 
SELECT 6, 65, DATE '2015-01-25' FROM DUAL; 

CREATE TABLE Table2 (id, id2, data) AS 
SELECT 1, 2,  '1.0,1.1,1.2' FROM DUAL UNION ALL 
SELECT 2, 5,  '1.0,1.1,1.2' FROM DUAL UNION ALL 
SELECT 3, 9,  '1.0,1.1,1.2' FROM DUAL UNION ALL 
SELECT 4, 10,  '3.2,3.3,4.5,6.7' FROM DUAL UNION ALL 
SELECT 5, 26,  '3.2,3.3,4.5,6.7' FROM DUAL UNION ALL 
SELECT 6, 65,  '3.2,3.3,4.5,6.7' FROM DUAL; 

查詢

SELECT t.id, 
     t.id2, 
     t."date", 
     d.column_value AS data 
FROM (
    SELECT MAX(t1.id ) KEEP (DENSE_RANK LAST ORDER BY t1."date") AS id, 
     MAX(t1.id2) KEEP (DENSE_RANK LAST ORDER BY t1."date") AS id2, 
     MAX(t1."date") AS "date", 
     t2.data 
    FROM Table1 t1 
     INNER JOIN 
     Table2 t2 
     ON (t1.id = t2.id AND t1.id2 = t2.id2) 
    GROUP BY t2.data 
) t, 
TABLE(SPLIT_STRING(t.data, ',')) d; 

輸出

 ID  ID2 date    DATA 
---------- ---------- ------------------- ---- 
     3   9 2015-09-05 00:00:00 1.0 
     3   9 2015-09-05 00:00:00 1.1 
     3   9 2015-09-05 00:00:00 1.2 
     4   10 2015-02-11 00:00:00 3.2 
     4   10 2015-02-11 00:00:00 3.3 
     4   10 2015-02-11 00:00:00 4.5 
     4   10 2015-02-11 00:00:00 6.7 
相關問題