2015-08-26 47 views
1

我有一些类似于下面显示的格式的文本文件。没有聚合的SQL数据透视表

ENTRY,1,000000,Widget 4000,1,,,2,, 
FIELD,Type,A 
FIELD,Component,Widget 4000 
FIELD,Vendor,Acme 
ENTRY,2,000000,PRODUCT XYZ,1,,,3, 
FIELD,Type,B 
FIELD,ItemAssembly,ABCD 
FIELD,Component,Product XYZ - 123 
FIELD,Description1,Product 
FIELD,Description2,XYZ-123 
FIELD,Description3,Alternate Part #440 
FIELD,Vendor,Contoso 

它们已被导入表格中,并且VARCHAR(MAX)作为唯一字段。每个ENTRY是一个“新”项目,并且所有后续的FIELD行都是该项目的属性。 FIELD旁边的数据是属性的列名称。属性右侧的数据是我想要显示的数据。

所需的输出将是:

ENTRY     Type  Component  Vendor ItemAssembly Description1 
1,000000,Widget 4000  A  Widget 4000  Acme 
2,000000,Product XYZ  B  Product XYZ-123 Contoso ABCD   Product 

我有使用下面的代码列名(有几个表,我有UNION ED一起列出所有的属性名称)。

select @cols = 
STUFF (
(select Distinct ', ' + QUOTENAME(ColName) from 
(SELECT 
    SUBSTRING(ltrim(textFileData),CHARINDEX(',', textFileData, 1)+1,CHARINDEX(',', textFileData, CHARINDEX(',', textFileData, 1)+1)- CHARINDEX(',', textFileData, 1)-1) as ColName 
    FROM [MyDatabase].[dbo].[MyTextFile] 
    where 
    (LEFT(textFileData,7) LIKE @c) 

    UNION 
    .... 
) A 
    FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,1,'') 

是一个数据透视表最好的办法吗?不需要聚合。有没有更好的方法来完成这一点?我想以列格式列出FIELD名称旁边的数据。

谢谢!

+1

您需要将文本文件重新导入到具有IDENTITY列的表中,以便您可以创建ENTRY与其后续FIELD之间的关系。与文本文件不同,表格是无序集合。 –

+0

@steve_o,我刚刚编辑了我的答案。它应该做你想要的... – Shnugo

回答

1

这里是SQL拨弄溶液: http://sqlfiddle.com/#!3/8f0b0/8

在格式(条目,字段,值)准备的原始数据,使用dynamic SQL to make pivot on unknown column count

对于字符串,MAX()足以模拟“无集合”行为。

create table t(data varchar(max)) 
insert into t values('ENTRY,1,000000,Widget 4000,1,,,2,,') 
insert into t values('FIELD,Type,A') 
insert into t values('FIELD,Component,Widget 4000') 
insert into t values('FIELD,Vendor,Acme ') 
insert into t values('ENTRY,2,000000,PRODUCT XYZ,1,,,3,') 
insert into t values('FIELD,Type,B') 
insert into t values('FIELD,ItemAssembly,ABCD') 
insert into t values('FIELD,Component,Product XYZ - 123') 
insert into t values('FIELD,Description1,Product ') 
insert into t values('FIELD,Description2,XYZ-123 ') 
insert into t values('FIELD,Description3,Alternate Part #440') 
insert into t values('FIELD,Vendor,Contoso'); 

create type preparedtype as table (entry varchar(max), field varchar(max), value varchar(max)) 


declare @prepared preparedtype 

;with identified as 
(
    select 
    row_number () over (order by (select 1)) as id, 
    substring(data, 1, charindex(',', data) - 1) as type, 
    substring(data, charindex(',', data) + 1, len(data)) as data 
    from t 
) 
, tree as 
(
    select 
    id, 
    (select max(id) 
    from identified 
    where type = 'ENTRY' 
    and id <= i.id) as parentid, 
    type, 
    data 
    from identified as i 
) 
, pivotsrc as 
(
    select 
    p.data as entry, 
    substring(c.data, 1, charindex(',', c.data) - 1) as field, 
    substring(c.data, charindex(',', c.data) + 1, len(c.data)) as value 
    from tree as p 
    inner join tree as c on c.parentid = p.id 
    where p.id = p.parentid 
    and c.parentid <> c.id 
) 
insert into @prepared 
select * from pivotsrc 

declare @dynamicPivotQuery as nvarchar(max) 
declare @columnName as nvarchar(max) 

select @columnName = ISNULL(@ColumnName + ',','') 
     + QUOTENAME(field) 
from (select distinct field from @prepared) AS fields 

set @dynamicPivotQuery = N'select * from @prepared 
pivot (max(value) for field in (' + @columnName + ')) as result' 

exec sp_executesql @DynamicPivotQuery, N'@prepared preparedtype readonly', @prepared 
0

在这里,你会回来,完全按照你的需要回来。我喜欢棘手的SQL :-)。这是一个真正的专门的singel声明调用。

DECLARE @tbl TABLE(OneCol VARCHAR(MAX)); 
INSERT INTO @tbl 
VALUES('ENTRY,1,000000,Widget 4000,1,,,2,,') 
    ,('FIELD,Type,A') 
    ,('FIELD,Component,Widget 4000') 
    ,('FIELD,Vendor,Acme ') 
    ,('ENTRY,2,000000,PRODUCT XYZ,1,,,3,') 
    ,('FIELD,Type,B') 
    ,('FIELD,ItemAssembly,ABCD') 
    ,('FIELD,Component,Product XYZ - 123') 
    ,('FIELD,Description1,Product ') 
    ,('FIELD,Description2,XYZ-123 ') 
    ,('FIELD,Description3,Alternate Part #440') 
    ,('FIELD,Vendor,Contoso'); 

WITH OneColumn AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS inx 
      ,CAST('<root><r>' + REPLACE(OneCol,',','</r><r>') + '</r></root>' AS XML) AS Split 
    FROM @tbl AS tbl 
) 
,AsParts AS 
(
    SELECT inx 
      ,Each.part.value('/root[1]/r[1]','varchar(max)') AS Part1 
      ,Each.part.value('/root[1]/r[2]','varchar(max)') AS Part2 
      ,Each.part.value('/root[1]/r[3]','varchar(max)') AS Part3 
      ,Each.part.value('/root[1]/r[4]','varchar(max)') AS Part4 
      ,Each.part.value('/root[1]/r[5]','varchar(max)') AS Part5 
    FROM OneColumn 
    CROSS APPLY Split.nodes('/root') AS Each(part) 
) 
,TheEntries AS 
(
    SELECT DISTINCT * 
    FROM AsParts 
    WHERE Part1='ENTRY' 
) 

SELECT TheEntries.Part2 + ',' + TheEntries.Part3 + ',' + TheEntries.Part4 AS [ENTRY] 
     ,MyFields.AsXML.value('(fields[1]/field[Part2="Type"])[1]/Part3[1]','varchar(max)') AS [Type] 
     ,MyFields.AsXML.value('(fields[1]/field[Part2="Component"])[1]/Part3[1]','varchar(max)') AS Component 
     ,MyFields.AsXML.value('(fields[1]/field[Part2="Vendor"])[1]/Part3[1]','varchar(max)') AS Vendor 
     ,MyFields.AsXML.value('(fields[1]/field[Part2="ItemAssembly"])[1]/Part3[1]','varchar(max)') AS ItemAssembly 
     ,MyFields.AsXML.value('(fields[1]/field[Part2="Description1"])[1]/Part3[1]','varchar(max)') AS Description1 

FROM TheEntries 
CROSS APPLY 
(

    SELECT * 
    FROM AsParts AS ap 
    WHERE ap.Part1='FIELD' AND ap.inx>TheEntries.inx 
      AND ap.inx < ISNULL((SELECT TOP 1 nextEntry.inx FROM TheEntries AS nextEntry WHERE nextEntry.inx>TheEntries.inx ORDER BY nextEntry.inx DESC),10000000) 
    ORDER BY ap.inx 
    FOR XML PATH('field'), ROOT('fields'),TYPE 
) AS MyFields(AsXML) 
+0

@steve_o,如果这有帮助,请投票和/或标记为接受,thx – Shnugo