【问题标题】:Improving performance of Sql Delete提高 Sql Delete 的性能
【发布时间】:2010-10-09 07:08:12
【问题描述】:

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

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

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

【问题讨论】:

  • 这是 ASE 还是 ASA?你知道你的版本号吗?

标签: sql database sybase query-optimization database-optimization


【解决方案1】:

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

【讨论】:

  • 不知道 Sybase 但在 SQLServer 中这也是我第一次尝试优化删除。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

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

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

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

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

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

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

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

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

      【讨论】:

      • 禁用触发器是一个非常糟糕的主意,除非您能够阻止其他用户在禁用时对数据库执行操作。
      • 我知道,这就是我写“如果您的业务规则允许的话”的原因。例如,我看到很多数据库都使用触发器进行某种审计更改。如果他需要表现,那么这种触发器可能可以消除。
      【解决方案5】:

      找出是什么消耗了性能!

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

      • 该表上有外键吗?确保引用 ID 已编入索引
      • 您在该表上有索引吗?可能是在删除之前删除并在删除之后重新创建可能会更快。
      • 检查执行计划。它是否使用全表扫描可能更快的索引?还是反过来?提示可能会有所帮助
      • 而不是上面建议的 select into new_table 而是 create table,因为 select 可能会更快。

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

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

      【讨论】:

        【解决方案6】:

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

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

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

        【讨论】:

          【解决方案7】:

          考虑批量运行。一次运行 1000 条记录的循环可能比一个执行所有操作的查询要快得多,而且不会使表在一段时间内对其他用户锁定很长时间。

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

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

          【讨论】:

          • +1 用于将 ID 放入临时表(或者永久工作表也可以)
          【解决方案8】:

          我也认为临时表可能是最好的解决方案。

          如果您要执行“从 .. 中删除 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
          

          【讨论】:

          • +1 给出了一个使用 tempdb 删除的工作示例,以免让原始发布者猜测如何这样做
          猜你喜欢
          • 2020-09-26
          • 1970-01-01
          • 2014-01-04
          • 2013-03-28
          • 2010-12-10
          • 1970-01-01
          • 2017-05-26
          • 1970-01-01
          相关资源
          最近更新 更多