我们有一个查询,要根据id字段(主键)从表中删除一些行。这是一个非常简单的查询:
delete all from OUR_TABLE where ID in (123, 345, ...)
问题是没有.id可能很大(例如70k),因此查询需要很长时间。有没有办法优化这个? (我们正在使用sybase - 如果这很重要的话)。
答案 0 :(得分:4)
有两种方法可以使这样的语句执行:
创建一个新表并复制除删除行之外的所有行。然后交换表格(alter table name ...
)我建议即使它听起来很愚蠢也要尝试一下。有些数据库的复制速度比删除时快得多。
对表格进行分区。创建N个表并使用视图将它们合并为一个。将行排序为按删除标准分组的不同表。我们的想法是删除整个表而不是删除单个行。
答案 1 :(得分:3)
我想知道解析一个包含70K项的IN子句是否有问题。您是否尝试过使用连接的临时表?
答案 2 :(得分:3)
考虑分批运行。一次运行1000条记录的循环可能比执行所有操作的一条查询快得多,此外不会长时间将表锁定给其他用户。
如果您有级联删除(以及许多受影响的外键表)或涉及的触发器,您可能需要以更小的批次运行。您需要体验一下,看看哪种情况最适合您的情况。我有桌子,我必须在100个批次中删除,其他50000个工作(幸运的是,因为我删除了100万条记录)。
但是在任何情况下,我都会将我打算删除的键值放入临时表中并从那里删除。
答案 3 :(得分:2)
Sybase可以在IN子句中处理70K参数吗?我使用的所有数据库都对IN
子句的参数数量有一些限制。例如,Oracle限制在1000左右。
你能创建subselect而不是IN子句吗?这将缩短sql。也许这可以帮助IN子句中的如此大量的值。像这样:
DELETE FROM OUR_TABLE WHERE ID IN
(SELECT ID FROM somewhere WHERE some_condition)
如果数据库模型允许,可以通过数据库中的一些干预来加速删除大量记录。以下是一些策略:
您可以通过删除索引,删除记录并再次重新创建索引来加快速度。这将消除重新平衡索引树,同时删除记录。
禁用表上的触发器,如果您有任何触发器,并且您的业务规则允许触发器。删除记录,然后启用触发器。
最后,按照其他建议执行操作 - 制作包含不要删除的行的表的副本,然后删除原始,重命名副本并重新创建完整性约束(如果有)。
我会尝试1,2和3的组合。如果这不起作用,那么4.如果一切都很慢,我会寻找更大的盒子 - 更多的内存,更快的磁盘。
答案 4 :(得分:2)
找出正在消耗性能的内容!
在许多情况下,您可能会使用其中一种解决方案。但可能还有其他人(基于Oracle的知识,所以其他数据库的情况会有所不同。编辑:只是看到你提到了sybase):
但请记住:首先找出正在消耗性能的内容。
当您使用DDL语句时,请确保您理解并接受它可能对事务和备份产生的后果。
答案 5 :(得分:1)
尝试按照与表相同的顺序对传入的ID进行排序,或者存储索引。然后,您可以在磁盘缓存上获得更多点击。
将要删除的ID放入临时表中,其中Ids的排序方式与主表的顺序相同,可以让数据库在主表上进行简单的扫描。
您可以尝试使用多个连接并通过连接执行工作,以便使用数据库服务器上的所有CPU,但请先考虑将取出哪些锁等。
答案 6 :(得分:1)
我也认为临时表可能是最好的解决方案。
如果您要执行“从...中删除ID(从中选择ID)”,但对于大型查询,它仍然会很慢。因此,我建议您使用联接删除 - 许多人不了解该功能。
所以,给出这个示例表:
-- set up tables for this example
if exists (select id from sysobjects where name = 'OurTable' and type = 'U')
drop table OurTable
go
create table OurTable (ID integer primary key not null)
go
insert into OurTable (ID) values (1)
insert into OurTable (ID) values (2)
insert into OurTable (ID) values (3)
insert into OurTable (ID) values (4)
go
然后我们可以按如下方式编写删除代码:
create table #IDsToDelete (ID integer not null)
go
insert into #IDsToDelete (ID) values (2)
insert into #IDsToDelete (ID) values (3)
go
-- ... etc ...
-- Now do the delete - notice that we aren't using 'from'
-- in the usual place for this delete
delete OurTable from #IDsToDelete
where OurTable.ID = #IDsToDelete.ID
go
drop table #IDsToDelete
go
-- This returns only items 1 and 4
select * from OurTable order by ID
go
答案 7 :(得分:0)
our_table是否有关于删除级联的参考?