TSQL - 将每条记录的行数转换为列数

2021-09-10 00:00:00 sql tsql sql-server

考虑下表:

+--------------------------------------------------------------------------------+|用户名 |设备GUID |详情GUID |价值 |iValue |gValue |日期存储 ||条目 1 |设备 1 |详情1 |SN112 |||01/01/2020 ||条目2 |设备 1 |详情4 ||第1241章|01/01/2020 ||条目3 |设备 1 |详情7 |||GUID12 |01/01/2020 ||条目4 |设备 2 |详情1 |SN111 |||01/01/2020 ||条目5 |设备 2 |详情2 |RND123 |||01/01/2020 ||ENRTY6 |设备 2 |详情4 ||2351 ||03/01/2020 ||条目7 |设备 3 |详情1 |SN100 |||02/01/2020 ||[...] |[...] |[...] |||||||||||||+------------------------------------------------------------------------------+

我有一个表,将 DeviceGUID 与 DetailsGUID 链接起来,其想法是具有无限的详细信息选项(只需创建一个新的详细信息即可获取).但是,这意味着每个 deviceGUID 的记录数量是有限且未知的.

我想向我的用户展示的表格是这样的:

<预><代码>+--------+---------------------------------------------------------------------+|用户名 |DeviceGUID |Detail1 |Detail2 |Detail4 |Detail7 |DateStored ||条目 1 |设备 1 |SN112 |[NULL] |1241 |GUID12 |[最大日期存储] ||条目2 |设备 2 |SN111 |RND123 |2351 |[空] |[最大日期存储] ||条目3 |DEVICE3 |SN100 ||||||[...] |[...] ||||||+------------------------------------------------------------------------------+

我一直在搜索并找到了 PIVOT 选项,但它似乎只对一个字段起作用,另一个选项是 CROSS APPLY,但这(似乎)需要将所有内容转换为相同的数据类型;因为我希望在 ColumnNames 中可见,我将有 3 种类型的数据:String (VARCHAR) 值、Integer 值、GUID (uniqueidentifier) 值,并且它们不能互换(意味着具有 GUID Detail1 的 Detail 将始终具有 VARCHAR,具有 DetailGUID Detail4 的 Detail 将始终是 Integer

到目前为止我能发现什么:

DECLARE @columns NVARCHAR(MAX), @sql NVARCHAR(MAX);SET @columns = N'';SELECT @columns+=N', p.'+QUOTENAME([名称])从(选择 GUID 作为 [名称]FROM [dbo].Details AS p) 作为 x;SET @sql = N'SELECT [DeviceObjectGUID], '+STUFF(@columns, 1, 2, '')+' FROM (选择 [DeviceObjectGUID]、[DateStored]、[DetailGUID] 作为 [Name]FROM [dbo].[DeviceDetails]) AS j PIVOT (MAX(DateStored) FOR [Name] in('+STUFF(REPLACE(@columns, ', p.[', ',['), 1, 1, '')+')) AS p;';EXEC sp_executesql @sql

制作用于转置数据的动态 PIVOT.. 但如前所述,这仅限于一列

选择 DeviceObjectGUID,值从设备详细信息交叉申请(select 'sValue', cast(sValue as varchar(MAX)) union all选择 'gValue', cast(gValue as varchar(MAX))union all选择 'iValue', cast(iValue as varchar(MAX))) c(列,值)

这将使我将所有字段转换为 VARCHAR..

我尝试过的另一个选项(为了理解 PIVOT)是这样的:

SELECT *从(选择 *从 [dbo].[设备详细信息]) AS 源表枢(max(sValue)FOR [DetailGUID] IN([450533BB-43B2-499B-B2F7-094BFAE949B0],[7483E518-EB61-4B72-93F7-0F97BBFAFA01],[29B1BDE8-3AD4-4576-8B76-3CAE83E10B11],[5FC8CC76-12EB-4924-9320-5D09BBE97C10],[789AA79B-B1DF-4BA2-860A-7129B39D341F],[C90F4EFE-D848-4BAB-96BF-8DC6BF4F6E62],[EC6A4ED3-1475-4B0A-8E08-B2F4E095622F],[D442B7CA-5825-49D9-9977-D88770304B57],[99B70FEE-999B-4D44-83E9-EB8119B15930],[3F83ED76-8CC3-4B3D-936A-F528DEB6C045])) 作为数据透视表;

('IN 子句中的 GUIDS 是 DetailGUID)这几乎让我得到了我想要的,除了它不是动态的,而且它仍然仅限于一个数据列.(max(sValue)) 在这种情况下.

====================

回应

解决方案

应该就是这么简单:

SELECT *从(选择设备GUID,详细GUID,CONCAT(sValue, iValue, gValue) as [value],日期存储从 my_table) DS枢(MAX([value]) FOR DetailGUID IN (......)) PVT

Consider the following table:

+------------------------------------------------------------------------------+
| GUID   | DeviceGUID | DetailGUID | sValue  | iValue  | gValue  | DateStored  |
| ENTRY1 | DEVICE1    | Detail1    | SN112   |         |         | 01/01/2020  |
| ENTRY2 | DEVICE1    | Detail4    |         | 1241    |         | 01/01/2020  |
| ENTRY3 | DEVICE1    | Detail7    |         |         | GUID12  | 01/01/2020  |
| ENTRY4 | DEVICE2    | Detail1    | SN111   |         |         | 01/01/2020  |
| ENTRY5 | DEVICE2    | Detail2    | RND123  |         |         | 01/01/2020  |
| ENRTY6 | DEVICE2    | Detail4    |         | 2351    |         | 03/01/2020  |
| ENTRY7 | DEVICE3    | Detail1    | SN100   |         |         | 02/01/2020  |
| [...]  | [...]      | [...]      |         |         |         |             |
|        |            |            |         |         |         |             |
+------------------------------------------------------------------------------+

I have a table which links a DeviceGUID with a DetailsGUID, with the idea of having unlimited options for Details (just create a new detail and it will be fetchable). however, this means I have a finite and unknown amount of records per deviceGUID.

What I want to show to my users is a table like this:


+--------+---------------------------------------------------------------------+
| GUID   | DeviceGUID |Detail1 |Detail2 |Detail4 |Detail7 |DateStored          |
| ENTRY1 | DEVICE1    |SN112   | [NULL] |1241    |GUID12  | [MAX DateStored]   |
| ENTRY2 | DEVICE2    |SN111   | RND123 |2351    | [NULL] | [MAX DateStored]   |
| ENTRY3 | DEVICE3    |SN100   |        |        |        |                    |
| [...]  | [...]      |        |        |        |        |                    |
+------------------------------------------------------------------------------+

I have been searching a bit and found the PIVOT option but that only seems to function for one field, another option was CROSS APPLY, but that (seems to) need to convert everything to the same datatype; as I hope is visible n the ColumnNames, I will have 3 types of data: String (VARCHAR) value, Integer value, GUID (uniqueidentifier) value, and they will not be interchangeable (meaning Detail with GUID Detail1 will always have a VARCHAR, Detail with DetailGUID Detail4 will always be an Integer

what I was able to find out until now:

DECLARE @columns NVARCHAR(MAX), @sql NVARCHAR(MAX);
SET @columns = N'';
SELECT @columns+=N', p.'+QUOTENAME([Name])
FROM
(
    SELECT GUID AS [Name]
    FROM [dbo].Details AS p
) AS x;

SET @sql = N'
SELECT [DeviceObjectGUID], '+STUFF(@columns, 1, 2, '')+' FROM (
SELECT [DeviceObjectGUID], [DateStored], [DetailGUID] as [Name] 
    FROM [dbo].[DeviceDetails]) AS j PIVOT (MAX(DateStored) FOR [Name] in 
       ('+STUFF(REPLACE(@columns, ', p.[', ',['), 1, 1, '')+')) AS p;';
EXEC sp_executesql @sql

to make a dynamic PIVOT for transposing the data.. but as mentioned this is limited to one column

and

select DeviceObjectGUID, value
from DeviceDetails
cross apply
(
  select 'sValue', cast(sValue as varchar(MAX)) union all
  select 'gValue', cast(gValue as varchar(MAX))union all
  select 'iValue', cast(iValue as varchar(MAX))
) c(col, value)

This will have me converting all fields to VARCHAR..

One other option I tried (to understand PIVOT) was this:

SELECT *
FROM
(SELECT *
    FROM [dbo].[DeviceDetails]
) AS SourceTable 
    PIVOT(
    max(sValue)FOR [DetailGUID] IN(
        [450533BB-43B2-499B-B2F7-094BFAE949B0],
        [7483E518-EB61-4B72-93F7-0F97BBFAFA01],
        [29B1BDE8-3AD4-4576-8B76-3CAE83E10B11],
        [5FC8CC76-12EB-4924-9320-5D09BBE97C10],
        [789AA79B-B1DF-4BA2-860A-7129B39D341F],
        [C90F4EFE-D848-4BAB-96BF-8DC6BF4F6E62],
        [EC6A4ED3-1475-4B0A-8E08-B2F4E095622F],
        [D442B7CA-5825-49D9-9977-D88770304B57],
        [99B70FEE-999B-4D44-83E9-EB8119B15930],
        [3F83ED76-8CC3-4B3D-936A-F528DEB6C045]
        )
    ) AS PivotTable;

(The GUIDS in the 'IN clause are the DetailGUIDs) this almost gets me what I want, except that it is not dynamic and the fact that it is still limited to one data column. (max(sValue)) in this case.

===================

in response to

解决方案

It should be as simple as that:

SELECT *
FROM
(
    SELECT DeviceGUID
          ,DetailGUID 
          ,CONCAT(sValue, iValue, gValue) as [value]
          ,DateStored 
    FROM my_table
) DS
PIVOT
(
    MAX([value]) FOR DetailGUID IN (......)
) PVT

相关文章