2017-10-10 15 views
0

SQL Serverのステージング領域に存在する区切りフィールドにデータを含むデータソースがあります。私はこのデータを多くの行に変換して作業しやすいようにしたいと思います。これは、この区切られたデータが存在する複数のフィールドがあるという点で、同様のトピックに関する他の多くの質問と回答とは異なります。ここに私のデータがどのように見えるかの例です:複数の区切りフィールドをSQL Serverの行に変換する

ID | Field | Value 
---+-------+------ 
1 | a,b,c | 1,2,3 
2 | a,c | 5,2 

は、そして、これは、所望の出力です:

ID | Field | Value 
---+-------+------  
1 | a  | 1 
1 | b  | 2 
1 | c  | 3 
2 | a  | 5 
2 | c  | 2 

私のコードは、これまで使用しています、ここで述べたようなXML構文解析方法:Turning a Comma Separated string into individual rows私が必要これを拡張して各フィールドを、それぞれのIDのrow_numberを生成し、IDとこのrow_numberに基づいて一致させて対応する値に結合します。

私の問題は、それは痛みを伴いますので、誰かがもっとパフォーマンスの良い方法を持っているのだろうかと疑問に思っています。

select  
    [Value].ID, [Field], [Value] 
from   
    (select 
     A.ID, Split.a.value('.', 'varchar(100)') as [Value], 
     row_number() over (partition by ID order by Split.a) as RowNumber 
    from 
     (select 
       ID, cast('<M>' + replace([Value], ',', '</M><M>') + '</M>' as xml) as [Value] 
      from 
       #source_table 
      where 
       [Field] not like '%[<>&%]%' and [Value] not like '%[<>&%]%') as A 
    cross apply 
     [Value].nodes ('/M') as Split(a) 
    ) [Value] 
inner join 
    (
     select 
      A.ID, Split.a.value('.', 'varchar(100)') as [Field], 
      row_number() over (partition by A.ID order by Split.a) as RowNumber 
     from 
      (select 
       ID, cast('<M>' + replace([Field], ',', '</M><M>') + '</M>' as xml) as [Field] 
      from 
       #source_table 
      where 
       [Field] not like '%[<>&%]%' and [Value] not like '%[<>&%]%') as A 
     cross apply 
      [Field].nodes ('/M') as Split(a) 
    ) [Field] on [Value].ID = [Field].ID and [Value].RowNumber = [Field].RowNumber 

答えて

0

一つの方法は、再帰CTEです:

with cte as (
     select id, cast(NULL as varchar(max)) as field, cast(NULL as varchar(max)) as value, field as field_list, value as value_list, 0 as lev 
     from t 
     union all 
     select id, left(field_list, charindex(',', field_list + ',') - 1), 
      left(value_list, charindex(',', value_list + ',') - 1), 
      substring(field_list, charindex(',', field_list + ',') + 1, len(field_list)), 
      substring(value_list, charindex(',', value_list + ',') + 1, len(value_list)), 
      1 + lev 
     from cte 
     where field_list <> '' and value_list <> '' 
    ) 
select * 
from cte 
where lev > 0; 

Hereは、それがどのように動作するかの一例です。

+0

フムを...あなたのソリューションは、ビットバギーのようだ:それはIDを返しますが、フィールドにも値でもありません...あなたが最初の反復でNULLを選択し、次にNULLの部分文字列を選択したために、最も確かに... – Tyron78

+0

パーフェクト。同様に動作するバージョンを追加しました。 – Tyron78

1

ここでは、Jeff Modenのスプリッタを使用する方法があります。 http://www.sqlservercentral.com/articles/Tally+Table/72993/そのスプリッタの優れた特徴の1つは、各要素の順序位置を返して、結合などに使用できることです。

いくつかのデータから始まります。

declare @Something table 
(
    ID int 
    , Field varchar(50) 
    , Value varchar(50) 
) 

insert @Something values 
(1, 'a,b,c', '1,2,3') 
, (2, 'a,c', '5,2') 
; 

区切られたデータが2セットあるので、区切られた値のセットごとにこれを分割する必要があります。ここでは、このスプリッタを活用してこれを達成する方法を示します。

with Fields as 
(
    select * 
    from @Something s 
    cross apply dbo.DelimitedSplit8K(s.Field, ',') f 
) 
, Value as 
(
    select * 
    from @Something s 
    cross apply dbo.DelimitedSplit8K(s.Value, ',') v 
) 

select f.ID 
    , Field = f.Item 
    , Value = v.Item 
from Fields f 
join Value v on v.ItemNumber = f.ItemNumber and v.ID = f.ID 

それはあなたがそれはで動作するように苦痛であるため、区切り正規化とではありませんので、それはあなたのソースデータを移入されているものは何でも、プロセス変更できるかどうかを確認するために最善だろう、すべての可能なの場合。ここ@Gordon Linoffのクエリに別の再帰CTE基づか

0

DECLARE @t TABLE(
    ID int 
    ,Field VARCHAR(MAX) 
    ,Value VARCHAR(MAX) 
) 

INSERT INTO @t VALUES 
(1, 'a,b,c', '1,2,3') 
,(2, 'a,c', '5,2') 
,(3, 'x', '7'); 


with cte as (
     select ID 
      ,SUBSTRING(Field, 1, CASE WHEN CHARINDEX(',', Field) > 0 THEN CHARINDEX(',', Field)-1 ELSE LEN(Field) END) AS Field 
      ,SUBSTRING(Value, 1, CASE WHEN CHARINDEX(',', Value) > 0 THEN CHARINDEX(',', Value)-1 ELSE LEN(Value) END) AS Value 
      ,SUBSTRING(Field, CASE WHEN CHARINDEX(',', Field) > 0 THEN CHARINDEX(',', Field)+1 ELSE 1 END, LEN(Field)-CASE WHEN CHARINDEX(',', Field) > 0 THEN CHARINDEX(',', Field) ELSE 0 END) as field_list 
      ,SUBSTRING(Value, CASE WHEN CHARINDEX(',', Value) > 0 THEN CHARINDEX(',', Value)+1 ELSE 1 END, LEN(Value)-CASE WHEN CHARINDEX(',', Value) > 0 THEN CHARINDEX(',', Value) ELSE 0 END) as value_list 
      ,0 as lev 
     from @t 
     WHERE CHARINDEX(',', Field) > 0 
     UNION ALL 
     select ID 
      ,SUBSTRING(field_list, 1, CASE WHEN CHARINDEX(',', field_list) > 0 THEN CHARINDEX(',', field_list)-1 ELSE LEN(field_list) END) AS Field 
      ,SUBSTRING(value_list, 1, CASE WHEN CHARINDEX(',', value_list) > 0 THEN CHARINDEX(',', value_list)-1 ELSE LEN(value_list) END) AS Value 
      ,CASE WHEN CHARINDEX(',', field_list) > 0 THEN SUBSTRING(field_list, CHARINDEX(',', field_list)+1, LEN(field_list)-CHARINDEX(',', field_list)) ELSE '' END as field_list 
      ,CASE WHEN CHARINDEX(',', value_list) > 0 THEN SUBSTRING(value_list, CHARINDEX(',', value_list)+1, LEN(value_list)-CHARINDEX(',', value_list)) ELSE '' END as value_list 
      ,lev + 1 
     from cte 
     WHERE LEN(field_list) > 0 
    ) 
select ID, Field, Value 
from cte 
UNION ALL 
SELECT ID, Field, Value 
    FROM @t 
    WHERE CHARINDEX(',', Field) = 0 
ORDER BY ID, Field 
OPTION (MAXRECURSION 0) 
関連する問題