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

时间:2015-08-08 07:09:36

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

我正在使用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)

2 个答案:

答案 0 :(得分: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
    
  • 在您的对象名称周围加上括号,并在和DB之间至少有一个空格:

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

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

  • 使用N&#39;&#39;和NVARCHAR(...)或&#39;&#39;和VARCHAR()但不要混合

您将所有内容声明为NVARCHAR,然后将其连接到&#39;&#39;。它应该是N&#39;。您还将所有内容转换/转换为VARCHAR。它应该是NVARCHAR(...)。 动态SQL使用NVARCHAR字符串。

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

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

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

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

  • 最后

在尝试执行之前,请确保您的打印输出正确的查询(打印仅显示前4000个字符)。如果查询不完整,则可能表示您在停止的行附近的代码中出现错误。 我尝试了你的proc,除了我首先初始化所有变量之外,它没有返回任何内容(NULL)。还有截断,因为您不使用N&#39;

答案 1 :(得分:1)

是的,由于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 STRINGS是NVARCHAR(MAX),你可以任何一个论点。

您也可以查看此链接。

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

<强>更新 我展示了你的整个代码,想要解释一些事情。

  1. 首先,为什么要进行动态查询。代码显示您可以在没有动态查询的情况下执行此操作,并且嵌套游标也很多(尝试通过简单查询忽略它)

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

  3. 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
    

    湾您在动态查询中使用了相同的参数。所以我认为你的问题必须在运行时给出默认值或赋值。因此,在连接字符串时,不会变为null。请参阅下面的示例。我将所有角色定义为&#39;&#39;和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