Sql server 无聚合的旋转

Sql server 无聚合的旋转,sql-server,tsql,pivot,aggregate-functions,Sql Server,Tsql,Pivot,Aggregate Functions,我正试着去,结果撞到了一堵墙 下面是我尝试使用的T-SQL示例: declare @optionalFields table (ParentId int, Name nvarchar(50), Value nvarchar(50)); insert into @optionalFields values (1, 'Field1', 'Foo'); insert into @optionalFields values (1, 'Field2', 'Bar'); insert into @optio

我正试着去,结果撞到了一堵墙

下面是我尝试使用的T-SQL示例:

declare @optionalFields table (ParentId int, Name nvarchar(50), Value nvarchar(50));
insert into @optionalFields values (1, 'Field1', 'Foo');
insert into @optionalFields values (1, 'Field2', 'Bar');
insert into @optionalFields values (1, 'Field3', '42');
insert into @optionalFields values (2, 'Field1', 'Bar');
insert into @optionalFields values (2, 'Field2', 'Foo');
insert into @optionalFields values (2, 'Field3', '24');

declare @data table (Id int, Name nvarchar(50));
insert into @data values (1, 'Test record 1');
insert into @data values (2, 'Test record 2');

declare @joined table (Id int, Name nvarchar(50), OptionalFieldName nvarchar(50), OptionalFieldValue nvarchar(50));
insert into @joined
select 
     data.Id
    ,data.Name
    ,opt.Name
    ,opt.Value
from @data data
    inner join @optionalFields opt on data.Id = opt.ParentId

declare @cols as nvarchar(max) = 
    stuff((select distinct ',' + quotename(OptionalFieldName) from @joined for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '');

select * into #tmp from @joined
-- just to see that it's returning the expected values (it does)
select
     Id
    ,Name
    ,OptionalFieldName
    ,OptionalFieldValue
    ,row_number() over (partition by Id order by Id) RN 
from #tmp -- this is the FROM clause in the below dynamic-sql query

declare @query as nvarchar(max) = '
    select Id, Name, ' + @cols + '
    from (select Id, Name, OptionalFieldName, OptionalFieldValue, row_number() over (partition by Id order by Id) RN from #tmp) src 
    pivot (max(OptionalFieldName) for RN in (' + @cols + ')) pvt';

execute(@query);
drop table #tmp;
SSMS给了我2个错误:

Msg 8114,16级,状态1,第4行 将数据类型nvarchar转换为bigint时出错。 Msg 473,16级,状态1,第4行 PIVOT运算符中提供的值字段1不正确。 debug select语句将返回以下内容:

上面的文章链接看起来很有希望,但我似乎无法让它发挥作用。我做错了什么?还是这篇文章完全错了,我想做的是不可能的

我见过许多类似的SO问题,但它们要么涉及所有可用于聚合的数值字段,要么涉及可实现为简单联接的已知列-我不知道我将选择什么OptionalFieldName值,OptionalFieldValue值是无法聚合的字符串,至少可以这样做。

EDIT:这里的答案只是为了说明实现此旋转的另一种方法。来自@bluefeet的答案是最好的解决方案! 希望我能正确理解您的需求:

declare @optionalFields table (ParentId int, Name nvarchar(50), Value nvarchar(50));
insert into @optionalFields values (1, 'Field1', 'Foo');
insert into @optionalFields values (1, 'Field2', 'Bar');
insert into @optionalFields values (1, 'Field3', '42');
insert into @optionalFields values (2, 'Field1', 'Bar');
insert into @optionalFields values (2, 'Field2', 'Foo');
insert into @optionalFields values (2, 'Field3', '24');

declare @data table (Id int, Name nvarchar(50));
insert into @data values (1, 'Test record 1');
insert into @data values (2, 'Test record 2');


select 
     data.Id
    ,data.Name
    ,opt.Name as Name1
    ,opt.Value into #tmp
from @data data
    inner join @optionalFields opt on data.Id = opt.ParentId

declare @cols as nvarchar(max) = 
    stuff((select distinct ',' + quotename(Name1) from #tmp for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '');

DECLARE @cols1 as nvarchar(max) = 
    stuff((select distinct +',MAX(CASE WHEN (pvt1.'+quotename(Name1) +' = ros.RN AND pvt1.id = ros.id) THEN ros.Value ELSE NULL END) as '+quotename(Name1)  from #tmp for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '');

declare @query as nvarchar(max) = '
 ;WITH cte AS(
 SELECT * FROM #tmp
),

        ros AS (
        SELECT ROW_NUMBER() OVER (partition by Id order by Id) AS [RN],id,Value
        FROM cte),
        pvt1 AS (
    select *
    from (select Id, Name, Name1, row_number() over (partition by Id order by Id) RN 
            from cte) src 
    pivot (max(RN) for Name1 in ('+@cols+')) pvt)

    SELECT pvt1.ID,
            pvt1.Name,
            '+@cols1+'
    FROM pvt1 
    CROSS JOIN ros
    GROUP BY pvt1.ID,
            pvt1.Name'

execute(@query);
drop table #tmp
结果:

ID  Name            Field1  Field2  Field3
1   Test record 1   Foo     Bar     42
2   Test record 2   Bar     Foo     24
如果在@optionalFields values 2中添加更多字段,如insert,'Field4','15';,您将获得:

ID  Name            Field1  Field2  Field3  Field4
1   Test record 1   Foo     Bar     42      NULL
2   Test record 2   Bar     Foo     24      15

我有点搞不懂你为什么要用行数来欺骗我。即使您有字符串值,您仍然可以聚合它-您只需要使用max或min来获得结果

我始终建议您首先尝试使用硬编码值编写查询,尤其是在尝试使用动态SQL之前使用PIVOT时。我不确定您为什么不能这样写查询:

select Id, Name, Field1, Field2, Field3
from 
(
  select
     Id
    ,Name
    ,OptionalFieldName
    ,OptionalFieldValue
  from #tmp
) d
pivot
(
  max(OptionalFieldValue)
  for OptionalFieldName in (Field1, Field2, Field3)
) piv;

如果您真的需要动态SQL,您只需编写它:

declare @optionalFields table (ParentId int, Name nvarchar(50), Value nvarchar(50));
insert into @optionalFields values (1, 'Field1', 'Foo');
insert into @optionalFields values (1, 'Field2', 'Bar');
insert into @optionalFields values (1, 'Field3', '42');
insert into @optionalFields values (2, 'Field1', 'Bar');
insert into @optionalFields values (2, 'Field2', 'Foo');
insert into @optionalFields values (2, 'Field3', '24');

declare @data table (Id int, Name nvarchar(50));
insert into @data values (1, 'Test record 1');
insert into @data values (2, 'Test record 2');

declare @joined table (Id int, Name nvarchar(50), OptionalFieldName nvarchar(50), OptionalFieldValue nvarchar(50));
insert into @joined
select 
     data.Id
    ,data.Name
    ,opt.Name
    ,opt.Value
from @data data
    inner join @optionalFields opt on data.Id = opt.ParentId

declare @cols as nvarchar(max);
set @cols = stuff((select distinct ',' + quotename(OptionalFieldName) 
                   from @joined
                   for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '');

select * into #tmp from @joined

DECLARE @query  AS NVARCHAR(MAX)

set @query = 'SELECT Id, Name,' + @cols + ' 
            from 
             (
                select Id
                  ,Name
                  ,OptionalFieldName
                  ,OptionalFieldValue
                from #tmp
            ) x
            pivot 
            (
                max(OptionalFieldValue)
                for OptionalFieldName in (' + @cols + ')
            ) p '

execute(@query);

。两个版本似乎都给出了您所请求的结果

首先,当RN是行数时,你在[Field1]、[Field2]、[Field3]等中旋转RN。我猜您的意思是为每个optionalfieldname?透视optionalfieldvalue?@ZLK确实如此,但是optionalfieldvalue不能聚合,因为它是一个varchar。。。在RN上旋转是用来欺骗旋转的,如我链接到的文章所示。嗯。。。你想要的输出是什么?我不确定这是否是您想要的,但请尝试添加一个与@cols相同的@cols2变量,但select语句是这样的:',MAX'+quoteNameOptions字段名+''+quoteNameOptions字段名,然后将@query更改为:declare@query as nvarcharmax='选择Id,Name,“++@cols2+”来自tmp pivot maxOptionalFieldValue,用于“++@cols+”pvt GROUP BY ID,Name”中的OptionalFieldName@幸好你根本不需要耍花招。即使需要聚合的值是字符串,也可以在其上使用min/max。这是一个演示-可以很容易地转换为动态sql。@bluefeet当我仔细阅读您的评论时,我理解我的答案的失败和您的解决方案的简单性:为什么我自己没有尝试一下?我羞愧地删除了我的答案:哇,你太棒了!非常感谢!