web-dev-qa-db-ja.com

データを削除しないクエリの削除

SQL Server 2012データベースに接続するWebサービスを実行しています。短時間(約5秒)で複数の削除クエリを実行すると、実際に実行されるクエリがランダムに表示されます。一度に1つずつ実行した場合、または各実行の間に0.5秒の遅延を置いた場合、それらは完全に実行されます。

SQLプロファイラーを見ると、すべてのクエリがRPC:Completed実際にテーブルの行を削除していない場合でも。テーブルを確認したところ、データはまだ残っていました。次に、プロファイラーからSSMSにクエリをコピーして貼り付け、1つの行に影響を与えて削除しました。

したがって、Webサービスは正常に動作しており、問題はデータベースのデータベース側にあると想定しています。クエリが成功したかどうかをプロファイラーに表示する方法はありますか?そして、これが実際に行に影響を与えない原因は何ですか?

トリガーはありません。異なるパラメーターを使用して同じクエリを実行する。通常、データのみが順次変化しています。現在、データベースで他のクエリは実行されていません。

クライアントサイトのログを追加しました。行を削除すると実際に1を返し、行を削除しないと0を返します。ただし、0として表示された場合でも、プロファイラーはクエリを実行したことを示しますが、影響はなかったようです。また、SSMSを介してクエリを実行すると、行に影響します。

Webサービスからエラーを受信せず、クエリはSSMSを介して正常に実行されます。すばやく連続して複数回実行された場合にのみ削除されないようです。私はそれが別の行をターゲットにしている可能性が最も高いことに同意しますが、SSMSで正常に実行されたときにそれがどのように発生するかはわかりません。

テーブルの構造とクエリ

CREATE TABLE dbo.ContractDates2HumanAssets
(
    iContractDate2HumanAssetID int IDENTITY(1,1) NOT NULL,
    iContractDateID int NOT NULL,
    iHumanAssetID int NOT NULL,
    cCategory nvarchar(256) NOT NULL,
    cHR_x0020_ID nvarchar(256) NOT NULL,
    cCD_x0020_ID nvarchar(256) NOT NULL,
     CONSTRAINT PK_ContractDates2HumanAssets PRIMARY KEY CLUSTERED 
    (
        iContractDate2HumanAssetID ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
GO

CREATE TABLE dbo.ContractDates
(
    iContractDateID int IDENTITY(1,1) NOT NULL,
    iContractID int NOT NULL,
    dDate datetime NOT NULL,
    cResource nvarchar(256) NOT NULL,
    cCD_x0020_ID nvarchar(256) NOT NULL,
    CONSTRAINT PK_ContractDates PRIMARY KEY CLUSTERED 
    (
        iContractDateID ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];

exec sp_executesql N'DELETE FROM ContractDates2HumanAssets 
WHERE iContractDateID IN ((SELECT ContractDates.iContractDateID 
FROM ContractDates2HumanAssets 
INNER JOIN ContractDates ON ContractDates.iContractDateID = ContractDates2HumanAssets.iContractDateID
WHERE iHumanAssetID = @humanid AND iContractID = (SELECT iContractID FROM Contracts WHERE cContractNo = @id) AND dDate = @newDate))',N'@id nvarchar(6),@newDate nvarchar(10),@humanid int',@id=N'999111',@newDate=N'2018-03-16',@humanid=82

それがプロファイラーに表示されるクエリです。テーブルをチェックした後、その行はまだそこにあります。しかし、それをSSMSにコピーして貼り付けて実行すると、1行が影響を受けて削除されます。

テーブルデータ

SELECT * 
FROM ContractDates2HumanAssets 
WHERE iContractDateID IN
(
    (
        SELECT ContractDates.iContractDateID 
        FROM ContractDates2HumanAssets 
        INNER JOIN ContractDates 
            ON ContractDates.iContractDateID = ContractDates2HumanAssets.iContractDateID 
        WHERE 
            iHumanAssetID = 82 
            AND iContractID = 
            (
                SELECT iContractID 
                FROM Contracts 
                WHERE cContractNo = N'999111'
            )
    )
);

結果:

╔════════════════════════════╦═════════════════╦══ ═════════════╦═══════════╦══════════════╦═════════ ═════╗
║iContractDate2HumanAssetID║iContractDateID║iHumanAssetID║cCategory║cHR_x0020_ID║cCD_x0020_ID║
╠════════════════════ ════════╬═════════════════╬═══════════════╬═══════ ════╬══════════════╬══════════════╣
║102538║113369║82║║║║ 
║102539║113370║82║║ ║║
║102540║113371║82║║║║
║102541║113372║82║║║║
║102542║113373║82║║║║
║102543║113374║82║║║║
║102544║113375║82║║║║
║102545║113376║82║║║║
║102546║ 113377║82║║║║
║102547║113378║82 ║║║。
║102548║113379║82║║║║
║102549║113380║82║║║║
║102550║113381║82║║║
║102551║113382║82║║║║
║102552║113383║82║║║║
║102553║113384║82║║║║
╚ ════════════════════════════╩═════════════════╩═══ ══════ ═════╩═══════════╩══════════════╩══════════════╝

すべての関連データを取得するためにそのクエリを実行しました。それがより役立つ場合は、テーブルごとに個別のデータを取得できます。

3
Tom Dee

SQL Serverneverは、行を削除するかどうかをランダムに選択します。有効なDELETEステートメントをSQL Serverに送信すると、それは実行します。保証されています。限目。ステートメントにエラーがある場合、SQL Serverはエラーを返します。エラーが発生していますか?

はるかに可能性の高い問題は、クエリ自体が意図した行をターゲットにしていないか、それらの行が存在しないことです。実際の削除ステートメントとともにテーブル構造を表示します。

下記のロバート・ロドリゲスの回答 は、回答が投稿されたときに反対票を投じましたが、問題の原因である可能性がある暗黙のトランザクションの影響について説明しています。 SQL Serverは最も確実にdoes行を削除し、evenwhen SET IMPLICIT_TRANSACTIONSONです。これを証明するには、SSMSのクエリウィンドウでテーブルを作成および設定する次のステートメントを実行します。

USE tempdb;

DROP TABLE IF EXISTS dbo.t;
CREATE TABLE dbo.t
(
    i int NOT NULL
        CONSTRAINT t_pk
        PRIMARY KEY
        CLUSTERED
);
GO

INSERT INTO dbo.t (i)
VALUES (1);

SELECT *
FROM dbo.t;
GO

上記のコードを実行すると、次の出力が表示されます。

╔═══╗
║i║
╠═══╣
║1║
╚═══╝

次に、2番目のSSMSクエリウィンドウで、次のコードを実行します。

USE tempdb;
/*********************************
* TURN ON IMPLICIT TRANSACTIONS  *
*********************************/
SET IMPLICIT_TRANSACTIONS ON;

SELECT [State] = 'PRE DELETE'
    , [@@TRANCOUNT] = @@TRANCOUNT
    , [IMPLICIT_TRANSACTIONS] = CASE WHEN @@OPTIONS & 2 = 2 THEN 'ON' ELSE 'OFF' END
    , [Count of Rows in dbo.t] = (SELECT COUNT(1) FROM dbo.t)

DELETE 
FROM dbo.t
WHERE dbo.t.i = 1;

SELECT [State] = 'POST DELETE'
    , [@@TRANCOUNT] = @@TRANCOUNT
    , [IMPLICIT_TRANSACTIONS] = CASE WHEN @@OPTIONS & 2 = 2 THEN 'ON' ELSE 'OFF' END
    , [Count of Rows in dbo.t] = (SELECT COUNT(1) FROM dbo.t)
GO

--DISCONNECT the session after you run the above code

上記のコードを実行すると、2つの結果セットが表示されます。

╔════════════╦═════════════╦══════════════════════ ═╦════════════════════════╗
║状態║@@ TRANCOUNT║IMPLICIT_TRANSACTIONS d dbo.tの行数║
╠════════════╬═════════════╬══════════════════ ═════╬════════════════════════╣
║削除前║1║オン║1║
╚════════════╩═════════════╩═════════════════════ ══╩════════════════════════╝
 
╔═══════════ ══╦═════════════ ═══════════════════════╦════════════════════════╗
║状態║@@ TRANCOUNT║IMPLICIT_TRANSACTIONS║dbo.tの行数║
╠═════════════╬═════════ ════╬═══════════════════════╬═════════════════════ ═══╣
║POST DELETE║1║ON║0║
╚═════════════╩══ ═══════════╩═══════════════════════╩══════════════ ══════════╝

上記の出力では、最初の結果セットが、行がdbo.tに存在することを示しています。 2番目の結果セットの前に、DELETE FROM dbo.tステートメントが実行されます。 2番目の結果セットでは、SQL Serverが実際に行を削除したことがわかります。 ROLLBACK TRANSACTIONと組み合わせた明示的なCOMMIT TRANSACTIONステートメントがないため、上記のセッションを切断してください。これにより、自動SET IMPLICIT_TRANSACTIONS ON;が開始されます。

次に、SSMSで3番目のクエリウィンドウを開き、次のコードを実行します。

SELECT [State] = 'POST ROLLBACK'
    , [@@TRANCOUNT] = @@TRANCOUNT
    , [IMPLICIT_TRANSACTIONS] = CASE WHEN @@OPTIONS & 2 = 2 THEN 'ON' ELSE 'OFF' END
    , [Count of Rows in dbo.t] = (SELECT COUNT(1) FROM dbo.t);

次の出力は、行がdbo.tにまだ存在していることを示しています。

╔═══════════════╦═════════════╦═══════════════════ ════╦════════════════════════╗
║状態║@@ TRANCOUNT║IMPLICIT_TRANSACTIONS d dboの行数。 t║
╠═══════════════╬═════════════╬════════════ ═══════════╬════════════════════════╣
║POSTロールバック║0║オフ║1║
╚═══════════════╩═════════════╩═══ ════════════════════╩════════════════════════╝

もちろん、暗黙的なトランザクションがロールバックされるため、ROLLBACK TRANSACTIONの直後にDELETE FROMを発行した場合とまったく同じように、行はテーブルに存在します。

行が実際にDELETE FROM dbo.tステートメントによって削除されたとまだ思わない場合は、問題のテーブルのDBCC PAGEからの出力を2番目のコードセットに追加して、再実行します。以下のコードは、DBCC PAGEを使用して行の詳細を表示する に関する私のブログ投稿から取得したものです

USE tempdb;
SET IMPLICIT_TRANSACTIONS ON;

SELECT [State] = 'PRE DELETE'
    , [@@TRANCOUNT] = @@TRANCOUNT
    , [IMPLICIT_TRANSACTIONS] = CASE WHEN @@OPTIONS & 2 = 2 THEN 'ON' ELSE 'OFF' END
    , [Count of Rows in dbo.t] = (SELECT COUNT(1) FROM dbo.t)


DBCC TRACEON(3604) WITH NO_INFOMSGS;
DECLARE @dbid int = DB_ID();
DECLARE @fileid int;
DECLARE @pageid int;
DECLARE cur CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR
SELECT dpa.allocated_page_file_id
    , dpa.allocated_page_page_id
FROM sys.schemas s  
    INNER JOIN sys.objects o ON o.schema_id = s.schema_id
CROSS APPLY sys.dm_db_database_page_allocations(DB_ID(), o.object_id, NULL, NULL, 'DETAILED') dpa
WHERE o.name = N't'
    AND s.name = N'dbo'
    AND dpa.page_type_desc = N'DATA_PAGE';
OPEN cur;
FETCH NEXT FROM cur INTO @fileid, @pageid;
WHILE @@FETCH_STATUS = 0
BEGIN
    DBCC PAGE (@dbid, @fileid, @pageid, 3);
    FETCH NEXT FROM cur INTO @fileid, @pageid;
END
CLOSE cur;
DEALLOCATE cur;
DBCC TRACEOFF(3604);

DELETE 
FROM dbo.t
WHERE dbo.t.i = 1;

DBCC TRACEON(3604) WITH NO_INFOMSGS;
DECLARE cur CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR
SELECT dpa.allocated_page_file_id
    , dpa.allocated_page_page_id
FROM sys.schemas s  
    INNER JOIN sys.objects o ON o.schema_id = s.schema_id
CROSS APPLY sys.dm_db_database_page_allocations(DB_ID(), o.object_id, NULL, NULL, 'DETAILED') dpa
WHERE o.name = N't'
    AND s.name = N'dbo'
    AND dpa.page_type_desc = N'DATA_PAGE';
OPEN cur;
FETCH NEXT FROM cur INTO @fileid, @pageid;
WHILE @@FETCH_STATUS = 0
BEGIN
    DBCC PAGE (@dbid, @fileid, @pageid, 3);
    FETCH NEXT FROM cur INTO @fileid, @pageid;
END
CLOSE cur;
DEALLOCATE cur;
DBCC TRACEOFF(3604);

SELECT [State] = 'POST DELETE'
    , [@@TRANCOUNT] = @@TRANCOUNT
    , [IMPLICIT_TRANSACTIONS] = CASE WHEN @@OPTIONS & 2 = 2 THEN 'ON' ELSE 'OFF' END
    , [Count of Rows in dbo.t] = (SELECT COUNT(1) FROM dbo.t)
GO

結果セットは上記の2番目のセットとまったく同じに見えますが、[メッセージ]タブを見ると、削除の前後にDBCC PAGEを実行した結果の出力が表示されます。削除前の最初のDBCC PAGEからの出力と、ヘッダーセクションが削除された出力は、次のようになります。

。
。
。
スロット0オフセット0x6b長さ11 
 
レコードタイプ= PRIMARY_RECORDレコード属性= NULL_BITMAPレコードサイズ= 11 
 
メモリダンプ@ 0x000000B76B27A06B 
 
 0000000000000000:10000800 01000000 010000 ........... 
 
スロット0列1オフセット0x4長さ4長さ(物理)4 
 
 i = 1 
 
スロット0オフセット0x0長さ0長さ(物理)0 
 
 KeyHashValue =(8194443284a0) 

上記の出力を見ると、Record TypePRIMARY_RECORDであり、スロット0には値1が含まれています。これは、行がテーブルに存在し、期待される値が含まれていることを示しています。

同じヘッダーセクションが削除された、DBCC PAGEからの出力(削除後)は次のようになります。

スロット0オフセット0x6b長さ11 
 
レコードタイプ= GHOST_DATA_RECORDレコード属性= NULL_BITMAPレコードサイズ= 11 
 
メモリダンプ@ 0x000000B76B27A06B 
 
 0000000000000000:1c000800 01000000 010000 ........... 
 
スロット0列1オフセット0x4長さ4長さ(物理)4 
 
 i = 1 
 
スロット0オフセット0x0長さ0長さ(物理)0 
 
 KeyHashValue =(8194443284a0)

出力は、スロットRecord Type0GHOST_RECORDであることを明確に示しており、スロット(行)が削除されたことを示しています

ドキュメントに記載されていないシステム関数sys.fn_dblogを介して、DBCC PAGE出力のm_lsn値を使用してトランザクションログを確認することもできます。

SELECT *
FROM sys.fn_dblog('102:281659:60', '102:281659:60');

出力は、削除がトランザクションログに記録されたことを示しています。

╔════════════════════════╦═════════════════╦══════ ═════════════╦════════════════╗
║現在のLSN║操作║コンテキスト║トランザクションID║
╠════════════════════════╬═════════════════╬════ ═══════════════╬════════════════╣
║00000066:00044c3b:003c║LOP_DELETE_ROWS║LCX_MARK_AS_GHOST║0000 :00160373║
╚════════════════════════╩════════════════ ═╩═══════════════════╩════════════════╝

簡潔にするために、ここでは出力の最初の数列しか示していませんが、LOP_DELETE_ROWSのコンテキストでLCX_MARK_AS_GHOSTオペレーションを明確に見ることができます。これは、SQL ServerがDELETEオペレーションを記録したことを示しますトランザクションログ。

12
Max Vernon

Webサービスコードがオンになっている可能性があります 暗黙のトランザクション

これがどのように機能するかの例として、tempdbに単純なテーブルを作成し、行を挿入し、暗黙のトランザクションをオンにし、行を削除しました。

USE tempdb;

CREATE TABLE dbo.t
(
    someval int
);

INSERT INTO dbo.t (someval)
VALUES (1);

SET IMPLICIT_TRANSACTIONS ON;
DELETE FROM dbo.t;

セッションを切断して再接続する場合は、次のコードを実行します。

USE tempdb;
SELECT *
FROM dbo.t

行がまだテーブルにあることがわかります。これは、あなたが見ているものの説明にかなり適合しています。これについては、Webサービスコードを確認するか、トレースを見て、このオプションが有効かどうかを確認する必要があります。

興味がある場合は、 thisNot Connectitem に賛成して、暗黙的であるかどうかを判断する簡単な方法を提供するように依頼できますトランザクションは有効です。

8

デッドロックが発生しているかどうかを確認しましたか?

私のバックグラウンドはSybaseですが、SQL Serverもかなり似ています。クライアントメッセージハンドラー(これはWebサービスにあります)は、デッドロックのメッセージ#1205を取得します。さらに、デッドロックはSQL Serverログに記録する必要があります。

デッドロックを引き起こすには、2つのセッションが2つのロックをめぐって争っている必要があります。接続のプールを使用するWebサービスが表示され、複数のセッションが発生しました。 2つのロックをめぐって争うことは、かなり簡単に達成できます。 1つのロックはデータページにあり、別のロックはインデックスページにあります。

4
Jay Anderson