从表中选择值作为列标题

Ank*_*tri 10 sql t-sql sql-server pivot sql-server-2008

我有一个表'propertyvalues'如下:

ID  FileID  Property  Value
1 x Name 1.pdf
2 x Size 12567
3 x Type application/pdf
4 y Name 2.pdf
5 y Size 23576
6 y Type application/pdf
......
and so on

如何在上面的表上编写SQL查询以获取如下所示的结果

 
FileID  Name     Size      Type
x 1.pdf 12567 application/pdf
y 2.pdf 23576 application/pdf

Tar*_*ryn 11

您没有指定RDBMS,如果您知道要转换的列数,那么您可以对值进行硬编码:

select FileId,
  max(case when property = 'Name' then value end) Name,
  max(case when property = 'Size' then value end) Size,
  max(case when property = 'Type' then value end) Type
from yourtable
group by FileId
Run Code Online (Sandbox Code Playgroud)

这基本上是一个PIVOT函数,一些RDBMS会有一个PIVOT,如果你这样做就可以使用以下内容,PIVOT可以在SQL Server,Oracle中使用:

select *
from 
(
  select FileId, Property, Value
  from yourTable
) x
pivot
(
  max(value)
  for property in ([Name], [Size], [Type])
) p
Run Code Online (Sandbox Code Playgroud)

如果要转换的列数未知,则可以使用动态PIVOT.这将获取要在运行时转换的列的列表:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(property) 
                    from yourtable
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT ' + @cols + ' from 
             (
                select FileId, Property, Value
                from yourtable
            ) x
            pivot 
            (
                max(value)
                for Property in (' + @cols + ')
            ) p '

execute(@query)
Run Code Online (Sandbox Code Playgroud)

  • 但是你仍然需要执行多个连接,这个方法没有连接,这种类型的查询不需要它们.不仅如此,OP可能只显示了部分字段列表.您的查询将起作用,但这并非错误,IMO不值得投票. (3认同)
  • @Antonio"带有"CASE"的解决方案总是比"JOIN"解决方案慢,因为JOIN可以使用RDBMS索引." 这完全是胡说八道.像这样使用'CASE'*可能*在CPU上更重,*可能*在I/O上更轻,但"更快"取决于您的环境.如果我不得不猜测我会说使用CASE*通常会更快. (3认同)
  • @Antonio我不确定你为什么说它太复杂了.这比执行多个连接要容易得多.如果他们有10个字段要转换,那么你将不得不执行10个连接. (2认同)
  • +1 - 显然不是过度工程,也有三个答案合二为一. (2认同)

ype*_*eᵀᴹ 9

具有连接的版本,无论缺少哪些行都可以使用:

SELECT  
    pd.FileID 
  , p1.Value  AS Name
  , p2.Value  AS Size
  , p3.Value  AS Type
FROM
        ( SELECT DISTINCT FileID
          FROM propertyvalues 
        ) AS pd
    LEFT JOIN
        propertyvalues AS p1
            ON  p1.FileID = pd.FileID 
            AND p1.Property = 'Name'
    LEFT JOIN
        propertyvalues AS p2
            ON  p2.FileID = pd.FileID 
            AND p2.Property = 'Size'
    LEFT JOIN
        propertyvalues AS p3
            ON  p3.FileID = pd.FileID
            AND p3.Property = 'Type' ;
Run Code Online (Sandbox Code Playgroud)

如果您有一个表FileID是主键,您可以DISTINCT用该表替换子查询.


关于效率,它取决于很多因素.例子:

  • 所有FileID都包含名称,大小和类型的行,而没有其他属性(并且您的表上有聚簇索引(FileID, Property))?然后MAX(CASE...)版本会表现得很好,因为无论如何都必须扫描整个表格.

  • 是否有(多个)超过3个属性,并且许多FileID没有名称,大小和类型,那么该JOIN版本适用于索引,(Property, FileID) INCLUDE (Value)因为只有此索引数据将用于连接.

  • 不确定PIVOT版本的效率如何.

我建议的是在您选择使用哪一个之前,在您的环境(版本,磁盘,内存,设置......)中测试各种版本的数据和表大小.