【问题标题】:SQL Pivot table without aggregate没有聚合的 SQL 数据透视表
【发布时间】:2015-08-27 14:30:32
【问题描述】:

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

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

我使用下面的代码获得了列名(我有几个表 UNIONed 一起列出所有属性名)。

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

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

谢谢!

【问题讨论】:

  • 您需要将文本文件重新导入到具有 IDENTITY 列的表中,以便您可以创建 ENTRY 与其后续 FIELDs 之间的关系。与文本文件不同,表格是无序集。
  • @steve_o,我刚刚编辑了我的答案。它应该做你想做的......

标签: sql-server pivot aggregate


【解决方案1】:

这是 SQL fiddle 中的解决方案: 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

【讨论】:

    【解决方案2】:

    在这里,它会完全按照您的需要返回。我喜欢棘手的 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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-09-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-03
      • 1970-01-01
      • 2018-11-27
      • 2022-12-03
      • 1970-01-01
      相关资源
      最近更新 更多