2016-04-08 8 views
0

これはこの1つのフォローアップの質問のようなものです:次のように私が持っている二つのテーブルがある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番号が含まれていますはid2に依存する可能性があります。,で区切られています。私は上記の表のために、以下のことになるTable 1で最大の日付を持っているTable 2でユニークdataを返すクエリを、書きたい

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 

たとえば、 Aはこのようなものかもしれません
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; 

をしかし、私はそこから継続するのか分かりません。どうもありがとう。代わりに、日付(予約語)のMyDateに該当私がここで使用しています

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 

とDデータの代わりに:

+0

が一定値80か、それともそれが行から変更することができます行? – Aleksej

+0

@Aleksej私はそれをより明確にするために質問の説明を更新しました。 – Dainy

+2

問題は、この恐ろしいテーブルのデザインです。 'data'がちょっとした文字列を含んでいる限り、大丈夫でしょう。しかし、これは当てはまりません。 'data'に格納されている部分文字列に興味があります。しかし、別々のデータに興味がある場合は、別々に保管してください!この混乱を混乱させるような質問を書かないでください。データベースを変更してください。 –

答えて

0

あなたはパイプライン機能を使用することができます。サンプル:

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関数は、区切られたフィールドからインデックスを抽出するだけです。 Ex。

もちろん、これは非常に計算コストが高いので、大きなテーブルではあまり効果がありません。

0

Oracleのセットアップ

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 
関連する問題