我正在使用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)
答案 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,设置它或给它一个默认值
您应该使用EXEC sp_executesql并在使用它们时声明变量 https://msdn.microsoft.com/fr-fr/library/ms188001(v=sql.120).aspx
EXEC sp_executesql @yourquery, N'@UserID int, ...', @userID = @UserId ...
仅在需要时保持连接(动态对象名称)
在尝试执行之前,请确保您的打印输出正确的查询(打印仅显示前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),你可以任何一个论点。
您也可以查看此链接。
<强>更新强> 我展示了你的整个代码,想要解释一些事情。
首先,为什么要进行动态查询。代码显示您可以在没有动态查询的情况下执行此操作,并且嵌套游标也很多(尝试通过简单查询忽略它)
如果你想去,那么我会删除你额外的代码(我不认为删除空间会起作用,我有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
湾您在动态查询中使用了相同的参数。所以我认为你的问题必须在运行时给出默认值或赋值。因此,在连接字符串时,不会变为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