时间:2021-07-01 10:21:17 帮助过:26人阅读
--Create table tmp_employee CREATE TABLE [dbo].[tmp_employee] ( [EmployeeNo] INT PRIMARY KEY, [EmployeeName] [nvarchar](50) NULL, [CreateUser] [nvarchar](50) NULL, [CreateDatetime] [datetime] NULL );
--循环插入 SET STATISTICS TIME ON; DECLARE @Index INT = 1; DECLARE @Timer DATETIME = GETDATE(); WHILE @Index <= 100000 BEGIN INSERT [dbo].[tmp_employee](EmployeeNo, EmployeeName, CreateUser, CreateDatetime) VALUES(@Index, ‘Employee_‘ + CAST(@Index AS CHAR(6)), ‘system‘, GETDATE()); SET @Index = @Index + 1; END SELECT DATEDIFF(MS, @Timer, GETDATE()) AS [执行时间(毫秒)]; SET STATISTICS TIME OFF;
--事务循环 BEGIN TRAN; SET STATISTICS TIME ON; DECLARE @Index INT = 1; DECLARE @Timer DATETIME = GETDATE(); WHILE @Index <= 100000 BEGIN INSERT [dbo].[tmp_employee](EmployeeNo, EmployeeName, CreateUser, CreateDatetime) VALUES(@Index, ‘Employee_‘ + CAST(@Index AS CHAR(6)), ‘system‘, GETDATE()); SET @Index = @Index + 1; END SELECT DATEDIFF(MS, @Timer, GETDATE()) AS [执行时间(毫秒)]; SET STATISTICS TIME OFF; COMMIT;
SET STATISTICS TIME ON; DECLARE @Timer DATETIME = GETDATE(); INSERT [dbo].[tmp_employee](EmployeeNo, EmployeeName, CreateUser, CreateDatetime) SELECT TOP(100000) EmployeeNo = ROW_NUMBER() OVER (ORDER BY C1.[OBJECT_ID]), ‘Employee_‘, ‘system‘, GETDATE() FROM SYS.COLUMNS AS C1 CROSS JOIN SYS.COLUMNS AS C2 ORDER BY C1.[OBJECT_ID] SELECT DATEDIFF(MS, @Timer, GETDATE()) AS [执行时间(毫秒)]; SET STATISTICS TIME OFF;
SET STATISTICS TIME ON; DECLARE @Timer DATETIME = GETDATE(); ;WITH CTE(EmployeeNo, EmployeeName, CreateUser, CreateDatetime) AS( SELECT TOP(100000) EmployeeNo = ROW_NUMBER() OVER (ORDER BY C1.[OBJECT_ID]), ‘Employee_‘, ‘system‘, GETDATE() FROM SYS.COLUMNS AS C1 CROSS JOIN SYS.COLUMNS AS C2 ORDER BY C1.[OBJECT_ID] ) INSERT [dbo].[tmp_employee] SELECT EmployeeNo, EmployeeName, CreateUser, CreateDatetime FROM CTE; SELECT DATEDIFF(MS, @Timer, GETDATE()) AS [执行时间(毫秒)]; SET STATISTICS TIME OFF;
小结:
SET STATISTICS TIME ON; DECLARE @Timer DATETIME = GETDATE(); DELETE FROM [dbo].[tmp_employee]; SELECT DATEDIFF(MS, @Timer, GETDATE()) AS [执行时间(毫秒)]; SET STATISTICS TIME OFF;
SET STATISTICS TIME ON; DECLARE @Timer DATETIME = GETDATE(); SET ROWCOUNT 100000; WHILE 1 = 1 BEGIN BEGIN TRAN DELETE FROM [dbo].[tmp_employee]; COMMIT IF @@ROWCOUNT = 0 BREAK; END SET ROWCOUNT 0; SELECT DATEDIFF(MS, @Timer, GETDATE()) AS [执行时间(毫秒)]; SET STATISTICS TIME OFF;
SET STATISTICS TIME ON; DECLARE @Timer DATETIME = GETDATE(); TRUNCATE TABLE [dbo].[tmp_employee]; SELECT DATEDIFF(MS, @Timer, GETDATE()) AS [执行时间(毫秒)]; SET STATISTICS TIME OFF;
小结:
三、磨刀霍霍 - 犹抱琵琶半遮面
由上面的第二点我们知道,插入最快和删除最快的方式分别是批量插入和TRUNCATE,所以为了达到删除大数据的目的,我们也将采用这两种方式的组合,其中心思想是先把需要保留的数据存放之新表中,然后TRUNCATE原表中的数据,最后再批量把数据插回去,当然实现方式也可以随便变通。
脚本类似如下
SELECT * INTO #keep FROM Original WHERE CreateDate > ‘2011-12-31‘ TRUNCATE TABLE Original INSERT Original SELECT * FROM #keep
第一条语句会把所有要保留的数据先存放至表#keep中(表#keep无需手工创建,由SELECT INTO生效),#keep会Copy原始表Original的表结构。PS:如果你只想创建表结构,但不拷贝数据,则对应的脚本如下
SELECT * INTO #keep FROM Original WHERE 1 = 2
第二条语句用于清除整个表中数据,产生的日志文件基本可以忽略;第三条语句用于还原保留数据。
几点说明:
CREATE TABLE #keep AS (xxx) xxx -- 使用上面提到的方法(使用既有表的创建脚本),但是不能够保证完全一致;
INSERT #keep SELECT * FROM Original where clause
DROP TBALE Original
EXEC SP_RENAME ‘#keep‘,‘Original‘
这种方式比第一种方法略快点,因为省略了数据还原(即最后一步的数据恢复),但是稍微麻烦点,因为你需要创建一张和以前原有一模一样的表结构,包括基本列、属性、约束、索性等等。
三、数据收缩 - 秋风少落叶
数据删除后,发现数据库占用空间大小并没有发生变化,此时我们就用借助强悍的数据收缩功能了,脚本如下,运行时间不定,取决于你的数据库大小,多则几十分钟,少则瞬间秒杀
DBCC SHRINKDATABASE(DB_NAME)
出处:https://www.cnblogs.com/panchunting/archive/2013/04/27/SQL_Tech_001.html
=====================================================================================
在SQL Server中,如何快速删除大表中的数据呢? 回答这个问题前,我们必须弄清楚上下文环境和以及结合实际、具体的需求,不同场景有不同的应对方法。
1: 整张表的数据全部删除
如果是整张表的数据全部清空、删除,这种场景倒是非常简单,TRUNCATE TABLE肯定是最快的。 反而用DELETE处理的话,就是一个糟糕的策略。
2: 大表中删除一部分数据
对于场景1、非常简单,但是很多实际业务场景,并不能使用TRUNCATE这种方法,实际情况可能只是删除表中的一部分数据或者进行数据归档后的删除。假设我们遇到的表为TEST,需要删除TEST表中的部分数据。那么首先我们需要对表的数据量和被删除的数据量做一个汇总统计,具体,我们应该采用下面方法:
· 检查表的数据量,以及要删除的数据量。然后计算删除的比例,
sp_spaceused‘dbo.TEST‘;
SELECT COUNT(*) AS DELETE_RCD WHERE TEST WHERE ......<删除条件>
2.1 删除大表中绝大部分的数据,但是这个绝大部分怎么定义不好量化,所以我们这里就量化为60%。如果删除的数据比例超过60%,就采用下面方法:
1: 新建表TEST_TMP
2: 将要保留的数据转移到TEST_TMP
3: 将原表TEST重命名为TEST_OLD, 而将TEST_TMP重命名为TEST
4: 检查相关的触发器、约束,进行触发器或约束的重命名
5: 核对操作是否正确后,原表(TEST_OLD)要么TRUANCATE后,再DROP掉。要么保留一段时间,保险起见。
注:至于这个比例60%是怎么来的。这个完全是个经验值,有简单的测试,但是没有很精确和科学的概率统计验证。
另外,还要考虑业务情况,如果一直有应用程序访问这个表,其实这种方式也是比较麻烦的,因为涉及数据的一致性,业务中断等等很多情况。但是,如果程序较少访问,或者在某个时间段没有访问,那么完全可以采用这种方法。
2.2 删除大表中部分数据,如果比例不超过60%
1:先删除或禁用无关索引(无关索引,这里指执行计划不用到的索引,这里是指对当前DELETE语句无用的索引)。因为DELETE操作属于DML操作,而且大表的索引一般也非常大,大量DELETE将会对索引进行维护操作,产生大量额外的IO操作。
2:用小批量,分批次删除(批量删除比一次性删除性能要快很多)。不要一次性删除大量数据。一次性删除大量记录。会导致锁的粒度范围很大,并且锁定的时间非常长,而且还可能产生阻塞,严重影响业务等等。而且数据库的事务日志变得非常大。执行的时间变得超长,性能非常糟糕。
批量删除时,到底一次性删除多少数量的记录数,SQL效率最高呢? 这个真没有什么规则计算,个人测试对比过,一次删除10000或100000,没有发现什么特别规律。(有些你发现的“规律”,换个案例,发现不一样的结果,这个跟环境有关,有时候可能是一个经验值)。不过一般用10000,在实际操作过程,个人建议可以通过做几次实验对比后,选择一个合适的值即可。
案例1:
DECLARE @delete_rows INT;
DECLARE @delete_sum_rows INT =0;
DECLARE @row_count INT=100000
WHILE 1 = 1
BEGIN
DELETE TOP ( @row_count )
FROM dbo.[EmployeeDayData]
WHERE WorkDate < CONVERT(DATETIME, ‘2012-01-01 00:00:00‘,120);
SELECT @delete_rows = @@ROWCOUNT;
SET @delete_sum_rows +=@delete_rows
IF @delete_rows = 0
BREAK;
END;
SELECT @delete_sum_rows;
案例2:
DECLARE @r INT;
DECLARE @Delete_ROWS BIGINT;
SET @r = 1;
SET @Delete_ROWS =0
WHILE @r > 0
BEGIN
BEGIN TRANSACTION;
DELETE TOP (10000) -- this will change
YourSQLDba..YdYarnMatch
WHERE Remark=‘今日未入‘ and Operation_Date<CONVERT(datetime, ‘2019-05-30‘,120);
SET @r = @@ROWCOUNT;
SET @Delete_ROWS += @r;
COMMIT TRANSACTION;
PRINT(@Delete_ROWS);
END
该表有下面两个索引
USE [YourSQLDba]
GO
IF EXISTS (