简体   繁体   English

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

[英]Is it possible to convert rows to a variable number of columns in T-SQL?

I have a strange requirement where I need to convert a variable amount of rows into columns. 我有一个奇怪的要求,我需要将数量可变的行转换为列。 I need help figuring out if this could be done in SQL, or if I should just write a program in a different language. 我需要帮助弄清楚这是否可以用SQL来完成,或者我是否应该只使用其他语言编写程序。

Let's assume I have a table Clients, which only holds minimal client data. 假设我有一个表Clients,该表仅包含最少的客户端数据。 Then, a table called Attributes, which names the different possible attributes (say, Phone number, address, etc). 然后,有一个称为属性的表,该表命名了不同的可能属性(例如,电话号码,地址等)。 Finally, I have the third table, ClientAttributes which holds the two FKs and the value. 最后,我有第三个表ClientAttributes,其中包含两个FK和值。

So for each client I have any number of attributes. 因此,对于每个客户端,我都有许多属性。 A client can have zero, 1 or infinite phone numbers, zero, 1, or infinite addresses, and so on. 客户端可以有零个,1个或无限的电话号码,零个,1个或无限的地址,依此类推。

What I need is a table view of all that data. 我需要的是所有这些数据的表格视图。 Client Name, Phone, Phone 2, Phone 3, ..., Address, Address 2, Address 3.... and so on. 客户端名称,电话,电话2,电话3,...,地址,地址2,地址3 ....等。 If a client has no such value, the value will be null. 如果客户端没有这样的值,则该值为null。 Obviously this means that the number of columns may be different every time the query is executed. 显然,这意味着每次执行查询时,列数可能会有所不同。

This needs to be compatible as far back as SQL Server 2008. 这需要与SQL Server 2008兼容。

Could this be done purely in T-SQL, or should do this client-side by just dumping the data and let C# handle it? 可以仅在T-SQL中完成此操作,还是应该仅通过转储数据并让C#处理它来在客户端进行此操作? I could easily do it in C# but I'm not sure this could be done in SQL. 我可以轻松地在C#中做到这一点,但是我不确定这可以在SQL中完成。 SQL would be preferred because the dataset may be too large to fit in RAM. 首选SQL,因为数据集可能太大而无法放入RAM。

This can be done in SQL through dynamic sql, if you need to. 如果需要,可以通过动态sql在SQL中完成。 The basic theory for doing it for one item (I'll use phone) is as follows, you'd repeat this for each other grouping of columns you want. 对一项(我将使用电话)执行此操作的基本理论如下,您将针对所需的其他列分组重复此操作。 Note that no one will ever say that it is pretty. 请注意,没有人会说它很漂亮。

  • Create a base query (CTE, TEMP table, whatever) that gets Client ID, Phone for every valid phone number. 创建一个基本查询(CTE,TEMP表等),以获取每个有效电话号码的客户ID,电话。 Add a rownumber "ROW_NUMBER() OVER (PARTITION BY ClientID ORDER BY (whatever))" - I'll call it basedata 添加一个行号“ ROW_NUMBER()OVER(PARTITION BY ClientID ORDER BY(无论如何))”-我称其为basedata
  • Get the max row_number from basedata - this is the number of phone columns you need 从basedata获取最大row_number-这是您需要的电话列数

Make parts of a dynamic SQL query string by looping from i = 1 to MaxRowNo. 通过从i = 1到MaxRowNo循环来构成动态SQL查询字符串的一部分。 In each loop, you build up a selection string, and a join string. 在每个循环中,您将建立一个选择字符串和一个连接字符串。 The selection string should add something like the following in each loop 选择字符串应在每个循环中添加如下内容

Set @SelectStr = @SelectStr + 'P' + cast(i as varchar(10)) + '.Phone,';

The join string should add something like this in each loop 连接字符串应在每个循环中添加如下内容

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));

You would repeat the whole above process for addresses, and any other repeating groups of columns - make sure you don't double up on alias names. 您将对地址和所有其他重复的列组重复上述整个过程-确保不要重复使用别名。 Then you would make up your final dynamic sql query by adding any fixed, unchanging parts of the query (client data), something like this 然后,您将通过添加查询中所有固定的,不变的部分(客户端数据)来构成最终的动态sql查询,如下所示

Set @FinalQuery = 'SELECT C.ClientID, C.ClientName, ' + @SelectStr + ' From Client C ' + @JoinStr

Your final built up query (assuming there is max three phones and two addresses as an example) would look something like this - then EXEC this string 您最终建立的查询(假设最多三个电话和两个地址为例)看起来像这样-然后执行此字符串

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM