我有一个包含大约10个字段的表来存储客户的gps信息。随着时间的推移,我们增加了更多的客户,表已经增长到大约1400万行。随着gps数据进入服务,不断地在表中插入一行。 90%的数据并不令人满意,即客户并不关心3个月前车辆的位置,但最新的数据用于生成跟踪报告。我的目标是编写一个sql来执行清除超过一个月的数据。
这是我的问题我不能使用TRUNCATE TABLE,因为我会失去一切? 昨天我用where子句写了一个删除表语句。当我在测试系统上运行它时,它锁定了我的表,并且模拟gps插入间歇性地失败。此外,我的事务日志在尝试记录每次删除时增长到超过6GB。
我的第一个想法是从最早的第一个开始删除数据,但我想知道是否有更好的方法。
答案 0 :(得分:10)
我的2美分:
如果您使用的是SQL 2005及更高版本,则可以考虑根据日期字段对表进行分区,以便在删除旧记录时不会锁定表。
也许,如果你处于做出dba决定的位置,你可以暂时将你的日志模型改为Simple,这样它就不会长得太快,它仍然会增长,但日志不会太详细
答案 1 :(得分:4)
最好是创建一个临时表并仅插入要保留的数据。然后截断原始表并复制备份。
Oracle语法(SQL Server类似)
create table keep as select * from source where data_is_good = 1;
truncate table source;
insert into source select * from keep;
如果源表上有任何外键,则需要禁用外键。
在Oracle中,索引名称在整个架构中必须是唯一的,而不仅仅是每个表。在SQL Server中,您可以通过将“keep”重命名为“source”来进一步优化它,因为您可以在两个表上轻松创建相同名称的索引
答案 2 :(得分:4)
试试这个
WHILE EXISTS(SELECT * FROM table WHERE(删除条件))
BEGIN
SET ROWCOUNT 1000
DELETE表WHERE(删除条件)
SET ROWCOUNT 0
端
这将删除1000个组中的行
答案 3 :(得分:3)
如果您使用的是SQL Server 2005或2008,滑动窗口分区就是完美解决方案 - 即时存档或清除,没有任何可察觉的锁定。请查看here以获取更多信息。
答案 4 :(得分:2)
您可以将最近的数据复制到新表中,截断表格,然后将其复制回来吗?
当然,那么你需要担心在6个月或一年内再次这样做。
答案 5 :(得分:2)
我会按天/月进行手动删除(无论你可以逃脱的最大单位是什么。)一旦你做了第一个,然后写一个存储过程开始每天开始删除你最旧的数据需要。
DELETE FROM TABLENAME
WHERE datediff(day,tableDateTime,getdate() > 90
就个人而言,我讨厌对生产数据集做一些事情,其中一个遗漏的密钥会导致一些非常糟糕的事情发生。
答案 6 :(得分:2)
欢迎使用Data Warehousing。您需要将数据拆分为两部分。
实际应用程序,仅包含当前数据。
历史。
您需要编写一些“ETL”作业来将数据从当前移动到历史记录并删除已移动的历史记录。
您需要定期运行此功能。每日 - 每周 - 每月一季 - 在技术上无关紧要。重要的是历史的使用和使用者。
答案 7 :(得分:1)
我可能会按照你已经想到的那样分批进行。另一种选择是将重要数据插入另一个表,截断GPS表,然后重新插入重要数据。你会有一个小窗口,你会错过最近的历史数据。该窗口的小小将取决于您需要重新插入的数据量。此外,如果表格使用自动增量数字或其他默认值,您需要小心,以便使用原始值。
清理好桌子后,应安排定期清洁工作。您可能还希望根据您的RDBMS来查看分区。
答案 8 :(得分:1)
我假设您无法关闭生产系统(或在清除完成后排队GPS结果以便插入)。
根据您在测试系统中发现的性能,我倾向于一次删除一小部分(可能是10%)。
您的表是否已编入索引?这可能有所帮助,但索引过程对我的系统产生了类似的影响,就像进行一次大清除一样。
答案 9 :(得分:0)
请记住,大多数数据库在事务期间锁定索引中的相邻记录,因此保持简短操作将会很有帮助。我假设您的插入在锁定等待超时时失败,因此在小型突发事务中删除您的数据。我建议使用单线程Perl脚本,以最早的1,000块增量循环。我希望你的主键(希望聚集索引能够以某种方式最终成为两种不同的东西)与时间相关联,因为这是最好的删除方式。
PseudoSQL: 选择max(primId)< 3个月前 从表中删除primId< maxPrimId limit 1000
现在,这是非常有趣的部分:所有这些删除可能会使您的索引变得混乱,并且需要重建它们以防止机器变慢。在这种情况下,您要么必须交换最新的从站,要么只是遭受一些停机。确保在测试机器上测试这种可能的情况。
答案 10 :(得分:0)
如果您正在使用oracle,我会在您的表和索引上按日期设置分区。然后你通过删除分区来删除数据......数据会神奇地消失掉分区。
这是一个简单的步骤 - 并且不会阻塞您的重做日志等。
所有这些here
都有一个基本的介绍答案 11 :(得分:0)
delete语句是否使用表中的任何索引?通常,通过修改语句以使用现有索引或在表上添加有助于提高delete语句执行查询性能的索引,可以获得巨大的性能提升。
另外,正如其他提到的,删除应该在多个块中完成,而不是一个巨大的语句。这可以防止表被锁定太长时间,并让其他进程超时等待删除完成。
答案 12 :(得分:0)
当放下一张桌子时性能非常快 - 即使是一张非常大的桌子。所以这就是我要做的。使用Management Studio中的索引编写完整的表格。编辑脚本并运行它以创建表的副本。称之为table2。执行选择插入以将要保留的数据驻留到新表2中。重命名旧表,比如tableOld。使用原始名称重命名table2。等待。如果没有人尖叫你掉桌2。 存在一些风险。 1)检查原始表上是否定义了触发器或约束。它们可能不会包含在管理工作室生成的脚本中。 2)如果原始表具有标识字段,则在插入新表之前可能必须打开identity_insert。
答案 13 :(得分:0)
我提出了以下T-SQL脚本,它获取了任意数量的最新数据。
IF EXISTS(SELECT name FROM sys.tables WHERE name = 'tmp_xxx_tblGPSVehicleInfoLog')
BEGIN
PRINT 'Dropping temp table tmp_xxx_tblGPSVehicleInfoLog'
DROP TABLE tmp_xxx_tblGPSVehicleInfoLog
END
GO
PRINT 'Creating temp table tmp_xxx_tblGPSVehicleInfoLog'
CREATE TABLE [dbo].[tmp_xxx_tblGPSVehicleInfoLog](
[GPSVehicleInfoLogId] [uniqueidentifier] NOT NULL,
[GPSVehicleInfoId] [uniqueidentifier] NULL,
[Longitude] [float] NULL,
[Latitude] [float] NULL,
[GroundSpeed] [float] NULL,
[Altitude] [float] NULL,
[Heading] [float] NULL,
[GPSDeviceTimeStamp] [datetime] NULL,
[Milliseconds] [float] NULL,
[DistanceNext] [float] NULL,
[UpdateDate] [datetime] NULL,
[Stopped] [nvarchar](1) NULL,
[StopTime] [datetime] NULL,
[StartTime] [datetime] NULL,
[TimeStopped] [nvarchar](100) NULL
) ON [PRIMARY]
GO
PRINT 'Inserting data from tblGPSVehicleInfoLog to tmp_xxx_tblGPSVehicleInfoLog'
SELECT * INTO tmp_xxx_tblGPSVehicleInfoLog
FROM tblGPSVehicleInfoLog
WHERE tblGPSVehicleInfoLog.UpdateDate between '03/30/2009 23:59:59' and '05/19/2009 00:00:00'
GO
PRINT 'Truncating table tblGPSVehicleInfoLog'
TRUNCATE TABLE tblGPSVehicleInfoLog
GO
PRINT 'Inserting data from tmp_xxx_tblGPSVehicleInfoLog to tblGPSVehicleInfoLog'
INSERT INTO tblGPSVehicleInfoLog
SELECT * FROM tmp_xxx_tblGPSVehicleInfoLog
GO
答案 14 :(得分:0)
要防止事务日志失控,请按以下方式对其进行修改:
DECLARE @i INT
SET @i = 1
SET ROWCOUNT 10000
WHILE @i > 0
BEGIN
BEGIN TRAN
DELETE TOP 1000 FROM dbo.SuperBigTable
WHERE RowDate < '2009-01-01'
COMMIT
SELECT @i = @@ROWCOUNT
END
SET ROWCOUNT 0
这是一个使用SQL 2005和2008首选TOP语法的版本:
DECLARE @i INT
SET @i = 1
WHILE @i > 0
BEGIN
BEGIN TRAN
DELETE TOP 1000 FROM dbo.SuperBigTable
WHERE RowDate < '2009-01-01'
COMMIT
SELECT @i = @@ROWCOUNT
END
答案 15 :(得分:0)
我正在分享我的解决方案。我没有索引日期字段。在该过程运行期间,我测试了获取记录计数,插入和更新。他们能够在过程运行时完成。在Azure托管实例中,以绝对最低的配置(通用,4个内核)运行,我能够在一分钟(约55秒)内清除100万行。
CREATE PROCEDURE [dbo].[PurgeRecords] (
@iPurgeDays INT = 2,
@iDeleteRows INT = 1000,
@bDebug BIT = 1 --defaults to debug mode
)
AS
SET NOCOUNT ON
DECLARE @iRecCount INT = 0
DECLARE @iCycles INT = 0
DECLARE @iRowCount INT = 1
DECLARE @dtPurgeDate DATETIME = GETDATE() - @iPurgeDays
SELECT @iRecCount = COUNT(1) FROM YOURTABLE WHERE [Created] <= @dtPurgeDate
SELECT @iCycles = @iRecCount / @iDeleteRows
SET @iCycles = @iCycles + 1 --add one my cycle to get the remainder
--purge the rows in groups
WHILE @iRowCount <= @iCycles
BEGIN
BEGIN TRY
IF @bDebug = 0
BEGIN
--delete a group of records
DELETE TOP (@iDeleteRows) FROM YOURTABLE WHERE [Created] <= @dtPurgeDate
END
ELSE
BEGIN
--display the delete that would have taken place
PRINT 'DELETE TOP (' + CONVERT(VARCHAR(10), @iDeleteRows) + ') FROM YOURTABLE WHERE [Created] <= ''' + CONVERT(VARCHAR(25), @dtPurgeDate) + ''''
END
SET @iRowCount = @iRowCount + 1
END TRY
BEGIN CATCH
--if there are any issues with the delete, raise error and back out
RAISERROR('Error purging YOURTABLE Records', 16, 1)
RETURN
END CATCH
END
GO