为什么我的 PIVOT 查询使用不包含 NULL 的数据集生成 NULL 结果?

Why does my PIVOT query produce NULL results with a dataset that contains no NULLs?

我有一个设备数据库,其中包含有关设备的数据,包括名称、位置以及一些数字和字符串属性。我正在尝试执行查询,仅将那些具有数值的属性拉入数据透视表 table.

当我平淡查询数据时,数据看起来没问题。但是当我 运行 对同一个数据集的 PIVOT 查询时,所有结果都是 NULL.

设置:

/* database setup and data insert */

USE [master] ;
GO

IF EXISTS ( SELECT * FROM sys.databases WHERE [name] = N'DeviceDatabase' )
BEGIN
    ALTER DATABASE [DeviceDatabase] SET SINGLE_USER WITH ROLLBACK IMMEDIATE ;
    DROP DATABASE [DeviceDatabase] ;
END
CREATE DATABASE [DeviceDatabase] ;
GO

USE [DeviceDatabase] ;
GO

CREATE TABLE dbo.Devices
(
    [id]                        INT             NOT NULL    IDENTITY (1,1)
        CONSTRAINT      [PK_Devices]            PRIMARY KEY CLUSTERED
    , [device_name]             VARCHAR(50)     NOT NULL
    , [device_display_name]     VARCHAR(50)     NOT NULL
    , [device_location]         VARCHAR(50)     NULL
) ;

CREATE TABLE dbo.DeviceGroups
(
    [id]                        INT             NOT NULL    IDENTITY (1,1)
        CONSTRAINT      [PK_DeviceGroups]       PRIMARY KEY CLUSTERED
    , [device_group_name]       VARCHAR(50)     NOT NULL
) ;

CREATE TABLE dbo.DeviceDeviceGroups
(
    [id]                        INT             NOT NULL    IDENTITY (1,1)
        CONSTRAINT      [PK_DeviceDeviceGroups] PRIMARY KEY CLUSTERED
    , [device_id]               INT             NOT NULL
        CONSTRAINT      [FK_DeviceDeviceGroups_Devices]
        FOREIGN KEY REFERENCES dbo.Devices ( [id] )
    , [group_id]                INT             NOT NULL
        CONSTRAINT      [FK_DeviceDeviceGroups_DeviceGroups]
        FOREIGN KEY REFERENCES dbo.DeviceGroups ( [id] )
) ;

CREATE TABLE dbo.DeviceAttributes
(
    [id]                        INT             NOT NULL    IDENTITY (1,1)
        CONSTRAINT      [PK_DeviceAttributes]   PRIMARY KEY CLUSTERED
    , [device_id]               INT             NOT NULL
        CONSTRAINT      [FK_DeviceAttributes_Devices]
        FOREIGN KEY REFERENCES dbo.Devices ( [id] )
    , [attribute_name]          VARCHAR(50)     NOT NULL
    , [attribute_value]         VARCHAR(50)     NULL
) ;
GO

INSERT
    INTO        dbo.Devices
                ( [device_name], [device_display_name], [device_location] )
    VALUES      ( 'dev001', 'Device 1', 'Location A' )
                , ( 'dev002', 'Device 2', 'Location A' )
                , ( 'dev003', 'Device 3', 'Location B' )
                , ( 'dev004', 'Device 4', 'Location B' ) ;

INSERT
    INTO        dbo.DeviceGroups
                ( [device_group_name] )
    VALUES      ( 'Group 1A' )  
                , ( 'Group 1B' ) ;

INSERT
    INTO        dbo.DeviceDeviceGroups
                ( [device_id], [group_id] )
    VALUES      ( 1, 1 )
                , ( 2, 1 )
                , ( 3, 1 )
                , ( 4, 1 ) ;

INSERT
    INTO        dbo.DeviceAttributes
                ( [device_id], [attribute_name], [attribute_value] )
    VALUES      ( 1, 'attrib #1', '0.10' )
                , ( 1, 'attrib #2', '0.02' )
                , ( 1, 'attrib #3', '0.07' )
                , ( 1, 'attrib #4', '0.02' )
                , ( 2, 'attrib #1', '0.16' )
                , ( 2, 'attrib #2', '0.05' )
                , ( 2, 'attrib #3', '0.12' )
                , ( 2, 'attrib #4', '0.04' )
                , ( 3, 'attrib #1', '0.15' )
                , ( 3, 'attrib #2', '0.05' )
                , ( 3, 'attrib #3', '0.07' )
                , ( 3, 'attrib #4', '0.06' )
                , ( 4, 'attrib #1', '0.10' )
                , ( 4, 'attrib #2', '0.03' )
                , ( 4, 'attrib #3', '0.07' )
                , ( 4, 'attrib #4', '0.03' ) ;
GO

令人恼火的是 attribute_value 列是字符串而不是数字,但并非所有属性本质上都是数字。 (这是供应商的架构。)

当我针对 table 执行平面查询(使用 CTE)时,我得到了一整套属性值。

查询:

/* flat query */

USE [DeviceDatabase] ;
GO

DECLARE     @PrinterGroup   AS VARCHAR(50) ;
SET         @PrinterGroup   = 'Group 1A' ;

WITH cte_GroupedDevices AS
(
    SELECT          d.[id] AS [device_id]
                    , d.[device_name]
                    , d.[device_display_name]
                    , d.[device_location]
                    , dg.[device_group_name]
    FROM            dbo.Devices AS d
        INNER JOIN  dbo.DeviceDeviceGroups AS ddg
            ON      d.[id] = ddg.[device_id]
        INNER JOIN  dbo.DeviceGroups AS dg
            ON      ddg.[group_id] = dg.[id]
    WHERE           dg.[device_group_name] = @PrinterGroup
)
, cte_AttributedDevices AS
(
    SELECT          gd.[device_name]
                    , gd.[device_display_name]
                    , gd.[device_group_name]
                    , gd.[device_location]
                    , da.[attribute_name]                   
                    , CAST ( da.[attribute_value] AS DECIMAL (5,2) ) AS [attribute_value]
    FROM            cte_GroupedDevices AS gd
        INNER JOIN  dbo.DeviceAttributes AS da
            ON      gd.[device_id] = da.[device_id]
    WHERE           da.[attribute_name] IN
                    ( 
                        'attrib #1'
                        , 'attrib #2'
                        , 'attrib #3'
                        , 'attrib #4'
                    )
)
SELECT          [device_display_name]
                , [device_group_name]
                , [device_location]
                , [attribute_name]                  
                , [attribute_value]
FROM            cte_AttributedDevices
ORDER BY        [device_name] ASC, [attribute_name] ASC ;

结果:

device_display_name  |  device_group_name | device_location  |  attribute_name   |  attribute_value
---------------------------------------------------------------------------------------
Device 1                Group 1A            Location A          attrib #1           0.10
Device 1                Group 1A            Location A          attrib #2           0.02
Device 1                Group 1A            Location A          attrib #3           0.07
Device 1                Group 1A            Location A          attrib #4           0.02
Device 2                Group 1A            Location A          attrib #1           0.16
Device 2                Group 1A            Location A          attrib #2           0.05
Device 2                Group 1A            Location A          attrib #3           0.12
Device 2                Group 1A            Location A          attrib #4           0.04
Device 3                Group 1A            Location B          attrib #1           0.15
Device 3                Group 1A            Location B          attrib #2           0.05
Device 3                Group 1A            Location B          attrib #3           0.07
Device 3                Group 1A            Location B          attrib #4           0.06
Device 4                Group 1A            Location B          attrib #1           0.10
Device 4                Group 1A            Location B          attrib #2           0.03
Device 4                Group 1A            Location B          attrib #3           0.07
Device 4                Group 1A            Location B          attrib #4           0.03

但是当我执行 PIVOT 查询(旋转 attribute_name 列)时,使用相同的 CTE 基础,针对相同的数据集,所有属性值都为 NULL。

查询:

/* pivot query */

USE [DeviceDatabase] ;
GO

DECLARE     @PrinterGroup   AS VARCHAR(50) ;
SET         @PrinterGroup   = 'Group 1A' ;

WITH cte_GroupedDevices AS
(
    SELECT          d.[id] AS [device_id]
                    , d.[device_name]
                    , d.[device_display_name]
                    , d.[device_location]
                    , dg.[device_group_name]
    FROM            dbo.Devices AS d
        INNER JOIN  dbo.DeviceDeviceGroups AS ddg
            ON      d.[id] = ddg.[device_id]
        INNER JOIN  dbo.DeviceGroups AS dg
            ON      ddg.[group_id] = dg.[id]
    WHERE           dg.[device_group_name] = @PrinterGroup
)
, cte_AttributedDevices AS
(
    SELECT          gd.[device_name]
                    , gd.[device_display_name]
                    , gd.[device_group_name]
                    , gd.[device_location]
                    , da.[attribute_name]                   
                    , CAST ( da.[attribute_value] AS DECIMAL (5,2) ) AS [attribute_value]
    FROM            cte_GroupedDevices AS gd
        INNER JOIN  dbo.DeviceAttributes AS da
            ON      gd.[device_id] = da.[device_id]
    WHERE           da.[attribute_name] IN
                    ( 
                        'attrib #1'
                        , 'attrib #2'
                        , 'attrib #3'
                        , 'attrib #4'
                    )
)
SELECT          [device_display_name]
                , [device_group_name]
                , [device_location]
                , [attrib_1]
                , [attrib_2]
                , [attrib_3]
                , [attrib_4]
FROM            cte_AttributedDevices
PIVOT
(
    MIN ( [attribute_value] )
    FOR [attribute_name] IN
    (
        [attrib_1]
        , [attrib_2]
        , [attrib_3]
        , [attrib_4]
    )
) AS pvt
ORDER BY        [device_name] ASC ;

结果:

device_display_name  |  device_group_name | device_location  |  attrib_1  | attrib_2 |  attrib_3  | attrib_4
------------------------------------------------------------------------------------------------------------
Device 1                Group 1A            Location A          NULL        NULL        NULL        NULL
Device 2                Group 1A            Location A          NULL        NULL        NULL        NULL
Device 3                Group 1A            Location B          NULL        NULL        NULL        NULL
Device 4                Group 1A            Location B          NULL        NULL        NULL        NULL

我在查询的 PIVOT 部分尝试了许多不同的函数 -- MINMAXSUMAVG - - 所有这些都产生相同的结果。

我已经尝试将数据转储到临时文件 table 中——一个在 attribute_value 列上具有数字数据类型的文件——但它产生相同的结果,无论是 flat 还是旋转查询。

而且我已经在 attribute_value 列上尝试了 NOT NULL 约束,在基础 table 和临时 table 中都是如此。同样的结果。

我做错了什么?

这是一个工作示例。注意:@YourResults 是您实际初始查询的替代品。

我没有看到将 attrib #1 转换为 attrib_1

的任何逻辑

例子

Declare @YourResults Table ([device_display_name] varchar(50),[device_group_name] varchar(50),[device_location] varchar(50),[attribute_name] varchar(50),[attribute_value] varchar(50))
Insert Into @YourResults Values 
 ('Device 1','Group 1A','Location A','attrib #1',0.10)
,('Device 1','Group 1A','Location A','attrib #2',0.02)
,('Device 1','Group 1A','Location A','attrib #3',0.07)
,('Device 1','Group 1A','Location A','attrib #4',0.02)
,('Device 2','Group 1A','Location A','attrib #1',0.16)
,('Device 2','Group 1A','Location A','attrib #2',0.05)
,('Device 2','Group 1A','Location A','attrib #3',0.12)
,('Device 2','Group 1A','Location A','attrib #4',0.04)
,('Device 3','Group 1A','Location B','attrib #1',0.15)
,('Device 3','Group 1A','Location B','attrib #2',0.05)
,('Device 3','Group 1A','Location B','attrib #3',0.07)
,('Device 3','Group 1A','Location B','attrib #4',0.06)
,('Device 4','Group 1A','Location B','attrib #1',0.10)
,('Device 4','Group 1A','Location B','attrib #2',0.03)
,('Device 4','Group 1A','Location B','attrib #3',0.07)
,('Device 4','Group 1A','Location B','attrib #4',0.03)
 
Select * 
 From @YourResults
 Pivot (min([attribute_value]) for [attribute_name] IN (
                                                         [attrib #1]
                                                       , [attrib #2]
                                                       , [attrib #3]
                                                       , [attrib #4]
                                                       ) ) Pvt

结果

编辑 - 如果你想 [attrib_1] 你可以指定列并分配别名。

Select [device_display_name]
      ,[device_group_name]
      ,[device_location]
      ,[attrib_1] = [attrib #1]
      ,[attrib_2] = [attrib #2]
      ,[attrib_3] = [attrib #3]
      ,[attrib_4] = [attrib #4]
 From @YourResults
 Pivot (min([attribute_value]) for [attribute_name] IN (
                                                         [attrib #1]
                                                       , [attrib #2]
                                                       , [attrib #3]
                                                       , [attrib #4]
                                                       ) ) Pvt