删除查询不删除数据

Tom*_*Dee 3 sql-server delete profiler

我正在运行连接到 SQL Server 2012 数据库的 Web 服务。在短时间内(大约 5 秒)内执行多个删除查询时,实际执行的查询似乎是随机的。当一次运行一个或在每次执行之间放置 0.5 秒的延迟时,它们运行完美。

查看 SQL 分析器,所有查询都显示为RPC:Completed即使它们实际上并未删除表中的行。我检查了表,数据仍然存在,然后将查询从 Profiler 复制并粘贴到 SSMS 中,这影响了一行并将其删除。

所以我假设 Web 服务运行良好,问题出在它的数据库端。探查器中有没有办法查看查询是否成功?什么可能导致这实际上不影响行?

没有触发器。仅使用不同的参数运行相同的查询。只有数据通常是按顺序变化的。刚才没有其他查询在数据库上运行。

我添加了客户端站点日志记录,如果它删除该行,它实际上会返回 1,如果它不删除该行,它会返回 0。然而,即使它出现为 0,Profiler 也会显示它已经运行了查询,但似乎没有影响它。当我通过 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
Run Code Online (Sandbox Code Playgroud)

这是出现在分析器中的查询。检查表后,该行仍然存在。但是将其复制并粘贴到 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'
            )
    )
);
Run Code Online (Sandbox Code Playgroud)

结果:

?????????????????????????????????????????????????????? ?????????????????????????????????????????????????????? ??????
? iContractDate2HumanAssetID ? iContractDateID ? iHumanAssetID ? c 类别 ? 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 ? ? ? ?
?????????????????????????????????????????????????????? ?????????????????????????????????????????????????????? ??????

我运行该查询以获取所有相关数据,如果更有帮助,我可以获取每个表的单个数据。

Han*_*non 12

SQL Server从不随机选择删除或不删除行。当您向DELETESQL Server发送有效语句时,它会执行它。保证。时期。如果语句中有错误,SQL Server 将返回错误。你看到错误了吗?

更有可能的问题是查询本身没有针对您认为的行,或者这些行不存在。显示您的表结构以及实际的删除语句。

罗伯特·罗德里格斯 (Robert Rodriguez)在下面的回答中谈到了隐式交易的影响,这很可能是导致您出现问题的原因,我在回答发布时对此进行了投票。SQL Server中的大多数肯定不会删除行,甚至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
Run Code Online (Sandbox Code Playgroud)

运行上面的代码,你会看到这样的输出:

?????
? 一世 ?
?????
? 1 ?
?????

现在,在第二个 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
Run Code Online (Sandbox Code Playgroud)

运行上面的代码,你会看到两组结果:

?????????????????????????????????????????????????????? ??????????????????????????????
? 状态 ?@@TRANCOUNT ?IMPLICIT_TRANSACTIONS ? dbo.t 中的行数?
?????????????????????????????????????????????????????? ??????????????????????????????
? 预删除?1 ? 在 ?1 ?
?????????????????????????????????????????????????????? ??????????????????????????????

?????????????????????????????????????????????????????? ?????????????????????????????????
? 状态 ?@@TRANCOUNT ?IMPLICIT_TRANSACTIONS ? dbo.t 中的行数?
?????????????????????????????????????????????????????? ?????????????????????????????????
? 发布删除?1 ? 在 ?0 ?
?????????????????????????????????????????????????????? ?????????????????????????????????

在上面的输出中,您可以看到第一个结果集表明该行确实存在于dbo.t. 在第二个结果集之前,DELETE FROM dbo.t语句执行。在第二个结果集中,您可以看到 SQL Server 实际上删除了该行。确保您现在断开上面的会话,ROLLBACK TRANSACTION由于缺少显式COMMIT TRANSACTION语句,它将与SET IMPLICIT_TRANSACTIONS ON;.

现在在 SSMS 中打开第三个查询窗口,并运行以下代码:

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);
Run Code Online (Sandbox Code Playgroud)

您将看到以下输出,表明该行仍然存在于dbo.t.

?????????????????????????????????????????????????????? ?????????????????????????????????
? 状态 ?@@TRANCOUNT ?IMPLICIT_TRANSACTIONS ? dbo.t 中的行数?
?????????????????????????????????????????????????????? ?????????????????????????????????
? 后回滚?0 ? 离开 ?1 ?
?????????????????????????????????????????????????????? ?????????????????????????????????

当然,由于隐式事务被回滚,该行存在于表中,就像您ROLLBACK TRANSACTION直接在DELETE FROM.

假设您仍然不相信该行实际上已被DELETE FROM dbo.t语句删除,让我们将有DBCC PAGE问题的表的输出添加到第二组代码中,然后重新运行它。下面的代码取自我关于使用 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
Run Code Online (Sandbox Code Playgroud)

结果集看起来与上面的第二组完全相同,但是如果我们查看“消息”选项卡,我们将看到删除前后运行 DBCC PAGE 的输出。删除前第一个 DBCC PAGE 的输出,并删除了标题部分,如下所示:

.
.
.
插槽 0 偏移 0x6b 长度 11

记录类型 = PRIMARY_RECORD 记录属性 = NULL_BITMAP 记录大小 = 11

内存转储@0x000000B76B27A06B

0000000000000000: 10000800 01000000 010000 .....

插槽 0 列 1 偏移量 0x4 长度 4 长度(物理)4

我 = 1                               

插槽 0 偏移量 0x0 长度 0 长度(物理)0

KeyHashValue = (8194443284a0)       

您可以在上面的输出中看到Record Typeis PRIMARY_RECORD,并且 Slot0包含值:1。这表明该行存在于表中并且具有我们期望的值。

DBCC PAGE 的输出,删除后,删除了相同的标题部分,如下所示:

插槽 0 偏移 0x6b 长度 11

记录类型 = GHOST_DATA_RECORD 记录属性 = NULL_BITMAP 记录大小 = 11

内存转储@0x000000B76B27A06B

0000000000000000: 1c000800 01000000 010000 .....................

插槽 0 列 1 偏移量 0x4 长度 4 长度(物理)4

我 = 1                               

插槽 0 偏移量 0x0 长度 0 长度(物理)0

KeyHashValue = (8194443284a0)       

输出清楚地显示Record Typefor slot 0is GHOST_RECORD,这表明 slot (row) 已被删除

我们还可以sys.fn_dblog使用m_lsnDBCC PAGE 输出中的值,通过未记录的系统函数检查事务日志:

SELECT *
FROM sys.fn_dblog('102:281659:60', '102:281659:60');
Run Code Online (Sandbox Code Playgroud)

输出显示删除已记录在事务日志中:

?????????????????????????????????????????????????????? ????????????????????????????????????
? 当前 LSN ? 手术 ?语境 ?交易 ID ?
?????????????????????????????????????????????????????? ????????????????????????????????????
? 00000066:00044c3b:003c?LOP_DELETE_ROWS ? LCX_MARK_AS_GHOST ?0000:00160373 ?
?????????????????????????????????????????????????????? ????????????????????????????????????

为简洁起见,这里只显示了输出的前几列,但您可以清楚地看到该LOP_DELETE_ROWS操作,其上下文为LCX_MARK_AS_GHOST,这表明 SQL Server 已DELETE在事务日志中记录了该操作。


Rob*_*uez 8

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;
Run Code Online (Sandbox Code Playgroud)

如果我现在断开连接并重新连接会话,则运行以下代码:

USE tempdb;
SELECT *
FROM dbo.t
Run Code Online (Sandbox Code Playgroud)

我看到该行仍在表格中。这非常符合您所看到的描述。您需要为此检查网络服务代码,或查看跟踪以查看此选项是否有效。

如果您有兴趣,可以对此Not Connect项目进行投票,以要求 Microsoft 提供一种更简单的方法来确定是否启用了隐式事务。