SQL 数据透视 table 无聚合

SQL Pivot table without aggregate

我有许多文本文件,其格式类似于下图所示。

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

它们已被导入到 table 中,VARCHAR(MAX) 是唯一的字段。每个 ENTRY 是一个 "new" 项目,所有后续 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

我使用下面的代码得到了列名称(有几个 table 我已经 UNION 编辑在一起以列出所有 属性 名称)。

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,'')

枢轴 table 是执行此操作的最佳方法吗?不需要聚合。有没有更好的方法来完成这个?我想以列格式列出 FIELD 名称旁边的数据。

谢谢!

给你,这完全按照你的需要回来了。我喜欢棘手的 SQL :-)。这是一个真正的临时单语句调用。

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)

下面是 SQL fiddle 中的解决方案: http://sqlfiddle.com/#!3/8f0b0/8

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

在这种情况下,

字符串的 MAX() 足以模拟 "without aggregate" 行为。

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