![](/img/trans.png)
[英]Why NVarchar(MAX) cannot execute complete dynamic query using SP_ExecuteSQL?
[英]Unable to Create the Complete Dynamic Query using NVARCHAR(Max)
我正在使用NVARCHAR(MAX)
創建動態查詢。由於NVARCHAR
每個字符使用2個字節,因此NVARCHAR(MAX)
變量(鏈接參考)中可以包含大約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)
確保您的變量不包含引號或雙引號。
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) + '
由於未在存儲過程中設置@BucketId,因此將返回NULL。 使用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 ...
僅在需要時才進行串聯(動態對象名稱)
在嘗試執行它之前,請確保您的打印輸出正確的查詢(打印僅顯示第1個4000個字符)。 如果查詢不完整,則可能意味着您在停止行附近的代碼中有錯誤。 我嘗試了您的proc,除非我首先初始化所有變量,否則它將不返回任何內容(NULL)。 由於不使用N,也存在截斷現象''
是的,由於nvarchar限制為4000個字符,您可能會遇到此問題。
我也遇到這個問題,並通過連接字符串然后執行來解決。
如果選擇或打印,則僅顯示4000個字符,但是如果連接或附加字符串,則必須附加(直到8000個字符)。 因此,不必為此而煩惱,您不會打印或選擇僅追加並執行及其絕對有效的方法。
在此鏈接中進行解釋。
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)
有一個規則 :
SET @dynamicSQL = [連接各種unicode字符串和總共超過4000個字符的nvarchar變量] –確保UNICODE字符串中至少有一個是NVARCHAR(MAX),您可以使用任何一個參數。
您也可以檢查此鏈接。
更新后,我將顯示您的整個代碼,並希望解釋一些事情。
首先,為什么要動態查詢。 代碼顯示您可以在沒有動態查詢的情況下執行此操作,並且嵌套的游標太多(嘗試通過簡單查詢忽略它)
仍然,如果您想去,那么我將為您刪除多余的代碼(我認為刪除空間將不起作用,我有4個聯合查詢,其長度非常大,並且在單獨的窗口中驗證了每個部分后使用了該策略)
在您閱讀以下內容之前,這里是另一種選擇。 而不是在查詢中定義參數,您也可以傳遞此參數。
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。 請參見下面的示例。 我將所有字符定義為'',並將int定義為數值,最后打印出可打印內容的字符。 如果沒有定義,則由於連接設置為null值,因此永遠不要打印空白。
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.