没有聚合函数的 TSQL Pivot

Posted

技术标签:

【中文标题】没有聚合函数的 TSQL Pivot【英文标题】:TSQL Pivot without aggregate function 【发布时间】:2010-11-23 12:14:07 【问题描述】:

我有一张这样的桌子...

CustomerID   DBColumnName   Data
--------------------------------------
1            FirstName      Joe
1            MiddleName     S
1            LastName       Smith
1            Date           12/12/2009
2            FirstName      Sam
2            MiddleName     S
2            LastName       Freddrick
2            Date           1/12/2009
3            FirstName      Jaime
3            MiddleName     S
3            LastName       Carol
3            Date           12/1/2009

我想要这个……

这可以使用 PIVOT 吗?

CustomerID  FirstName   MiddleName          LastName        Date
----------------------------------------------------------------------
1           Joe             S               Smith           12/12/2009
2           Sam             S               Freddrick       1/12/2009
3           Jaime           S               Carol           12/1/2009

【问题讨论】:

【参考方案1】:

是的,但为什么!!??

   Select CustomerID,
     Min(Case DBColumnName When 'FirstName' Then Data End) FirstName,
     Min(Case DBColumnName When 'MiddleName' Then Data End) MiddleName,
     Min(Case DBColumnName When 'LastName' Then Data End) LastName,
     Min(Case DBColumnName When 'Date' Then Data End) Date
   From table
   Group By CustomerId

【讨论】:

^^ 这对我有用。 PIVOT 对非数字值无效。 这是一个很好的选择。我在查询中使用了Pivot,然后我切换到这个并查看了同时运行两者的执行计划。这种方法的成本为 8%,而 Pivot 方法的成本为 92%! @CharlesBretana,你太棒了!你救了我的灵魂! ) 那是最好的解决方案。谢谢! 真的很喜欢这个解决方案,它还确保列包含正确的数据而不是 Pivot 数据,谢谢! 这项工作很棒!但是我该如何预防 - Warning: Null value is eliminated by an aggregate or other SET operation【参考方案2】:

您可以使用 MAX 聚合,它仍然可以工作。一个值的最大值 = 该值..

在这种情况下,您还可以在 customerid 上进行 5 次自联接,按表引用按 dbColumnName 进行过滤。效果可能会更好。

【讨论】:

如果您有 2 位同名的客户,这实际上是行不通的 那行得通。请记住,DBColumnName 是元数据 - 您实际上是按“CustomerID = 1 AND DBColumnName = 'FirstName'”过滤的。当然,如果给定 CustomerID 有多个 FirstName 行,这会中断,但如果您正确创建表,CustomerID 和 DBColumnName 都是主键的一部分... 以一些代码/模拟为例会很棒,并使这个答案非常完整。【参考方案3】:
WITH pivot_data AS
(
SELECT customerid, -- Grouping Column
dbcolumnname, -- Spreading Column
data -- Aggregate Column
FROM pivot2 
)
SELECT customerid, [firstname], [middlename], [lastname]
FROM pivot_data
PIVOT (max(data) FOR dbcolumnname IN ([firstname],[middlename],[lastname])) AS p;

【讨论】:

这应该是公认的答案,因为它显示了正确使用 TSQL Pivot 命令。 值得注意的是,在这个查询中,“pivot2”是原始数据所在的表的名称。此外,此处使用 CTE 是多余的 - CTE 下方的 SELECT 语句可能只是指定了原始表的名称。 @STLDev 实际上 STLDev 这不是 pivot 的工作方式。我们不知道表“pivot2”中的所有列。事实上,表中可能存在 OP 未指定的其他列。因此,除非您限制列 - 使用 CTE 或派生表查询 - 否则表中的所有列都将用于分组。换句话说,PIVOT 返回了一些东西,但不是我们所期望的。这是 70-761 认证考试深入探讨的概念。 值得注意的是,PIVOT 会自动按 PIVOT 本身未使用的列进行分组。所以在这个例子中,[data] 和 [dbcolumnname] 在 PIVOT 中,所以所有内容都将按 [CustomerId] 分组【参考方案4】:

好的,抱歉这个可怜的问题。 gbn 让我走上了正轨。 这就是我在答案中寻找的。​​p>

SELECT [FirstName], [MiddleName], [LastName], [Date] 
FROM #temp 
PIVOT
(   MIN([Data]) 
    FOR [DBColumnName] IN ([FirstName], [MiddleName], [LastName], [Date]) 
)AS p

然后我不得不使用 while 语句并将上述语句构建为 varchar 并使用 dynmaic sql。

使用类似的东西

SET @fullsql = @fullsql + 'SELECT ' + REPLACE(REPLACE(@fulltext,'(',''),')','')
SET @fullsql = @fullsql + 'FROM #temp '
SET @fullsql = @fullsql + 'PIVOT'
SET @fullsql = @fullsql + '('
SET @fullsql = @fullsql + ' MIN([Data])'
SET @fullsql = @fullsql + ' FOR [DBColumnName] IN '+@fulltext
SET @fullsql = @fullsql + ')'
SET @fullsql = @fullsql + 'AS p'

EXEC (@fullsql)

使用 while 循环构建 @fulltext 并从表中选择不同的列名。感谢您的回答。

【讨论】:

【参考方案5】:
SELECT
main.CustomerID,
f.Data AS FirstName,
m.Data AS MiddleName,
l.Data AS LastName,
d.Data AS Date
FROM table main
INNER JOIN table f on f.CustomerID = main.CustomerID
INNER JOIN table m on m.CustomerID = main.CustomerID
INNER JOIN table l on l.CustomerID = main.CustomerID
INNER JOIN table d on d.CustomerID = main.CustomerID
WHERE f.DBColumnName = 'FirstName' 
AND m.DBColumnName = 'MiddleName' 
AND l.DBColumnName = 'LastName' 
AND d.DBColumnName = 'Date' 

编辑:我在没有编辑器的情况下编写了这个并且没有运行 SQL。我希望,你明白了。

【讨论】:

【参考方案6】:

OP 实际上不需要在没有聚合的情况下进行旋转,但对于那些来到这里了解如何查看的人来说:

sql parameterised cte query

该问题的答案涉及需要不进行聚合的枢轴的情况,因此示例是解决方案的一部分。

【讨论】:

【参考方案7】:

试试这个:

SELECT CUSTOMER_ID, MAX(FIRSTNAME) AS FIRSTNAME, MAX(LASTNAME) AS LASTNAME ...

FROM
(

SELECT CUSTOMER_ID, 
       CASE WHEN DBCOLUMNNAME='FirstName' then DATA ELSE NULL END AS FIRSTNAME,
       CASE WHEN DBCOLUMNNAME='LastName' then DATA ELSE NULL END AS LASTNAME,
        ... and so on ...
GROUP BY CUSTOMER_ID

) TEMP

GROUP BY CUSTOMER_ID

【讨论】:

【参考方案8】:

这应该可行:

select * from (select [CustomerID]  ,[Demographic] ,[Data]
from [dbo].[pivot]
) as Ter

pivot (max(Data) for  Demographic in (FirstName, MiddleName, LastName, [Date]))as bro

【讨论】:

【参考方案9】:

这是为数据透视查询构建动态字段的好方法:

--将值汇总到 tmp 表中

declare @STR varchar(1000)
SELECT  @STr =  COALESCE(@STr +', ', '') 
+ QUOTENAME(DateRange) 
from (select distinct DateRange, ID from ##pivot)d order by ID

---查看生成的字段

print @STr

exec('  .... pivot code ...
pivot (avg(SalesAmt) for DateRange IN (' + @Str +')) AS P
order by Decile')

【讨论】:

以上是关于没有聚合函数的 TSQL Pivot的主要内容,如果未能解决你的问题,请参考以下文章

TSQL - 将 Sales_Rep 行分组到列并将 DESCRIP 列显示为行 无聚合

具有动态生成列、聚合函数和无聚合列的 SQL Pivot

使用pivot使用两个聚合函数

在没有任何聚合函数的情况下将列数据转换为行

同一列上具有多个聚合的 T-SQL Pivot

SQL Server中行列转换 Pivot UnPivot