2日前、INSERT
とUPDATE
を必要とする関数を作成し、_XP_CMDShell
_を有効にして、スクリプトを実行しました。
その後、SQL ServerはSELECT
コマンドに対して非常に遅く動作します。分離されたコマンドを介してSELECT
の後に実行される非常に単純なINSERT
ステートメントでも。
他のデータベースでこの動作をテストしたところ、数分後にSELECT
が実行されるのと同じ結果が得られました。
また、SQL Server 2014を搭載した他の2つのホストでテストしたところ、結果は同じでした。
別の選択ステートメント内でその値を取得するために作成した関数:
_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
は、アセンブリによってペルシャ日付時刻を保持するユーザー定義型です。
文字列のペルシャ日付をJalaliDateに変換する_Gnr.RevercePersianDate
_関数。 _[Prg].[intGetLastDateForDelayedProcessProgram]
_休日で指定された日付を計算する他の関数は発行しないと思います。
更新1
インライン宣言によってパラメーター値を渡すことで機能コードをテストし、テスト値を宣言して設定します。次に、Sql Server Profilerを使用して関数のスクリプトを確認します。アセンブリの関数を使用するこの行SET @CurrentDate = dbo.GetCurrentJalaliDate()
でスクリプトが停止するのを確認します。実行はエラーなしで実行され、応答しません!!注:エラーなしで非常に高速に応答するSELECT dbo.GetCurrentJalaliDate()
をテストします!
更新2
関数_[Prg].[intCheckDelayedProcessProgram]
_をSP
内で、複雑なSELECT
ステートメント内で呼び出します。SP
をselect
で呼び出すことはできません。私は機能を定義しました。すべてのデータおよび他のSPおよび関数と一緒にあるローカルサーバーで実行されるコード。CURSOR
の代替手段がある場合は、それを使用して関数を書き直すのを手伝ってください。
xp_cmdshell
を使用してSQL Serverに対してクエリを実行する代わりに、コードを変更して ストアドプロシージャ 、 ユーザー定義関数 、または 動的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;