在MSSQL Server 2016中使用PIVOT时,我偶然发现了非常奇怪的行为。我试图使用PIVOT检索数据并根据查询返回的行数获取错误。
以下是我专门为重现此错误而编写的查询。
我现在要补充一点,我知道行大小限制,包括知道nvarchar(max)
类型指针也受这些限制。
但是:
我非常希望了解此错误,以便编写可避免此问题的查询。它也可能是某种bug,但我认为它现在已经解决,所以可能不是一个bug。
我得到的错误是:
消息511,级别16,状态1无法创建大小为XXXX的行 大于允许的最大行大小8060。
以下是我测试过的几个参数值组合(第一次查询)。当值超出下面指定的限制时发生错误:
当FieldValue类型更改为nvarchar(4000)
或FieldName类型更改为int
(第二个查询)时,不会发生错误,并且我能够成功运行查询,每个列填充64KB数据
首先查询:
CREATE TABLE PvErrorTest (
GroupKeyID INT NOT NULL
,FieldName NVARCHAR (50) NOT NULL
,FieldValue NVARCHAR (MAX) NULL
,UNIQUE (
GroupKeyID
,FieldName
)
)
GO
DECLARE
-- When NoOfGroupKeys is 1 then NoOfFieldNames can be up to 615
-- When NoOfGroupKeys is 2 then NoOfFieldNames can be up to 308
-- When NoOfGroupKeys is 3 and more (checked up to 100) then NoOfFieldNames can be up to 307
@NoOfGroupKeys INT = 2
,@NoOfFieldNames INT = 309
-- Data in FieldValue doesn't seem to affect this test
,@FieldValue NVARCHAR (MAX) = NULL--REPLICATE('X', 8000)
--SET @FieldValue += @FieldValue
--SELECT DATALENGTH(@FieldValue)
DECLARE @_GroupKeys TABLE (
GroupKeyID INT NOT NULL
PRIMARY KEY CLUSTERED
)
DECLARE @_FieldNames TABLE (
FieldName NVARCHAR (50) NOT NULL
UNIQUE
)
SET NOCOUNT ON
DECLARE
@GKID INT = 0
,@FNID INT
,@PVC NVARCHAR (MAX) = ''
WHILE @GKID < @NoOfGroupKeys
BEGIN
SET @GKID += 1
INSERT INTO @_GroupKeys (GroupKeyID)
VALUES (@GKID)
END
SET @FNID = 0
WHILE @FNID < @NoOfFieldNames
BEGIN
SET @FNID += 1
INSERT INTO @_FieldNames (FieldName)
VALUES ('FN_' + CAST(@FNID AS NVARCHAR(50)))
SET @PVC += ',[FN_' + CAST(@FNID AS NVARCHAR(MAX)) + ']'
END
INSERT INTO PvErrorTest (GroupKeyID, FieldName, FieldValue)
SELECT
GroupKeyID = GK.GroupKeyID
,FieldName = FN.FieldName
,FieldValue = @FieldValue--NULL--NEWID()
FROM
@_GroupKeys AS GK
CROSS JOIN @_FieldNames AS FN
SET NOCOUNT OFF
DECLARE @_Query NVARCHAR(MAX) = '
SELECT
PV.GroupKeyID
' + @PVC + '
FROM
(SELECT GroupKeyID, FieldName, FieldValue FROM PvErrorTest)
AS PVIN
PIVOT
(
MAX(PVIN.FieldValue)
FOR PVIN.FieldName IN ([FN_0]' + @PVC + ')
) AS PV
'
EXEC sp_executesql
@_Query
GO
DROP TABLE PvErrorTest
GO
第二次查询:
CREATE TABLE PvErrorTest (
GroupKeyID INT NOT NULL
,FieldName INT NOT NULL
,FieldValue NVARCHAR (MAX) NULL
,PRIMARY KEY CLUSTERED (
GroupKeyID
,FieldName
)
)
GO
DECLARE
-- When FieldName is of INT type, error does not occur
@NoOfGroupKeys INT = 4
,@NoOfFieldNames INT = 4095
-- Data in FieldValue doesn't seem to affect this test
,@FieldValue NVARCHAR (MAX) = REPLICATE('X', 8000)
DECLARE @_GroupKeys TABLE (
GroupKeyID INT NOT NULL
PRIMARY KEY CLUSTERED
)
DECLARE @_FieldNames TABLE (
FieldName INT NOT NULL
PRIMARY KEY CLUSTERED
)
SET NOCOUNT ON
DECLARE
@GKID INT = 0
,@FNID INT
,@PVC NVARCHAR (MAX) = ''
WHILE @GKID < @NoOfGroupKeys
BEGIN
SET @GKID += 1
INSERT INTO @_GroupKeys (GroupKeyID)
VALUES (@GKID)
END
SET @FNID = 0
WHILE @FNID < @NoOfFieldNames
BEGIN
SET @FNID += 1
INSERT INTO @_FieldNames (FieldName)
VALUES (@FNID)
SET @PVC += ',[' + CAST(@FNID AS NVARCHAR(MAX)) + ']'
END
INSERT INTO PvErrorTest (GroupKeyID, FieldName, FieldValue)
SELECT
GroupKeyID = GK.GroupKeyID
,FieldName = FI.FieldName
,FieldValue = @FieldValue
FROM
@_GroupKeys AS GK
CROSS JOIN @_FieldNames AS FI
SET NOCOUNT OFF
DECLARE @_Query NVARCHAR(MAX) = '
SELECT
PV.GroupKeyID
' + @PVC + '
FROM
(SELECT GroupKeyID, FieldName, FieldValue FROM PvErrorTest)
AS PVIN
PIVOT
(
MAX(PVIN.FieldValue)
FOR PVIN.FieldName IN ([0]' + @PVC + ')
) AS PV
'
EXEC sp_executesql
@_Query
GO
DROP TABLE PvErrorTest
GO
我现在在MSSQL Server 2008 R2上对此进行了测试,得到了类似的结果:
更改FieldName类型或FieldValue类型可防止发生错误。
我真的希望有人可以解释这种行为。