【问题标题】:Unable to Create the Complete Dynamic Query using NVARCHAR(Max)无法使用 NVARCHAR(Max) 创建完整的动态查询
【发布时间】:2015-10-31 15:50:41
【问题描述】:

我正在使用NVARCHAR(MAX) 创建动态查询。由于NVARCHAR 每个字符使用2 个字节,因此NVARCHAR(MAX) 变量(Link Reference) 中可以包含大约10 亿个字符。

我尝试在 SQL Server 本身中执行存储过程,然后通过应用程序执行存储过程。

这两种情况动态查询不超过那些字符长度。但只有部分动态查询被执行。因为该存储过程向应用程序抛出错误。

我是否遗漏了任何代码?

USE [MyDemoDB]
GO

ALTER PROCEDURE [dbo].[sp_Apply]
(
        @scenarioId INT,
        @userId INT,
        @bookId INT
) 
AS

        DECLARE @BucketId           INT
        DECLARE @HierarchyId        NVARCHAR(10)
        DECLARE @Year               INT
        DECLARE @Month              INT
        DECLARE @PlanningSeason     NVARCHAR(20)
        DECLARE @StructureId        INT = 9
        DECLARE @AllocStructureId   INT = 11
        DECLARE @UpdatedUser        INT = 2
        DECLARE @InsertOne          NVARCHAR(MAX)=''
        DECLARE @AreaSchema         NVARCHAR(40)
        DECLARE @AreaCode           NVARCHAR(20)
        DECLARE @EmptyValue         NVARCHAR(20)

        SET @AreaCode   = ''
        SET @AreaSchema = '[dbo]'


          SET @InsertOne =      '
                                   DECLARE @FGSupplySeqId      INT
                                   DECLARE @FGSupplyId         NVARCHAR(10)
                                   DECLARE @PlannedQty         DECIMAL(18,2)
                                   DECLARE @ConfirmdQty        DECIMAL(18,2)
                                   DECLARE @Year                INT 
                                   DECLARE @Month               INT
                                   DECLARE @Season              NVARCHAR(20)
                                   DECLARE @MerchantId          NVARCHAR(50) 
                                   DECLARE @UpdatedUser     INT 
                                   DECLARE @HierarchyId     NVARCHAR(10) 
                                   DECLARE @BucketId        INT
                                   DECLARE @ProductNo       NVARCHAR(100)
                                   DECLARE @LocationNo      NVARCHAR(100) 

                                   SET @BucketId            = '+ CAST(@BucketId AS VARCHAR) + '
                                   SET @UpdatedUser         = '+ CAST(@userId AS VARCHAR) + '

                       IF @BucketId = 1 
                       BEGIN
                                   DECLARE Supplys
                                   CURSOR FOR 
                                   SELECT [FGSupplySeqId],[FGSupplyId] FROM ' + @AreaSchema + '.[FGSupply]
                                   WHERE  PlanningScenarioId ='+ CONVERT(VARCHAR(10),@scenarioId)+ '

                                   OPEN Supplys
                                   FETCH NEXT 
                                   FROM Supplys 
                                   INTO @FGSupplySeqId,@FGSupplyId

                                           WHILE @@FETCH_STATUS = 0 
                                           BEGIN

                                                DECLARE Allocations
                                                CURSOR FOR  
                                                SELECT @FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,ConfirmedQty,Year,Season,ProductNo,LocationNo
                                                FROM  '+ @AreaSchema +'.[FGAllocation]
                                                WHERE FGSupplySeqId = @FGSupplySeqId

                                                OPEN Allocations
                                                FETCH NEXT 
                                                FROM Allocations 
                                                INTO @FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@ConfirmdQty,@Year,@Season,@ProductNo,@LocationNo

                                                     WHILE @@FETCH_STATUS = 0 
                                                     BEGIN

                                                         DECLARE @FGAllocationId  NVARCHAR(10)
                                                         DECLARE @AllocStatus  INT
                                                         SET @FGAllocationId = ''E''
                                                         SET @AllocStatus= 0

                                                          SELECT @FGAllocationId = FGAllocationId,@AllocStatus=Status
                                                          FROM ' + @AreaSchema+'.[SN_PLANNING_FGAllocation]
                                                          WHERE [HierarchyId]=@HierarchyId AND  [MerchantNo]=@MerchantId AND YEAR = @Year AND [Month] IS NULL


                                                          IF @FGAllocationId = ''E''
                                                          BEGIN

                                                          -- IF  @AllocStatus <> 5

                                                          INSERT INTO'+ @AreaSchema+'.[SN_PLANNING_FGAllocation]
                                                         (FinishedGoodSupplyId,FGHierarchyId,MerchantNo,PlannedQty,Year,Season,Status,IsActive,CreatedBy,UpdatedBy,CreatedOn,UpdatedOn,ProductNo,LocationNo)
                                                         VALUES(@FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@Year,@Season,0,1,@UpdatedUser,@UpdatedUser,GETDATE(),GETDATE(),@ProductNo,@LocationNo)

                                                         END
                                                         ELSE 
                                                         BEGIN
                                                            -- IF  @AllocStatus <> 5
                                                                    UPDATE ' + @AreaSchema + '.[SN_PLANNING_FGAllocation] 
                                                                    SET PlannedQty = @PlannedQty ,ConfirmedQty=@ConfirmdQty,UpdatedBy=@UpdatedUser, UpdatedOn=GETDATE()
                                                                    WHERE FGAllocationId = @FGAllocationId

                                                         END
                                                           FETCH NEXT 
                                                           FROM Allocations 
                                                           INTO @FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@ConfirmdQty,@Year,@Season,@ProductNo,@LocationNo
                                                     END

                                                 CLOSE Allocations
                                                 DEALLOCATE Allocations

                                               FETCH NEXT 
                                               FROM Supplys 
                                               INTO @FGSupplySeqId,@FGSupplyId
                                          END

                                   CLOSE Supplys
                                   DEALLOCATE Supplys
                    END

                    IF @BucketId = 2 
                       BEGIN
                                   DECLARE Supplys
                                   CURSOR FOR 
                                   SELECT [FGSupplySeqId],[FGSupplyId] FROM ' + @AreaSchema + '.[FGSupply]
                                   WHERE  PlanningScenarioId ='+ CONVERT(VARCHAR(10),@scenarioId)+ 'AND Month IS NOT NULL

                                   OPEN Supplys
                                   FETCH NEXT 
                                   FROM Supplys 
                                   INTO @FGSupplySeqId,@FGSupplyId

                                           WHILE @@FETCH_STATUS = 0 
                                           BEGIN

                                                DECLARE Allocations
                                                CURSOR FOR  
                                                SELECT @FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,ConfirmedQty,Year, Month,Season,@ProductNo,@LocationNo
                                                FROM  '+ @AreaSchema +'.[FGAllocation]
                                                WHERE FGSupplySeqId = @FGSupplySeqId AND Month IS NOT NULL

                                                OPEN Allocations
                                                FETCH NEXT 
                                                FROM Allocations 
                                                INTO @FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@ConfirmdQty,@Year,@Month,@Season,@ProductNo,@LocationNo

                                                     WHILE @@FETCH_STATUS = 0 
                                                     BEGIN

                                                          DECLARE @FGAllocationId1  NVARCHAR(10)
                                                         SET @FGAllocationId1 = ''E''

                                                          SELECT @FGAllocationId1 = FGAllocationId,@AllocStatus=Status
                                                          FROM ' + @AreaSchema+'.[SN_PLANNING_FGAllocation]
                                                          WHERE [HierarchyId]=@HierarchyId AND  [MerchantNo]=@MerchantId AND YEAR = @Year AND [Month] = @Month

                                                          IF @FGAllocationId1 = ''E''

                                                          BEGIN
                                                         --  IF  @AllocStatus <> 5
                                                          INSERT INTO'+ @AreaSchema+'.[SN_PLANNING_FGAllocation]
                                                         (FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,Year,Month,Season,Status,IsActive,CreatedBy,UpdatedBy,CreatedOn,UpdatedOn,ProductNo,LocationNo)
                                                         VALUES(@FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@Year,@Month,@Season,0,1,@UpdatedUser,@UpdatedUser,GETDATE(),GETDATE(),@ProductNo,@LocationNo)
                                                          END
                                                         ELSE 
                                                         BEGIN
                                                         -- IF  @AllocStatus <> 5
                                                                    UPDATE ' + @AreaSchema + '.[SN_PLANNING_FGAllocation] 
                                                                    SET PlannedQty = @PlannedQty ,ConfirmedQty=@ConfirmdQty,UpdatedBy=@UpdatedUser, UpdatedOn=GETDATE()
                                                                    WHERE FGAllocationId = @FGAllocationId1

                                                         END
                                                           FETCH NEXT 
                                                           FROM Allocations 
                                                           INTO @FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@ConfirmdQty,@Year,@Month,@Season,@ProductNo,@LocationNo
                                                     END

                                                 CLOSE Allocations
                                                 DEALLOCATE Allocations

                                               FETCH NEXT 
                                               FROM Supplys 
                                               INTO @FGSupplySeqId,@FGSupplyId
                                          END

                                   CLOSE Supplys
                                   DEALLOCATE Supplys
                    END'

         print @InsertOne
        EXEC(@InsertOne)

【问题讨论】:

标签: sql sql-server stored-procedures nvarchar dynamicquery


【解决方案1】:
  • 确保您的变量不包含引号或双引号。

    declare @value nvarchar(100) = 'abcd''efgh'
    declare @sql nvarchar(max)
    
    -- tons of QUOTE !!!
    Set @sql = N'select ''' + REPLACE(@value, '''', '''''') + '''';
    print @sql 
    exec sp_executesql @sql
    
  • 在您的对象名称周围加上括号,并且在 into 和 DB 之间至少有一个空格:

    INSERT INTO'+ @AreaSchema+'.[SN_PLANNING_FGAllocation]
    -- space is missing
    

    或者你可以使用QUOTENAME函数QUOTENAME(@AreaSchema)

  • 使用 N'' 和 NVARCHAR(...) 或 '' 和 VARCHAR() 但不要混合使用

您将所有内容声明为 NVARCHAR,然后将其与 '' 连接起来。应该是N''。您还可以将所有内容转换/转换为 VARCHAR。它应该是 NVARCHAR(...)。 动态 SQL 使用 NVARCHAR 字符串。

  • 确保变量已设置或具有默认值

    SET @BucketId            = '+ CAST(@BucketId AS nVARCHAR) + '
    

这将返回 NULL,因为您的存储过程中没有设置 @BucketId。要么使用 ISNULL,要么设置它,要么给它一个默认值

仅在需要时才保留连接(动态对象名称)

  • 终于

在尝试执行它之前,请确保您的打印输出正确的查询(打印只显示第一个 4000 个字符)。如果查询不完整,则可能意味着您在停止行附近的代码中有错误。 我尝试了您的 proc,除非我首先初始化所有变量,否则它不会返回任何内容(NULL)。也有截断,因为你不使用 N''

【讨论】:

    【解决方案2】:

    是的,您可能会遇到这个问题,因为 nvarchar 限制为 4000 个字符。

    我也遇到了这个问题,通过连接字符串然后执行来解决。

    如果您选择或打印它只显示 4000 个字符,但如果您连接或附加字符串,它必须附加(直到 8000 个字符)。所以不要为此烦恼,你不打印或选择只是追加和执行,它肯定有效。

    在这个link 这是解释。

    declare @sql Nvarchar(max),
       @a nvarchar(max),
       @b nvarchar(max);
    
    select @sql =N'', @a = N'a', @b = N'b';
    
    select @sql = @sql +replicate(@a,4000) + replicate(@b, 6000);
    
    select len(@sql)
    

    这里有one rule:-

    SET @dynamicSQL = [连接各种 unicode 字符串和 nvarchar 变量总计超过 4000 个字符] -- 确保至少有一个 UNICODE 字符串是 NVARCHAR(MAX),你可以转换任何一个参数。

    您也可以查看此链接。

    https://dba.stackexchange.com/questions/18483/varcharmax-field-cutting-off-data-after-8000-characters-sql-server-2008

    更新 我展示了你的整个代码并想解释一些事情。

    1. 首先,为什么要动态查询。代码表明你可以在没有动态查询的情况下做到这一点,而且还有很多嵌套游标(尝试用简单的查询忽略它)

    2. 如果你想去,那么我删除你额外的代码(我不认为删除空间会起作用,我有 4 个联合查询及其非常大的长度,并且在验证每个部分在单独的窗口中)

    a.在您阅读下文之前,这是另一个选项。而不是在查询中定义参数,你也可以传递这个参数。

    begin tran
    create table table1 ( id int, value varchar(10) )
    insert into table1 values( 1,'001')
    insert into table1 values(2, '002')
    insert into table1 values( 3,'003')
    insert into table1 values( 4,'004')
    
    declare @sql nvarchar(max) , @temp nvarchar(50) = '1,2,3', @tempIntSingleValue nvarchar(50) = '2'
    select * from table1
    
    set @sql = 'select * from table1 where id in ( ' + @temp + ')'
    print @sql
    exec sp_executesql @sql 
    
    set @sql = 'select * from table1 where id in ( @tempInner)'
    print @sql
    exec sp_executesql @sql , N'@tempInner int', @tempInner = @tempIntSingleValue
    
    rollback
    

    b.您在动态查询中使用了相同的参数。所以我认为您必须在运行时提供默认值或分配值。所以在连接你的字符串时不会变成空。请参阅下面的示例。我将所有字符定义为 '' 并将 int 定义为数值,最后打印出打印的东西。如果我们没有定义它,则永远不会因为连接设置空值而打印空白。

    declare @scenarioId INT = 1 ,
            @userId INT = 5,
            @bookId INT = 1
    
     DECLARE @BucketId           INT = 0
            DECLARE @HierarchyId        NVARCHAR(10)
            DECLARE @Year               INT
            DECLARE @Month              INT
            DECLARE @PlanningSeason     NVARCHAR(20)
            DECLARE @StructureId        INT = 9
            DECLARE @AllocStructureId   INT = 11
            DECLARE @UpdatedUser        INT = 2
            DECLARE @InsertOne          NVARCHAR(MAX) =''
            DECLARE @AreaSchema         NVARCHAR(40)
            DECLARE @AreaCode           NVARCHAR(20)
            DECLARE @EmptyValue         NVARCHAR(20)
    
            SET @AreaCode = ''
            SET @AreaSchema = '[dbo]'
    
             SET @InsertOne =      
             'DECLARE @FGSupplySeqId INT = 5
             DECLARE @FGSupplyId NVARCHAR(10) = ''''
             DECLARE @PlannedQty DECIMAL(18,2) = ''''
             DECLARE @ConfirmdQty DECIMAL(18,2) = ''''
             DECLARE @Year INT = 2015
             DECLARE @Month INT = 7
             DECLARE @Season NVARCHAR(20) = ''''
             DECLARE @MerchantId NVARCHAR(50)  = ''''
             DECLARE @UpdatedUser INT 
             DECLARE @HierarchyId NVARCHAR(10) = ''''
             DECLARE @BucketId INT = 0
             DECLARE @ProductNo NVARCHAR(100)= ''''
             DECLARE @LocationNo NVARCHAR(100) 
    
             SET @BucketId = '+ CAST(@BucketId AS VARCHAR) + '
             SET @UpdatedUser = '+ CAST(@userId AS VARCHAR) + '
    
             IF @BucketId = 1 
             BEGIN
              DECLARE Supplys
              CURSOR FOR 
              SELECT [FGSupplySeqId],[FGSupplyId] FROM ' + @AreaSchema + '.[FGSupply]
              WHERE  PlanningScenarioId ='+ CONVERT(VARCHAR(10),@scenarioId)+ '
    
              OPEN Supplys
              FETCH NEXT 
              FROM Supplys 
              INTO @FGSupplySeqId,@FGSupplyId
    
                      WHILE @@FETCH_STATUS = 0 
                      BEGIN
    
                           DECLARE Allocations
                           CURSOR FOR  
                           SELECT @FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,ConfirmedQty,Year,Season,ProductNo,LocationNo
                           FROM  '+ @AreaSchema +'.[FGAllocation]
                           WHERE FGSupplySeqId = @FGSupplySeqId
    
                           OPEN Allocations
                           FETCH NEXT 
                           FROM Allocations 
                           INTO @FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@ConfirmdQty,@Year,@Season,@ProductNo,@LocationNo
    
       WHILE @@FETCH_STATUS = 0 
       BEGIN
    
           DECLARE @FGAllocationId  NVARCHAR(10)
           DECLARE @AllocStatus  INT
           SET @FGAllocationId = ''E''
           SET @AllocStatus= 0
    
            SELECT @FGAllocationId = FGAllocationId,@AllocStatus=Status
            FROM ' + @AreaSchema+'.[SN_PLANNING_FGAllocation]
            WHERE [HierarchyId]=@HierarchyId AND  [MerchantNo]=@MerchantId AND YEAR = @Year AND [Month] IS NULL
    
    
            IF @FGAllocationId = ''E''
            BEGIN
    
            -- IF  @AllocStatus <> 5
    
            INSERT INTO'+ @AreaSchema+'.[SN_PLANNING_FGAllocation]
           (FinishedGoodSupplyId,FGHierarchyId,MerchantNo,PlannedQty,Year,Season,Status,IsActive,CreatedBy,UpdatedBy,CreatedOn,UpdatedOn,ProductNo,LocationNo)
           VALUES(@FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@Year,@Season,0,1,@UpdatedUser,@UpdatedUser,GETDATE(),GETDATE(),@ProductNo,@LocationNo)
    
           END
           ELSE 
           BEGIN
              -- IF  @AllocStatus <> 5
                      UPDATE ' + @AreaSchema + '.[SN_PLANNING_FGAllocation] 
                      SET PlannedQty = @PlannedQty ,ConfirmedQty=@ConfirmdQty,UpdatedBy=@UpdatedUser, UpdatedOn=GETDATE()
                      WHERE FGAllocationId = @FGAllocationId
    
           END
             FETCH NEXT 
             FROM Allocations 
             INTO @FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@ConfirmdQty,@Year,@Season,@ProductNo,@LocationNo
       END
    
                            CLOSE Allocations
                            DEALLOCATE Allocations
    
                          FETCH NEXT 
                          FROM Supplys 
                          INTO @FGSupplySeqId,@FGSupplyId
                     END
    
              CLOSE Supplys
              DEALLOCATE Supplys
                        END
    
                        IF @BucketId = 2 
                           BEGIN
              DECLARE Supplys
              CURSOR FOR 
              SELECT [FGSupplySeqId],[FGSupplyId] FROM ' + @AreaSchema + '.[FGSupply]
              WHERE  PlanningScenarioId ='+ CONVERT(VARCHAR(10),@scenarioId)+ 'AND Month IS NOT NULL
    
              OPEN Supplys
              FETCH NEXT 
              FROM Supplys 
              INTO @FGSupplySeqId,@FGSupplyId
    
                      WHILE @@FETCH_STATUS = 0 
                      BEGIN
    
                           DECLARE Allocations
                           CURSOR FOR  
                           SELECT @FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,ConfirmedQty,Year, Month,Season,@ProductNo,@LocationNo
                           FROM  '+ @AreaSchema +'.[FGAllocation]
                           WHERE FGSupplySeqId = @FGSupplySeqId AND Month IS NOT NULL
    
                           OPEN Allocations
                           FETCH NEXT 
                           FROM Allocations 
                           INTO @FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@ConfirmdQty,@Year,@Month,@Season,@ProductNo,@LocationNo
    
                           WHILE @@FETCH_STATUS = 0 
                           BEGIN
    
                                DECLARE @FGAllocationId1  NVARCHAR(10)
                               SET @FGAllocationId1 = ''E''
    
                                SELECT @FGAllocationId1 = FGAllocationId,@AllocStatus=Status
                                FROM ' + @AreaSchema+'.[SN_PLANNING_FGAllocation]
                                WHERE [HierarchyId]=@HierarchyId AND  [MerchantNo]=@MerchantId AND YEAR = @Year AND [Month] = @Month
    
                                IF @FGAllocationId1 = ''E''
    
                                BEGIN
                               --  IF  @AllocStatus <> 5
                                INSERT INTO'+ @AreaSchema+'.[SN_PLANNING_FGAllocation]
                               (FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,Year,Month,Season,Status,IsActive,CreatedBy,UpdatedBy,CreatedOn,UpdatedOn,ProductNo,LocationNo)
                               VALUES(@FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@Year,@Month,@Season,0,1,@UpdatedUser,@UpdatedUser,GETDATE(),GETDATE(),@ProductNo,@LocationNo)
                                END
                               ELSE 
                               BEGIN
                               -- IF  @AllocStatus <> 5
                                          UPDATE ' + @AreaSchema + '.[SN_PLANNING_FGAllocation] 
                                          SET PlannedQty = @PlannedQty ,ConfirmedQty=@ConfirmdQty,UpdatedBy=@UpdatedUser, UpdatedOn=GETDATE()
                                          WHERE FGAllocationId = @FGAllocationId1
    
                               END
                                 FETCH NEXT 
                                 FROM Allocations 
                                 INTO @FGSupplyId,@HierarchyId,@MerchantId,@PlannedQty,@ConfirmdQty,@Year,@Month,@Season,@ProductNo,@LocationNo
                           END
    
                            CLOSE Allocations
                            DEALLOCATE Allocations
    
                          FETCH NEXT 
                          FROM Supplys 
                          INTO @FGSupplySeqId,@FGSupplyId
                     END
    
          CLOSE Supplys
          DEALLOCATE Supplys
         END'
    
        print @InsertOne 
    

    【讨论】:

    • 是的,亲爱的,这是限制或说问题。我写的是,如果你选择或打印它总是将字符串中断到 4000 个字符,但如果你执行,你会得到结果。这是我告诉他的经验。
    • 但在我的情况下,当我删除不必要的空间并 cmets 查询有效。
    猜你喜欢
    • 2019-02-26
    • 2013-02-21
    • 1970-01-01
    • 2021-05-14
    • 1970-01-01
    • 2012-10-07
    • 1970-01-01
    • 2013-09-16
    • 1970-01-01
    相关资源
    最近更新 更多