Hon*_*ger 18 sql-server insert rollback sql-server-2016 select-into
今天我发现存储我的数据库的硬盘已满。这种情况以前发生过,通常原因很明显。通常有一个错误的查询,这会导致对 tempdb 的大量溢出,它会一直增长到磁盘已满。这次发生的事情不太明显,因为 tempdb 不是驱动器满的原因,而是数据库本身。
事实:
我找到了可能的原因;有一个查询选择了太多的行(错误连接会导致选择 110 亿行,而预计会有几十万行)。这是一个SELECT INTO查询,这让我怀疑是否可能发生以下情况:
但是,在这种情况下,我不希望 由 创建的表SELECT INTO仍然存在,它应该被回滚删除。我测试了这个:
BEGIN TRANSACTION
SELECT T.x
INTO TMP.test
FROM (VALUES(1))T(x)
ROLLBACK
SELECT *
FROM TMP.test
Run Code Online (Sandbox Code Playgroud)
这导致:
(1 row affected)
Msg 208, Level 16, State 1, Line 8
Invalid object name 'TMP.test'.
Run Code Online (Sandbox Code Playgroud)
然而目标表确实存在。不过,实际查询并未在显式事务中执行,这能解释目标表的存在吗?
我在这里勾画的假设是否正确?这是可能发生的情况吗?
sep*_*pic 17
不过,实际查询并未在显式事务中执行,这能解释目标表的存在吗?
是的,正是如此。
如果你select into在 an 之外做一个简单的explicit transaction,transactions在自动提交模式下有两个:第一个创建table,第二个填充它。
您可以通过以下方式向自己证明:
在一个专用database的测试服务器中simple recovery model,首先制作一个checkpoint并确保日志只包含与checkpoint. 然后运行select into一行中的 a 并log再次检查,寻找与以下begin tran相关的select into:
checkpoint;
select *
from sys.fn_dblog(null, null);
select 'a' as col
into dbo.t3;
select *
from sys.fn_dblog(null, null)
where Operation = 'LOP_BEGIN_XACT'
and [Transaction Name] = 'SELECT INTO';
Run Code Online (Sandbox Code Playgroud)
你会得到 2 行,显示你有 2 transactions。
我在这里勾画的假设是否正确?这是可能发生的情况吗?
是的,他们是正确的。
was的insert部分,但它不释放任何数据空间。您可以通过执行来验证这一点;你会看到很多。select intorolled backsp_spaceusedunallocated space
如果您希望数据库释放这个未分配的空间,您应该使用shrink您的数据文件。
Jos*_*ell 16
你是对的,SELECT...INTO命令不是原子的。这在原始帖子发布时没有记录,但现在在 MS Docs上的SELECT - INTO Clause (Transact-SQL)页面上特别指出(是开源的!):
该
SELECT...INTO语句分为两部分 - 创建新表,然后插入行。这意味着如果插入失败,它们将全部回滚,但新的(空)表将保留。如果您需要整个操作作为一个整体成功或失败,请使用显式事务。
我将创建一个使用完整恢复模型的数据库。我会给它一个相当小的日志文件,然后告诉它日志文件不能自动增长:
CREATE DATABASE [SelectIntoTestDB]
ON PRIMARY
(
NAME = N'SelectIntoTestDB',
FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\SelectIntoTestDB.mdf',
SIZE = 8192KB,
FILEGROWTH = 65536KB
)
LOG ON
(
NAME = N'SelectIntoTestDB_log',
FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\SelectIntoTestDB_log.ldf',
SIZE = 8192KB,
FILEGROWTH = 0
)
Run Code Online (Sandbox Code Playgroud)
然后我将尝试插入我的 StackOverflow2010 数据库副本中的所有帖子。这应该将一堆东西写入日志文件。
USE [SelectIntoTestDB];
GO
SELECT *
INTO dbo.Posts
FROM StackOverflow2010.dbo.Posts;
Run Code Online (Sandbox Code Playgroud)
这导致运行4秒后出现以下错误:
消息 9002,级别 17,状态 4,第 1 行
由于“ACTIVE_TRANSACTION”,数据库“SelectIntoTestDB”的事务日志已满。
但是我的新数据库中有一个空的 Posts 表:
因此,正如您所怀疑的那样,CREATE TABLE成功了,但该INSERT部分已全部回滚。一种解决方法是使用显式事务(您已在问题中指出)。
| 归档时间: |
|
| 查看次数: |
2732 次 |
| 最近记录: |