为啥我的 PIVOT 查询使用不包含 NULL 的数据集产生 NULL 结果?
Posted
技术标签:
【中文标题】为啥我的 PIVOT 查询使用不包含 NULL 的数据集产生 NULL 结果?【英文标题】:Why does my PIVOT query produce NULL results with a dataset that contains no NULLs?为什么我的 PIVOT 查询使用不包含 NULL 的数据集产生 NULL 结果? 【发布时间】:2022-01-06 00:40:02 【问题描述】:我有一个设备数据库,其中包含有关设备的数据,包括名称、位置以及许多数字和字符串属性。我正在尝试执行查询,仅将具有数值的属性拉入数据透视表。
当我平面查询数据时,数据看起来很好。但是当我在同一个数据集上运行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
列是字符串而不是数字,但并非所有属性本质上都是数字的。 (这是供应商的架构。)
当我对表执行平面查询(使用 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
段中尝试了许多不同的函数——MIN
、MAX
、SUM
、AVG
——所有这些都会产生相同的结果。
我尝试将数据转储到一个临时表中——一个在attribute_value
列上具有数字数据类型的临时表——但它产生了相同的结果,无论是平面查询还是旋转查询。
我已经在attribute_value
列上尝试了NOT NULL
约束,包括基表和临时表。同样的结果。
我做错了什么?
【问题讨论】:
据我所知,在您的 PIVOT 中,当您似乎应该使用 [attrib #1] 时,您引用了 [attrib_1](例如) 这是 pivot 的问题之一 - 因为它生成的列来自列数据 values,因此服务器无法预先告诉您元数据“你拼错了”。 【参考方案1】:这是一个工作示例。注意:@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
【讨论】:
以上是关于为啥我的 PIVOT 查询使用不包含 NULL 的数据集产生 NULL 结果?的主要内容,如果未能解决你的问题,请参考以下文章