在T-SQL中是否可以将行转换为可变数量的列?

在T-SQL中是否可以将行转换为可变数量的列?,sql,sql-server,tsql,Sql,Sql Server,Tsql,我有一个奇怪的需求,需要将数量可变的行转换为列。我需要帮助弄清楚这是否可以用SQL完成,或者我是否应该用另一种语言编写一个程序 假设我有一个表Clients,它只保存最小的客户机数据。 然后,一个名为Attributes的表,它命名了不同的可能属性(例如电话号码、地址等)。 最后,我有第三个表ClientAttributes,它包含两个FK和值 因此,对于每个客户机,我有任意数量的属性。客户机可以有零个、1个或无限个电话号码、零个、1个或无限个地址,依此类推 我需要的是所有这些数据的表视图。客户

我有一个奇怪的需求,需要将数量可变的行转换为列。我需要帮助弄清楚这是否可以用SQL完成,或者我是否应该用另一种语言编写一个程序

假设我有一个表Clients,它只保存最小的客户机数据。 然后,一个名为Attributes的表,它命名了不同的可能属性(例如电话号码、地址等)。 最后,我有第三个表ClientAttributes,它包含两个FK和值

因此,对于每个客户机,我有任意数量的属性。客户机可以有零个、1个或无限个电话号码、零个、1个或无限个地址,依此类推

我需要的是所有这些数据的表视图。客户名称,电话,电话2,电话3,…,地址,地址2,地址3。。。。等等如果客户端没有此类值,则该值将为null。 显然,这意味着每次执行查询时,列的数量可能不同

这需要与SQL Server 2008兼容


这完全可以在T-SQL中完成,还是应该在客户端通过转储数据并让C#处理来完成?我可以用C#轻松完成,但我不确定这是否可以用SQL完成。最好使用SQL,因为数据集可能太大,无法装入RAM。

如果需要,可以通过动态SQL在SQL中完成此操作。为一个项目(我将使用phone)执行此操作的基本理论如下,您可以为每个您想要的列分组重复此操作。请注意,没有人会说它很漂亮

  • 创建一个基本查询(CTE、TEMP表等),为每个有效的电话号码获取客户端ID和电话。添加一个rownumber“ROW_NUMBER()OVER(partitionby ClientID ORDER BY(which))”——我将其称为basedata
  • 从basedata获取最大行数-这是您需要的电话列数
通过从i=1循环到MaxRowNo,生成动态SQL查询字符串的一部分。在每个循环中,构建一个选择字符串和一个连接字符串。选择字符串应该在每个循环中添加如下内容

Set @SelectStr = @SelectStr + 'P' + cast(i as varchar(10)) + '.Phone,';
Set @JoinStr = @JoinStr + ' left outer join baseData P' + cast(i as varchar(10)) + ' on P' + cast(i as varchar(10)) + '.ClientID = C.ClientID and P' + cast(i as varchar(10)) + '.RowNo = ' + cast(i as varchar(10));
连接字符串应该在每个循环中添加类似的内容

Set @SelectStr = @SelectStr + 'P' + cast(i as varchar(10)) + '.Phone,';
Set @JoinStr = @JoinStr + ' left outer join baseData P' + cast(i as varchar(10)) + ' on P' + cast(i as varchar(10)) + '.ClientID = C.ClientID and P' + cast(i as varchar(10)) + '.RowNo = ' + cast(i as varchar(10));
您将对地址和任何其他重复的列组重复上述整个过程-确保别名不重复。然后,通过添加查询的任何固定的、不变的部分(客户机数据),组成最终的动态sql查询,如下所示

Set @FinalQuery = 'SELECT C.ClientID, C.ClientName, ' + @SelectStr + ' From Client C ' + @JoinStr
您最终构建的查询(假设最多有三个电话和两个地址作为示例)将如下所示-然后执行此字符串

SELECT C.ClientID, C.ClientName, --any other client stuff you need here
   P1.Phone, P2.Phone, P3.Phone, A1.Address, A2.Address
From Client C
   left outer join baseData P1 on P1.ClientID = C.ClientID and P1.RowNo = 1
   left outer join baseData P2 on P2.ClientID = C.ClientID and P2.RowNo = 2
   left outer join baseData P3 on P3.ClientID = C.ClientID and P3.RowNo = 3
   left outer join baseAddr A1 on A1.ClientID = C.ClientID and A1.RowNo = 1
   left outer join baseAddr A2 on A2.ClientID = C.ClientID and A2.RowNo = 2

用另一种语言可能更容易实现,但可以通过编程方式构建一个字符串(其中包含您希望执行的动态SQL)在SQL中实现。动态SQL是一个相当大的安全问题,从性能角度看,它真的很糟糕。但是,这是可以做到的。如果您想要一个示例,请在问题中提供源数据和所需结果。搜索SQL pivot示例。。。这里有很多例子,但由于列数可变,我可能会用c#来实现。JeffBreadner我讨厌动态sql,但公司很喜欢它。明天我将尝试扩展我的问题。@Tanner如果您有x个所需的列数,并且该字段中有最大值的一个用户,那么您能否有效地使用pivot?是的,理解用例很重要。有时,向最终用户展示excel数据透视表的魔力很容易解决这个问题