发生TSQL错误取决于PIVOT

时间:2017-06-28 00:43:57

标签: sql-server tsql pivot sql-server-2016

在MSSQL Server 2016中使用PIVOT时,我偶然发现了非常奇怪的行为。我试图使用PIVOT检索数据并根据查询返回的行数获取错误。

以下是我专门为重现此错误而编写的查询。

我现在要补充一点,我知道行大小限制,包括知道nvarchar(max)类型指针也受这些限制。 但是:

  1. 在这种情况下,由于PIVOT语句的一些内部工作原理及其对临时表的使用(猜测),似乎发生错误
  2. 我不明白行大小限制与select语句返回的行数有什么关系。
  3. 我非常希望了解此错误,以便编写可避免此问题的查询。它也可能是某种bug,但我认为它现在已经解决,所以可能不是一个bug。

    我得到的错误是:

      

    消息511,级别16,状态1无法创建大小为XXXX的行   大于允许的最大行大小8060。

    以下是我测试过的几个参数值组合(第一次查询)。当值超出下面指定的限制时发生错误:

    • 当NoOfGroupKeys为1时,NoOfFieldNames最多可达615
    • 当NoOfGroupKeys为2时,NoOfFieldNames最多可达308
    • 当NoOfGroupKeys为3或更多(检查到100)时,NoOfFieldNames最多可达307

    当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上对此进行了测试,得到了类似的结果:

    1. 当NoOfGroupKeys为1时,NoOfFieldNames最多可达620
    2. 当NoOfGroupKeys为2时,NoOfFieldNames最多可为311
    3. 当NoOfGroupKeys为3或更多时,NoOfFieldNames最多可达307
    4. 更改FieldName类型或FieldValue类型可防止发生错误。

      我真的希望有人可以解释这种行为。

0 个答案:

没有答案