6

我们有一个查询来根据 id 字段(主键)从表中删除一些行。这是一个非常简单的查询:

delete all from OUR_TABLE where ID in (123, 345, ...)

问题是 id 的数量可能很大(例如 70k),因此查询需要很长时间。有没有办法优化这个?(我们正在使用 sybase - 如果这很重要)。

4

8 回答 8

4

有两种方法可以使这样的语句执行:

  1. 创建一个新表并复制除要删除的行之外的所有行。之后交换桌子(alter table name ...)我建议尝试一下,即使它听起来很愚蠢。一些数据库在复制时比在删除时快得多。

  2. 对表进行分区。创建 N 个表并使用视图将它们合并为一个。将行排序到按删除条件分组的不同表中。这个想法是删除整个表而不是删除单个行。

于 2009-02-23T10:59:53.173 回答
3

我想知道解析包含 70K 项的 IN 子句是否有问题。您是否尝试过使用连接的临时表?

于 2009-02-23T10:55:40.813 回答
3

考虑分批运行它。一次运行 1000 条记录的循环可能比一个执行所有操作的查询要快得多,此外,它不会让其他用户在一段时间内锁定表。

如果您有级联删除(并且许多外键表受到影响)或涉及触发器,您可能需要以更小的批次运行。您必须进行试验,看看哪个数字最适合您的情况。我有一些表格,我必须分批删除 100 个表格,而其他表格需要 50000 个表格(幸运的是,我正在删除一百万条记录)。

但在任何情况下,我都会将我打算删除的键值放入临时表并从那里删除。

于 2009-02-23T14:35:23.577 回答
2

Sybase 可以处理 IN 子句中的 70K 参数吗?IN我使用的所有数据库对子句的参数数量都有一些限制。例如,Oracle 限制在 1000 左右。

您可以创建子选择而不是 IN 子句吗?这将缩短sql。也许这对 IN 子句中的大量值有帮助。像这样的东西:

  DELETE FROM OUR_TABLE WHERE ID IN 
        (SELECT ID FROM somewhere WHERE some_condition)

如果数据库模型允许,可以通过对数据库进行一些干预来加快删除大量记录。以下是一些策略:

  1. 您可以通过删除索引、删除记录和重新创建索引来加快速度。这将在删除记录时消除重新平衡索引树。

    • 删除表上的所有索引
    • 删除记录
    • 重新创建索引
    • 如果您与该表有很多关系,如果您绝对确定 delete 命令不会破坏任何完整性约束,请尝试禁用约束。删除会更快,因为数据库不会检查完整性。删除后启用约束。
    • 禁用完整性约束,禁用检查约束
    • 删除记录
    • 启用约束
    • 如果您有任何触发器并且您的业务规则允许,请禁用表上的触发器。删除记录,然后启用触发器。

    • 最后,按照其他建议做 - 制作包含不会被删除的行的表的副本,然后删除原始,重命名副本并重新创建完整性约束(如果有)。

我会尝试 1、2 和 3 的组合。如果这不起作用,那么 4。如果一切都很慢,我会寻找更大的盒子 - 更多的内存,更快的磁盘。

于 2009-02-23T11:12:35.453 回答
2

找出是什么消耗了性能!

在许多情况下,您可能会使用提供的解决方案之一。但可能还有其他人(基于 Oracle 知识,所以在其他数据库上情况会有所不同。编辑:刚刚看到你提到了 sybase):

  • 你那张桌子上有外键吗?确保引用的 id 被编入索引
  • 你在那个表上有索引吗?可能是在删除之前删除并在删除之后重新创建可能会更快。
  • 检查执行计划。它是否使用全表扫描可能更快的索引?还是反过来?提示可能会有所帮助
  • 而不是像上面建议的那样选择新表,因为选择可能会更快。

但请记住:首先找出消耗性能的原因。

当您使用 DDL 语句时,请确保您了解并接受它可能对事务和备份产生的后果。

于 2009-02-23T11:30:08.873 回答
1

尝试按照与表相同的顺序对传递到“in”中的 ID 进行排序,或者索引存储在其中。然后您可能会在磁盘缓存上获得更多命中。

将要删除的 ID 放入一个临时表中,该临时表的 ID 与主表的排序顺序相同,可以让数据库对主表进行简单的扫描。

您可以尝试使用多个连接并将工作吐出连接,以便使用数据库服务器上的所有 CPU,但是首先考虑将取出哪些锁等。

于 2009-02-23T11:59:30.207 回答
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
于 2011-03-09T17:04:41.657 回答
0

our_table 是否有关于删除级联的参考?

于 2009-02-23T10:58:17.407 回答