啟用 XP_CMDShell 甚至禁用後 SQL Server 工作緩慢,直到重新啟動服務
2 天前,我創建了一個需要
INSERT
and的函式,我通過執行腳本UPDATE
啟用並完成了它。XP_CMDShell
之後,SQL Server 對
SELECT
命令的執行速度非常慢。甚至通過單獨的命令SELECT
執行的非常簡單的語句。INSERT
我在其他數據庫上測試了這種行為,幾分鐘後執行結果相同
SELECT
。此外,我在其他 2 台使用 SQL Server 2014 的主機上對其進行了測試,結果是相同的。
我為在另一個 select 語句中獲取它的值而創建的函式:
ALTER FUNCTION [Prg].[intCheckDelayedProcessProgram] ( @SalesOrderProductID INT, @MainProductTreeID INT, @ProductTreeID INT, @ProcessId INT, @additionalDays INT = 2, @currentDateReverceString VARCHAR(10) = NULL ) RETURNS INT AS BEGIN --declare @SalesOrderProductID INT = 40957, -- @MainProductTreeID INT = 93758, -- @ProductTreeID INT = 93758, -- @ProcessId INT = 4472, -- @additionalDays INT = 2, -- @currentDateReverceString VARCHAR(10) = null -- '30/09/1398' DECLARE @ProduceDailyProgramProductTree_Id INT, @ProgramQuantity INT, @startDate JalaliDate, @CurrentDate JalaliDate, @lastDate JalaliDate, @additionalDate JalaliDate, @holiDaysCount INT; IF(@currentDateReverceString IS NULL OR @currentDateReverceString = '') SET @CurrentDate = dbo.GetCurrentJalaliDate(); ELSE SET @CurrentDate = Gnr.RevercePersianDate(@currentDateReverceString); IF (@additionalDays IS NULL) SET @additionalDays = 2; DECLARE @allDelayedItemsCount INT = 0; BEGIN DECLARE @sql NVARCHAR(4000), @cmd VARCHAR(4000); DECLARE cursor_pdppt CURSOR FOR SELECT pdppt.ID, pdppt.ProgramQuantity, pdppt.[Date] FROM Prg.ProduceDailyProgramProductTree pdppt WHERE pdppt.SalesOrderProductID = @SalesOrderProductID AND pdppt.MainProductTreeID = @MainProductTreeID AND pdppt.ProductTreeID = @ProductTreeID AND pdppt.Process = @ProcessId AND ISNULL(pdppt.IsDelayed, 0) = 0 OPEN cursor_pdppt; FETCH NEXT FROM cursor_pdppt INTO @ProduceDailyProgramProductTree_Id, @ProgramQuantity, @startDate; WHILE @@FETCH_STATUS = 0 BEGIN SET @lastDate = Gnr.RevercePersianDate([Prg].[intGetLastDateForDelayedProcessProgram](@startDate, @additionalDays, @CurrentDate, 1)); IF (@CurrentDate.GetDate() > @lastDate.GetDate()) BEGIN IF ((SELECT COUNT(*) FROM Prg.ProduceDailyOperation pdo WHERE pdo.ProduceDailyProgramProductTreeID = @ProduceDailyProgramProductTree_Id AND pdo.Process = @ProcessId AND pdo.[Date].GetDate() > @lastDate.GetDate()) = 0) BEGIN SET @allDelayedItemsCount = @allDelayedItemsCount + @ProgramQuantity; END ELSE BEGIN SET @allDelayedItemsCount = @allDelayedItemsCount + (@ProgramQuantity - (SELECT SUM(pdo.ProducedQuantity) FROM Prg.ProduceDailyOperation pdo WHERE pdo.ProduceDailyProgramProductTreeID = @ProduceDailyProgramProductTree_Id AND pdo.Process = @ProcessId AND pdo.[Date].GetDate() <= @lastDate.GetDate())); END; SELECT @sql = 'UPDATE [Prg].[ProduceDailyProgramProductTree] SET [IsDelayed] = 1 WHERE ID = ' + CONVERT(VARCHAR(10), @ProduceDailyProgramProductTree_Id); SELECT @cmd = 'sqlcmd -S ' + @@SERVERNAME + ' -d ' + DB_NAME() + ' -Q "' + @sql + '"' EXEC MASTER..XP_CMDSHELL @cmd , 'no_output' END; FETCH NEXT FROM cursor_pdppt INTO @ProduceDailyProgramProductTree_Id, @ProgramQuantity, @startDate; END; CLOSE cursor_pdppt; IF (@allDelayedItemsCount > 0) BEGIN IF (EXISTS(SELECT 1 FROM Prg.ProduceDailyProgramProductTreeDelayed pdppt WHERE pdppt.SalesOrderProductID = @SalesOrderProductID AND pdppt.MainProductTreeID = @MainProductTreeID AND pdppt.ProductTreeID = @ProductTreeID AND pdppt.Process = @ProcessId)) BEGIN SELECT @allDelayedItemsCount = @allDelayedItemsCount + (SELECT pdpptd.DelayedQuantity FROM Prg.ProduceDailyProgramProductTreeDelayed pdpptd WHERE pdpptd.SalesOrderProductID = @SalesOrderProductID AND pdpptd.MainProductTreeID = @MainProductTreeID AND pdpptd.ProductTreeID = @ProductTreeID AND pdpptd.Process = @ProcessId AND ISNULL(pdpptd.Active, 0) = 1 AND ISNULL(pdpptd.IsDeleted, 0) = 0); SELECT @sql = 'UPDATE [Prg].[ProduceDailyProgramProductTreeDelayed] SET DelayedQuantity = ' + CONVERT(VARCHAR(10), @allDelayedItemsCount) + 'WHERE SalesOrderProductID = ' + CONVERT(VARCHAR(10), @SalesOrderProductID) + 'AND MainProductTreeID = ' + CONVERT(VARCHAR(10), @MainProductTreeID) + 'AND ProductTreeID = ' + CONVERT(VARCHAR(10), @ProductTreeID) + 'AND Process = ' + CONVERT(VARCHAR(10), @ProcessId) +';'; END ELSE BEGIN SELECT @sql = 'INSERT INTO [Prg].[ProduceDailyProgramProductTreeDelayed] (SalesOrderProductID, MainProductTreeID, Process, ProductTreeID, DelayedQuantity, Active, IsDeleted) VALUES (' + CONVERT(VARCHAR(10), @SalesOrderProductID) + ', ' + CONVERT(VARCHAR(10), @MainProductTreeID) + ', ' + CONVERT(VARCHAR(10), @ProcessId) + ', ' + CONVERT(VARCHAR(10), @ProductTreeID) + ', ' + CONVERT(VARCHAR(10), @allDelayedItemsCount) + ', ''1'', ''0'')'; END; SELECT @cmd = 'sqlcmd -S ' + @@SERVERNAME + ' -d ' + DB_NAME() + ' -Q "' + @sql + '"' EXEC MASTER..XP_CMDSHELL @cmd , 'no_output' END ELSE IF(EXISTS(SELECT 1 FROM Prg.ProduceDailyProgramProductTreeDelayed pdpptd WHERE pdpptd.SalesOrderProductID = @SalesOrderProductID AND pdpptd.MainProductTreeID = @MainProductTreeID AND pdpptd.ProductTreeID = @ProductTreeID AND pdpptd.Process = @ProcessId AND ISNULL(pdpptd.Active, 0) = 1 AND ISNULL(pdpptd.IsDeleted, 0) = 0)) BEGIN SELECT @allDelayedItemsCount = (SELECT pdpptd.DelayedQuantity FROM Prg.ProduceDailyProgramProductTreeDelayed pdpptd WHERE pdpptd.SalesOrderProductID = @SalesOrderProductID AND pdpptd.MainProductTreeID = @MainProductTreeID AND pdpptd.ProductTreeID = @ProductTreeID AND pdpptd.Process = @ProcessId AND ISNULL(pdpptd.Active, 0) = 1 AND ISNULL(pdpptd.IsDeleted, 0) = 0); END; END; RETURN @allDelayedItemsCount; END
JalaliDate
是使用者定義的類型,由大會保存波斯日期時間。
Gnr.RevercePersianDate
將字元串波斯日期轉換為 JalaliDate 的函式。[Prg].[intGetLastDateForDelayedProcessProgram]
其他按我認為不會發布的假期計算指定日期的功能。更新 1
我通過內聯聲明和設置測試值傳遞參數值來測試功能程式碼。然後,使用者 Sql Server Profiler 檢查函式的腳本,看到腳本停止在
SET @CurrentDate = dbo.GetCurrentJalaliDate()
使用 Assembly 函式的這一行。執行執行沒有任何錯誤並且沒有響應!注意:我測試SELECT dbo.GetCurrentJalaliDate()
響應非常快,沒有錯誤!更新 2
我在一個不能呼叫的複雜語句中呼叫函式
[Prg].[intCheckDelayedProcessProgram]
,所以對SP
我來說,唯一的解決方案是定義函式。程式碼在與所有數據和其他 SP 和功能一起執行的本地伺服器中執行。如果有替代方法可以幫助我用它重寫功能。SELECT``SP``select``CURSOR
不要使用 using
xp_cmdshell
對 SQL Server 執行查詢,而是將程式碼更改為使用儲存過程、使用者定義的函式,甚至是呼叫 using的動態 T-SQLsp_executesql
。如果您需要針對不同的伺服器執行程式碼,請使用連結伺服器。最後,如果可能,請考慮重寫程式碼以不使用游標。SQL Server 中基於集合的操作要高效得多。
由於這些主題本身都是非常深入的主題,因此我無法在這裡充實答案,但我希望這些連結有所幫助。祝你好運。
您絕對應該找到比通過函式執行 DML 更好的方法。您不需要將此過程附加到
SELECT
語句中。您可以將查詢結果轉儲到臨時表並使用 CURSOR 對其進行迭代(就像您在此函式中所做的那樣),呼叫儲存過程來執行您目前在此函式中所做的事情。但是,至少,您應該向游標添加一些選項,以便它們不會鎖定基表:
DECLARE cursor_pdppt CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR ...
並且,在 之後
CLOSE cursor_pdppt;
,您應該立即添加:DEALLOCATE cursor_pdppt;