我们有一个查询来根据 id 字段(主键)从表中删除一些行。这是一个非常简单的查询:
delete all from OUR_TABLE where ID in (123, 345, ...)
问题是 id 的数量可能很大(例如 70k),因此查询需要很长时间。有没有办法优化这个?(我们正在使用 sybase - 如果这很重要)。
我们有一个查询来根据 id 字段(主键)从表中删除一些行。这是一个非常简单的查询:
delete all from OUR_TABLE where ID in (123, 345, ...)
问题是 id 的数量可能很大(例如 70k),因此查询需要很长时间。有没有办法优化这个?(我们正在使用 sybase - 如果这很重要)。
有两种方法可以使这样的语句执行:
创建一个新表并复制除要删除的行之外的所有行。之后交换桌子(alter table name ...
)我建议尝试一下,即使它听起来很愚蠢。一些数据库在复制时比在删除时快得多。
对表进行分区。创建 N 个表并使用视图将它们合并为一个。将行排序到按删除条件分组的不同表中。这个想法是删除整个表而不是删除单个行。
我想知道解析包含 70K 项的 IN 子句是否有问题。您是否尝试过使用连接的临时表?
考虑分批运行它。一次运行 1000 条记录的循环可能比一个执行所有操作的查询要快得多,此外,它不会让其他用户在一段时间内锁定表。
如果您有级联删除(并且许多外键表受到影响)或涉及触发器,您可能需要以更小的批次运行。您必须进行试验,看看哪个数字最适合您的情况。我有一些表格,我必须分批删除 100 个表格,而其他表格需要 50000 个表格(幸运的是,我正在删除一百万条记录)。
但在任何情况下,我都会将我打算删除的键值放入临时表并从那里删除。
Sybase 可以处理 IN 子句中的 70K 参数吗?IN
我使用的所有数据库对子句的参数数量都有一些限制。例如,Oracle 限制在 1000 左右。
您可以创建子选择而不是 IN 子句吗?这将缩短sql。也许这对 IN 子句中的大量值有帮助。像这样的东西:
DELETE FROM OUR_TABLE WHERE ID IN
(SELECT ID FROM somewhere WHERE some_condition)
如果数据库模型允许,可以通过对数据库进行一些干预来加快删除大量记录。以下是一些策略:
您可以通过删除索引、删除记录和重新创建索引来加快速度。这将在删除记录时消除重新平衡索引树。
如果您有任何触发器并且您的业务规则允许,请禁用表上的触发器。删除记录,然后启用触发器。
最后,按照其他建议做 - 制作包含不会被删除的行的表的副本,然后删除原始,重命名副本并重新创建完整性约束(如果有)。
我会尝试 1、2 和 3 的组合。如果这不起作用,那么 4。如果一切都很慢,我会寻找更大的盒子 - 更多的内存,更快的磁盘。
找出是什么消耗了性能!
在许多情况下,您可能会使用提供的解决方案之一。但可能还有其他人(基于 Oracle 知识,所以在其他数据库上情况会有所不同。编辑:刚刚看到你提到了 sybase):
但请记住:首先找出消耗性能的原因。
当您使用 DDL 语句时,请确保您了解并接受它可能对事务和备份产生的后果。
尝试按照与表相同的顺序对传递到“in”中的 ID 进行排序,或者索引存储在其中。然后您可能会在磁盘缓存上获得更多命中。
将要删除的 ID 放入一个临时表中,该临时表的 ID 与主表的排序顺序相同,可以让数据库对主表进行简单的扫描。
您可以尝试使用多个连接并将工作吐出连接,以便使用数据库服务器上的所有 CPU,但是首先考虑将取出哪些锁等。
我也认为临时表可能是最好的解决方案。
但是,如果您要执行“从 .. 中删除 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
our_table 是否有关于删除级联的参考?