无法使用 NVARCHAR(Max) 创建完整的动态查询

Unable to Create the Complete Dynamic Query using NVARCHAR(Max)

我正在使用 NVARCHAR(MAX) 创建动态 query.Since NVARCHAR 每个字符使用 2 个字节,NVARCHAR(MAX) 变量中可以包含大约 10 亿个字符 (Link Reference).

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

这两种情况动态查询都是不超过那些字符长度的。但只有部分动态查询是获取 executed.Because 存储过程向应用程序抛出错误。

我是否遗漏了任何代码?

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)
  • 确保您的变量不包含引号或双引号。

    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:

    之间至少加上一个 space
    INSERT INTO'+ @AreaSchema+'.[SN_PLANNING_FGAllocation]
    -- space is missing
    

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

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

您将所有内容声明为 NVARCHAR,然后将其与 '' 连接。应该是N''。您还将 convert/cast 一切都作为 VARCHAR。它应该是 NVARCHAR(...)。 动态 SQL 使用 NVARCHAR 字符串。

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

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

这将 return NULL,因为@BucketId 未在您的存储过程中设置。使用 ISNULL,设置它或给它一个默认值

仅在需要时保持串联(动态对象名称)

  • 终于

在尝试执行它之前,请确保您的打印输出正确的查询(打印仅显示前 4000 个字符)。如果查询不完整,可能意味着您在停止处附近的代码中有错误。 我试过你的过程,但它 return 没有任何东西(NULL),除非我首先初始化所有变量。还有截断,因为你不使用 N''

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

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

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

在此这是解释。

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 = [concatenate various unicode strings and nvarchar variables totalling over 4000 characters] -- MAKE SURE AT LEAST ONE OF THE UNICODE STRINGS IS NVARCHAR(MAX), YOU CAN CAST ANY ONE ARGUMENT.

你也可以检查这个link。

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

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

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

  2. 不过,如果你想去,那么我会删除你额外的代码(我不认为删除 space 会起作用,我有 4 个联合查询,它的长度非常大,它在单独验证每个部分后使用此策略 window)

在阅读下文之前,

a.Here 是另一种选择。除了在查询中定义参数,您也可以传递此参数。

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 定义为数值,最后 print 打印一些东西。如果我们不定义它永远不会打印空白,因为连接设置了空值。

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