OLTPメモリ内テーブルのマージコマンドをシミュレートするストアドプロシージャを作成しました。シミュレートされたマージを使用して、大量のデータを通常のテーブルからOLTP 1つ。
これはコードです:
CREATE PROCEDURE [cache].[MoveInverterData] (@sourecInverterID bigint, @from datetime2(7))
AS
BEGIN
SET NOCOUNT ON;
DECLARE @i INT = 1;
DECLARE @InverterID bigint, @Timestamp datetime2(7), @Status nvarchar(50);
DECLARE Employee_Cursor CURSOR READ_ONLY FOR
SELECT [InverterID],[Timestamp],[Status]
FROM [data].[InverterData]
WHERE [InverterID] = @sourecInverterID AND [Timestamp] >= @from;
OPEN Employee_Cursor;
FETCH NEXT FROM Employee_Cursor;
WHILE @@FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM Employee_Cursor INTO @InverterID, @Timestamp, @Status;
UPDATE [cache].[InverterData]
SET [Status] = @Status
WHERE [InverterID] = @InverterID AND [Timestamp] = @Timestamp;
-- if there was no row to update, insert
IF @@ROWCOUNT=0
INSERT INTO [cache].[InverterData]
([InverterID],[Timestamp],[Status])
VALUES
(@InverterID, @Timestamp, @Status);
END
CLOSE Employee_Cursor;
DEALLOCATE Employee_Cursor;
RETURN(0)
END
OLTPテーブル(ターゲット) サポートされていませんMERGE
コマンドであるため、カーソルはこのような行の範囲をアップサートします。
SPを実行すると、最初に選択したカーソルFETCH
の結果と、単一行数のリストが表示されます。
これは正常な動作であることは理解していますが、より簡単な結果を得るためにこれを抑制しようとしています。
私が欲しいもの:
CURSOR
selectからのテーブル結果はありません空のFETCH
は確かにエラーであり、ROW
の結果の問題を解決します(ポイント1)。
今このように見えます:
CREATE PROCEDURE [cache].[MoveInverterData] (@sourecInverterID bigint, @from datetime2(7))
AS
BEGIN
SET NOCOUNT ON;
DECLARE @i INT = 1;
DECLARE @InverterID bigint, @Timestamp datetime2(7), @Status nvarchar(50);
DECLARE Employee_Cursor CURSOR READ_ONLY FOR
SELECT [InverterID],[Timestamp],[Status]
FROM [data].[InverterData]
WHERE [InverterID] = @sourecInverterID AND [Timestamp] >= @from;
OPEN Employee_Cursor;
FETCH NEXT FROM Employee_Cursor INTO @InverterID, @Timestamp, @Status;
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE [cache].[InverterData]
SET [Status] = @Status
WHERE [InverterID] = @InverterID AND [Timestamp] = @Timestamp;
-- if there was no row to update, insert
IF @@ROWCOUNT=0
INSERT INTO [cache].[InverterData]
([InverterID],[Timestamp],[Status])
VALUES
(@InverterID, @Timestamp, @Status);
FETCH NEXT FROM Employee_Cursor INTO @InverterID, @Timestamp, @Status;
END
CLOSE Employee_Cursor;
DEALLOCATE Employee_Cursor;
RETURN(0)
END
カーソル*を使用する代わりに、更新および挿入操作をセットベースの操作として明示的に記述します。
CREATE OR ALTER PROCEDURE cache.MoveInverterData
(
@sourceInverterID bigint,
@from datetime2(7)
)
AS
BEGIN
SET XACT_ABORT, NOCOUNT ON;
DECLARE @RowsAffected integer = 0;
-- TODO: Add error handling
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
-- Update existing rows
UPDATE CID
SET [Status] = DID.[Status]
FROM [data].InverterData AS DID
JOIN cache.InverterData AS CID
WITH (SNAPSHOT)
ON CID.InverterID = DID.InverterID
AND CID.[Timestamp] = DID.[Timestamp]
WHERE
DID.InverterID = @sourceInverterID
AND DID.[Timestamp] >= @from;
SET @RowsAffected += @@ROWCOUNT;
-- Insert new rows
INSERT cache.InverterData
(InverterID, [Timestamp], [Status])
SELECT
DID.InverterID,
DID.[Timestamp],
DID.[Status]
FROM [data].InverterData AS DID
WHERE
DID.InverterID = @sourceInverterID
AND DID.[Timestamp] >= @from
AND NOT EXISTS
(
SELECT 1
FROM cache.InverterData AS CID
WITH (SNAPSHOT)
WHERE
CID.InverterID = DID.InverterID
AND CID.[Timestamp] = DID.[Timestamp]
);
SET @RowsAffected += @@ROWCOUNT;
COMMIT TRANSACTION;
-- TODO: Use the @RowsAffected result
END;
両方のテーブルのInverterID, [Timestamp]
に(ハッシュ以外の)インデックスがあることを確認してください。
*他のデータベースエンジンとは異なり、SQLServerでは通常カーソルは非常に非効率的です。オーバーヘッド操作と行ごとの操作により、ほとんどの場合、セットベースのソリューションがより高速かつ効率的になります。
または、ネイティブにコンパイルされたストアドプロシージャを可能な限り使用する場合は、メモリに最適化されたテーブルタイプを作成して、新しいデータを保持します。
CREATE TYPE dbo.InverterData AS TABLE
(
RowID integer IDENTITY (1, 1) NOT NULL,
InverterID bigint NOT NULL,
[Timestamp] datetime2(7) NOT NULL,
[Status] nvarchar(50) NOT NULL,
PRIMARY KEY NONCLUSTERED HASH (RowID)
WITH (BUCKET_COUNT = 1024)
)
WITH (MEMORY_OPTIMIZED = ON);
次に、ネイティブにコンパイルされたプロシージャを記述して、新しいデータをマージします。
CREATE PROCEDURE cache.MoveInverterData_Native
@InverterData dbo.InverterData READONLY
WITH NATIVE_COMPILATION, SCHEMABINDING
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english')
DECLARE @RowID integer = 1;
DECLARE @InverterID bigint, @Timestamp datetime2(7), @Status nvarchar(50);
WHILE @RowID > 0
BEGIN
SELECT
@InverterID = ID.InverterID,
@Timestamp = ID.[Timestamp],
@Status = ID.[Status]
FROM @InverterData AS ID
WHERE ID.RowID = @RowID;
IF @@ROWCOUNT = 0
BEGIN
SET @RowID = 0
END
ELSE
BEGIN
UPDATE cache.InverterData
SET [Status] = @Status
WHERE InverterID = @InverterID
AND [Timestamp] = @Timestamp;
IF @@ROWCOUNT = 0
INSERT cache.InverterData
(InverterID, [Timestamp], [Status])
VALUES
(@InverterID, @Timestamp, @Status);
SET @RowID += 1;
END;
END;
END;
そして、ソーステーブルからメモリ最適化テーブルタイプを設定するラッパープロシージャ:
CREATE OR ALTER PROCEDURE cache.MoveInverterData
(
@sourceInverterID bigint,
@from datetime2(7)
)
AS
BEGIN
SET XACT_ABORT, NOCOUNT ON;
DECLARE @InverterData dbo.InverterData;
INSERT @InverterData
(InverterID, [Timestamp], [Status])
SELECT
ID.InverterID,
ID.[Timestamp],
ID.[Status]
FROM [data].InverterData AS ID
WHERE
ID.InverterID = @sourceInverterID
AND ID.[Timestamp] >= @from;
EXECUTE cache.MoveInverterData_Native @InverterData;
END;
次に、プロセス全体が次のような1回の呼び出しで呼び出されます。
EXECUTE cache.MoveInverterData
@sourceInverterID = 1,
@from = '20180601';