約15の異なるテーブルから取得するクエリがあります。これをxml/jsonに格納するテーブルに具体化したいと考えています。 (パフォーマンスを向上させるため。)
私が抱えている問題は、これらのテーブルがいくつかのプロセスによって更新されることです。可能であれば、これをSQL Serverで維持する方法を探しています。
理想的には、トランザクションがコミットする直前に起動するトリガーがSQL Serverにあり、影響を受けるテーブルとレコードを確認して、「結果」テーブルを更新する必要があるかどうかを知ることができれば、私はそれを気に入っています。
SQL Serverにそのようなものはありますか?
注:INSTEAD OF
トリガーの使用を検討しましたが、トランザクション内のテーブルの順序を知る方法がないため、トランザクションが15個すべてのテーブルを更新する場合、「結果」テーブルを更新します15同じ行の時間。
男、すみません。
私はあなたが考案したような簡単な解決策はないと言って答えています。
あなたは私たちに非常に興味深い挑戦をしました。
SQL Serverエンジンだけに依存するコミットされたトランザクションの余波を収集する方法がないことを確認するために、過去4時間に研究と演習を行ってきました。
ソリューションアーキテクチャの全体像を見せていただければ、最小の労力でソリューションを考案します。
たとえば、シナリオでは:
これらのテーブルのデータを変更する可能性がある場合は、次のようにします。
そして、遮断点について、私は以下を行います:
以下に紹介します:
後処理するレコードを登録するには、次のような単一のテーブルを設計します。
IF EXISTS(SELECT * FROM sys.tables WHERE name = 'TBL_2PROC') DROP TABLE TBL_2PROC
go
CREATE TABLE TBL_2PROC(
session_id INT, -- the session ID - it's expected each app transaction (or session) to have
-- its own, exclusive, not shared, connection, even if pooled
transaction_id BIGINT, -- to assure grouping -> (session_id, transaction_id)
event VARCHAR(255), -- string stating an INSERT, UPDATE or DELETE
tblName VARCHAR(255), -- name of the table affected
keyId VARCHAR(255) -- value of the (single) id column, converted to varchar
)
go
実際のトリガーを作成する前に、古いトリガーが残っていないことを確認する必要があります。
-- Clean-up existing ones ------------------------------------------------------
--------------------------------------------------------------------------------
DECLARE cCursor CURSOR LOCAL FAST_FORWARD FOR
SELECT name
FROM sys.triggers
WHERE name LIKE 'TR_2PROC%'
--
DECLARE @trName VARCHAR(255)
--
DECLARE @sql NVARCHAR(MAX)
OPEN cCursor
FETCH cCursor INTO @trName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @sql = 'DROP TRIGGER [' + @trName + ']'
EXECUTE sp_executesql @sql
FETCH cCursor INTO @trName
END
go
プログラムで必要なトリガーを作成する前に、どのトリガーになるかを宣言する必要があります。
-- Parameterize the target tables and theirs key columns -----------------------
--------------------------------------------------------------------------------
CREATE TABLE #to_log(tblName VARCHAR(255), keyName VARCHAR(255))
go
INSERT INTO #to_log VALUES('SomeTable', 'Id')
INSERT INTO #to_log VALUES('SomeOtherTable', 'OtherId')
go
ターゲットが設定されているため、このスクリプトはトリガーを作成します。
-- Setup triggers --------------------------------------------------------------
--------------------------------------------------------------------------------
DECLARE @tblName VARCHAR(255)
DECLARE @keyName VARCHAR(255)
--
DECLARE @sql NVARCHAR(MAX)
--
DECLARE cCursor CURSOR LOCAL FAST_FORWARD FOR
SELECT tblName, keyName
FROM #to_log
OPEN cCursor
FETCH cCursor INTO @tblName, @keyName
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @triggerName VARCHAR(255)
SET @triggerName = '[TR_2PROC_' + @tblName + ']'
-- Drop if exists ----------------------------------------------------------
SET @sql = N'
IF EXISTS(SELECT * FROM sys.triggers WHERE name = ''' + @triggerName + ''')
DROP TRIGGER ' + @triggerName
EXECUTE sp_executesql @sql
-- Create ------------------------------------------------------------------
SET @sql = N'
CREATE TRIGGER ' + @triggerName + '
ON [' + @tblName + ']
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
DECLARE @session_id INT
DECLARE @transaction_id BIGINT
SELECT
@session_id = session_id,
@transaction_id = transaction_id
FROM sys.dm_tran_session_transactions
WHERE session_id = @@spid
INSERT INTO TBL_2PROC
SELECT
@session_id, @transaction_id,
CASE
WHEN del.[' + @keyName + '] IS NULL THEN ''INSERT''
ELSE ''UPDATE''
END,
''' + @tblName + ''',
CONVERT(VARCHAR(255), ins.[' + @keyName + '])
FROM
inserted ins
LEFT OUTER JOIN deleted del
ON del.[' + @keyName + '] = ins.[' + @keyName + ']
INSERT INTO TBL_2PROC
SELECT
@session_id, @transaction_id,
''DELETE'',
''' + @tblName + ''',
CONVERT(VARCHAR(255), del.[' + @keyName + '])
FROM deleted del
WHERE
del.[' + @keyName + '] NOT IN(
SELECT ins.[' + @keyName + ']
FROM inserted ins
)
END'
EXECUTE sp_executesql @sql
FETCH cCursor INTO @tblName, @keyName
END
GO
いくつかのクリーンアップが続きます:
-- Clean-up --------------------------------------------------------------------
--------------------------------------------------------------------------------
DROP TABLE #to_log
go
今、本当の取引。
最後になりましたが、少なくとも、処理手順を作成する仕事が残ります。
トリガーによってCOMMIT
をインターセプトできたとしても、処理される結果の出力はこれと非常に似ています。
IF EXISTS(SELECT * FROM sys.procedures WHERE name = 'SP_2PROC') DROP PROCEDURE SP_2PROC
GO
CREATE PROCEDURE SP_2PROC AS
BEGIN
-- Important!
--
-- Use WITH(NOLOCK) to avoid dead-locks
--
-- Do NOT use SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED here,
-- unless you know what you are doing
-- (this isolation level is often safe in other contexts)
-- You, now, shall build your process up from here, based on the
-- query below
SELECT *
FROM
TBL_2PROC WITH(NOLOCK)
WHERE session_id = @@SPID
END
GO