【问题标题】:Creating SQL pivot where duplicate column names exist创建存在重复列名的 SQL 数据透视表
【发布时间】:2021-12-28 16:21:54
【问题描述】:

我有几个表(在 SQL Server 2014 SP2 数据库中),我试图在这些表上进行透视,并且我试图从其中一个表中重命名一些列。

表格如下所示:

数据源快照

|   snapshotId |     snapshotTime |
-----------------------------------
|            1 | 2021-12-01 07:00 |
|            2 | 2021-12-02 07:00 |
...

数据源

| snapshotId | dsRecordId | dsId | dsName |       dsTableDate |
---------------------------------------------------------------
|          1 |          1 |    1 |    cpu |  2021-12-01 07:00 |
|          1 |          2 |    2 |    mem |  2021-12-01 07:00 |
|          1 |          3 |    3 |  cache |  2021-12-01 07:00 |
|          2 |          4 |    1 |    cpu |  2021-12-01 07:00 |
|          2 |          5 |    2 |    mem |  2021-12-01 07:00 |
|          2 |          6 |    3 |  cache |  2021-12-01 07:00 |
...

数据源属性

这是一个截断的数据源属性列表。

| dsRecordId | dsPropPropertyRecordId | dsPropPropertyName | dsPropPropertyValue |
----------------------------------------------------------------------------------
|          1 |                      1 |                 id |                   1 |  
|          1 |                      2 |        description |           cpu stats |
|          1 |                      3 |               name |                 cpu |
|          1 |                      4 |    collectInterval |                 300 |
|          1 |                      5 |              group |                     |
|          1 |                      6 |         dataPoints |     System.Object[] |
|          2 |                      7 |                 id |                   2 |  
|          2 |                      8 |        description |        memory stats |
|          2 |                      9 |               name |                 mem |
|          2 |                     10 |    collectInterval |                 300 |
|          2 |                     11 |              group |                     |
|          2 |                     12 |         dataPoints |     System.Object[] |
|          3 |                     13 |                 id |                   3 |  
|          3 |                     14 |        description |                     |
|          3 |                     15 |               name |               cache |
|          3 |                     16 |    collectInterval |                 600 |
|          3 |                     17 |              group |                     |
|          3 |                     18 |         dataPoints |     System.Object[] |
...

数据点属性

这是一个截断的数据点属性列表。

| dsRecordId | dpRecordId | dpPropertyName |    dpPropertyValue |
-----------------------------------------------------------------
|          1 |          1 |             id |                  1 |  
|          1 |          2 |   datasourceId |                  1 |
|          1 |          3 |           name |           cpuState |
|          1 |          4 |    description |                    |
|          1 |          5 |      alertExpr |             != 0 0 |
|          1 |          6 |           type |                  2 |
|          1 |          7 |             id |                  2 |  
|          1 |          8 |   datasourceId |                  1 |
|          1 |          9 |           name |        freePercent |
|          1 |         10 |    description |                    |
|          1 |         11 |      alertExpr |           >= 90 90 |
|          1 |         12 |           type |                  2 |
|          2 |         13 |             id |                  3 |
|          2 |         14 |   datasourceId |                  2 |
|          2 |         15 |           name |        freePercent |
|          2 |         16 |    description | Memory utilization |
|          2 |         17 |      alertExpr |           >= 90 90 |
|          2 |         18 |           type |                  2 |
|          3 |         19 |             id |                  4 |
|          3 |         20 |   datasourceId |                  3 |
|          3 |         21 |           name |              state |
|          3 |         22 |    description |                    |
|          3 |         23 |      alertExpr |            = 1 1 1 |
|          3 |         24 |           type |                  4 |
...

我正在尝试为每个数据源(但仅是该数据源条目的最新实例)和每个数据点(包括所有数据源属性)获取一行,其中数据点属性被旋转以使其成为列。像这样的:

| dsRecordId | id |    description |    name | collectInterval | group |      dataPoints | dp_id | dp_datasourceId |     dp_name |     dp_description | dp_alertExpr | dp_type |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|          1 |  1 |      cpu stats |     cpu |             300 |       | System.Object[] |     1 |               1 |    cpuState |                    |       != 0 0 |       2 |
|          1 |  1 |      cpu stats |     cpu |             300 |       | System.Object[] |     2 |               1 | freePercent |                    |     >= 90 90 |       2 |
|          2 |  2 |   memory stats |     mem |             300 |       | System.Object[] |     3 |               2 | freePercent | Memory utilization |     >= 90 90 |       2 |
|          3 |  3 |                |   cache |             600 |       | System.Object[] |     4 |               3 |       state |                    |      = 1 1 1 |       4 |

我有一些 T-SQL 可以进行数据透视,但是因为数据源和数据点都具有 id、名称和描述属性,所以我必须在数据透视中排除这些属性。这个查询:

DECLARE 
    @dsPropPropertyColumns NVARCHAR(MAX) = '',
    @dpPropertyColumns NVARCHAR(MAX) = '',
    @sql     NVARCHAR(MAX) = '';

-- select the property names
--- properties in the datasourceProperty table
SELECT @dsPropPropertyColumns = (
    SELECT DISTINCT '[' + [dsPropPropertyName] + ']' + ','
    FROM dbo.dataSourceSnapshot dss
    LEFT OUTER JOIN dbo.datasource ds ON ds.snapshotId = dss.snapshotId
    LEFT OUTER JOIN dbo.datasourceProperty dsp ON dsp.dsRecordId = ds.dsRecordId
    WHERE dss.snapshotTime = (
        SELECT MAX(snapshotTime) FROM dbo.dataSourceSnapshot
    )
    FOR XML PATH(''), TYPE
).value('.', 'nvarchar(max)')

--- properties in the datapointProperty table
SELECT @dpPropertyColumns = (
    SELECT DISTINCT '[' + [dpPropertyName] + ']' + ','
    FROM dbo.dataSourceSnapshot dss
    LEFT OUTER JOIN dbo.datasource ds ON ds.snapshotId = dss.snapshotId
    LEFT OUTER JOIN dbo.datapointProperty dp ON dp.dsRecordId = ds.dsRecordId
    WHERE dss.snapshotTime = (
        SELECT MAX(snapshotTime) FROM dbo.dataSourceSnapshot
    )
    AND [dpPropertyName] NOT IN ('name','id', 'description')
    FOR XML PATH(''), TYPE
).value('.', 'nvarchar(max)')

-- remove the trailing comma
SET @dsPropPropertyColumns = LEFT(@dsPropPropertyColumns, LEN(@dsPropPropertyColumns) - 1);
SET @dpPropertyColumns = LEFT(@dpPropertyColumns, LEN(@dpPropertyColumns) - 1);

-- construct dynamic SQL
SET @sql ='
SELECT * FROM (
    SELECT dsp.dsRecordId, dsp.dsPropPropertyName, dsp.dsPropPropertyValue, dp.dpPropertyName, dp.dpPropertyValue
    FROM dbo.snapshot dss
    LEFT OUTER JOIN dbo.datasource ds ON ds.snapshotId = dss.snapshotId
    LEFT OUTER JOIN dbo.datasourceProperty dsp ON dsp.dsRecordId = ds.dsRecordId
    LEFT OUTER JOIN dbo.datapointProperty dp ON dp.dsRecordId = ds.dsRecordId
    WHERE dss.snapshotTime = (
        SELECT MAX(snapshotTime) FROM dbo.dataSourceSnapshot
    )
) t1
PIVOT (
    MAX(dsPropPropertyValue) FOR dsPropPropertyName IN ('+ @dsPropPropertyColumns +')
) AS pivot_table
PIVOT (
    MAX(dpPropertyValue) FOR dpPropertyName IN ('+ @dpPropertyColumns +')
) AS pivot_table2
ORDER BY id
'

EXECUTE sp_executesql @sql;

产生这个输出:

| dsRecordId | id |    description |    name | collectInterval | group |      dataPoints | datasourceId | alertExpr | type |
----------------------------------------------------------------------------------------------------------------------------
|          1 |  1 |      cpu stats |     cpu |             300 |       | System.Object[] |            1 |    != 0 0 |    2 |
|          1 |  1 |      cpu stats |     cpu |             300 |       | System.Object[] |            1 |  >= 90 90 |    2 |
|          2 |  2 |   memory stats |     mem |             300 |       | System.Object[] |            2 |  >= 90 90 |    2 |
|          3 |  3 |                |   cache |             600 |       | System.Object[] |            3 |   = 1 1 1 |    4 |

我在第 38 行有 Select *...,因为我不知道所有属性名称。在我写这篇文章时,我突然想到,也许我可以以某种方式使用列名变量(@dsPropPropertyColumns 和@dpPropertyColumns)?那会是什么样子?

我查看了以下问题,但我认为它们没有回答我的问题:

问题是:如何动态生成用于透视的列列表,并将“dp_”添加到 datasourceProperty 表中的每个列名,这样我就可以包含 id;姓名;和描述列没有得到“重复的列名”错误?我认为这将产生所需的输出。

【问题讨论】:

  • 我不明白这有什么帮助,你能更具体一点吗?我可以使用您的建议生成列表属性,但我仍然会得到两个“id”列:SELECT '[' + [dsPropPropertyName] + ']' + ',' FROM dbo.snapshot dss LEFT OUTER JOIN dbo.datasource ds ON ds.snapshotId = dss.snapshotId LEFT OUTER JOIN dbo.datasourceProperty dsp ON dsp.dsRecordId = ds.dsRecordId WHERE dss.snapshotTime = ( SELECT MAX(dataSourceSnapshotTime) FROM logicmonitor.dataSourceSnapshot ) GROUP BY [dsPropPropertyName]
  • SELECT @dpPropertyColumns = ( SELECT DISTINCT quotename(concat('dp_',[dpPropertyName])) ...SET @sql ='SELECT * FROM (...dsp.dsPropPropertyValue, concat('dp_', dp.dpPropertyName) as dpPropertyName, dp.dpPropertyValue ...
  • @lptr,我进行了更改,现在我有了“dp_”列,这很棒。我没有的是每个数据点的一行。我认为枢轴会发生这种情况,因为有多个实例。
  • 如果每个 dsRecordId 有多个数据点实例,则为数据点分配一个“分组编号”SET @sql ='SELECT * FROM (...row_number() over(partition by dp.dsRecordId, dp.dpPropertyName order by dp.dpRecordId) as datapointrn,...
  • 这并没有达到我的预期。为了清楚起见(并合并引用名/concat),我创建了Output multiple rows after joining and pivoting multiple tables。我会将此标记为已回答。

标签: sql sql-server


【解决方案1】:

根据@lptr 的回复,我将 T-SQL 更新为:

DECLARE 
    @dsPropPropertyColumns NVARCHAR(MAX) = '',
    @dpPropertyColumns NVARCHAR(MAX) = '',
    @sql     NVARCHAR(MAX) = '';

-- select the property names
--- properties in the datasourceProperty table
SELECT @dsPropPropertyColumns = (
    SELECT DISTINCT '[' + [dsPropPropertyName] + ']' + ','
    FROM dbo.dataSourceSnapshot dss
    LEFT OUTER JOIN dbo.datasource ds ON ds.snapshotId = dss.snapshotId
    LEFT OUTER JOIN dbo.datasourceProperty dsp ON dsp.dsRecordId = ds.dsRecordId
    WHERE dss.snapshotTime = (
        SELECT MAX(snapshotTime) FROM dbo.dataSourceSnapshot
    )
    FOR XML PATH(''), TYPE
).value('.', 'nvarchar(max)')

--- properties in the datapointProperty table
SELECT @dpPropertyColumns = (
    SELECT DISTINCT '[' + [dpPropertyName] + ']' + ','
    FROM dbo.dataSourceSnapshot dss
    LEFT OUTER JOIN dbo.datasource ds ON ds.snapshotId = dss.snapshotId
    LEFT OUTER JOIN dbo.datapointProperty dp ON dp.dsRecordId = ds.dsRecordId
    WHERE dss.snapshotTime = (
        SELECT MAX(snapshotTime) FROM dbo.dataSourceSnapshot
    )
    FOR XML PATH(''), TYPE
).value('.', 'nvarchar(max)')

-- remove the trailing comma
SET @dsPropPropertyColumns = LEFT(@dsPropPropertyColumns, LEN(@dsPropPropertyColumns) - 1);
SET @dpPropertyColumns = LEFT(@dpPropertyColumns, LEN(@dpPropertyColumns) - 1);

-- construct dynamic SQL
SET @sql ='
SELECT * FROM (
    SELECT dsp.dsRecordId, dsp.dsPropPropertyName, dsp.dsPropPropertyValue, CONCAT(''dp_'', dp.dpPropertyName) as dpPropertyName, dp.dpPropertyValue
    FROM dbo.snapshot dss
    LEFT OUTER JOIN dbo.datasource ds ON ds.snapshotId = dss.snapshotId
    LEFT OUTER JOIN dbo.datasourceProperty dsp ON dsp.dsRecordId = ds.dsRecordId
    LEFT OUTER JOIN dbo.datapointProperty dp ON dp.dsRecordId = ds.dsRecordId
    WHERE dss.snapshotTime = (
        SELECT MAX(snapshotTime) FROM dbo.dataSourceSnapshot
    )
) t1
PIVOT (
    MAX(dsPropPropertyValue) FOR dsPropPropertyName IN ('+ @dsPropPropertyColumns +')
) AS pivot_table
PIVOT (
    MAX(dpPropertyValue) FOR dpPropertyName IN ('+ @dpPropertyColumns +')
) AS pivot_table2
ORDER BY id
'

EXECUTE sp_executesql @sql;

正确命名透视列。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    • 2019-12-22
    • 1970-01-01
    • 2020-08-18
    • 1970-01-01
    • 2021-01-19
    相关资源
    最近更新 更多