【问题标题】:SQL Server: creating indexes on foreign keysSQL Server:在外键上创建索引
【发布时间】:2021-07-09 03:49:52
【问题描述】:

我在这里试验的是 DELETE 语句如何在一个非常简单的示例中执行。我目前正在使用 SQL Server 2017(我也尝试使用 SQL Server 2014,结果相似)。

我有两张桌子:ParentChildChild 有一个指向 Parent (Parent_ID) 的外键。

家长:

Parent_ID      Name
-----------------------
   1            P1
   2            P2

孩子:

Child_ID    Parent_ID   Data
-----------------------------
  1         1           P1C1
  2         2           P2C1
  3         2           PPPPCCCC
  4         2           P2C1
  5         2           PPPPCCCC
  (around 4 million more rows with Parent_ID=2)

我一直认为在外键上添加索引(Parent_ID in Child 这里)是个好主意。但是今天,我在一个有点极端的情况下尝试了 DELETE 的行为 - 但我确信这种情况可能会发生在现实生活中 - (在 Child 表中有 Parent_ID=2 的 400 万行,Parent_ID= 只有一行1).

如果我尝试删除 Parent_ID = 1 的行,它看起来不错:它足够快,使用了索引,逻辑读取量似乎还不错(12 次逻辑读取:我我不是专家,不知道对于这么少量的数据是否真的可以)。

现在这是我不明白(也不喜欢)的地方:

我尝试删除 Child 中 Parent_ID=2 的所有记录:

BEGIN TRAN

    DELETE FROM child 
    WHERE parent_id = 2

ROLLBACK TRAN

IO 统计显示(对于 DELETE):

表“孩子”。扫描计数 1,逻辑读取 38486782,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。

38486782 逻辑读...不是很大吗?我已经尝试更新统计数据以确保。

    UPDATE STATISTICS Child WITH FULLSCAN

然后再次运行我的查询 => 相同的结果。问题可能是 IX_Child_Parent_ID 上的索引删除?

禁用外键索引后,情况好多了:

表“孩子”。扫描计数 1,逻辑读取 202233,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。

注意:SQL Server 建议为 FK 创建索引。

202233 逻辑读取听起来好多了……至少对于 Parent_Id=2 的特定情况。

问题是:为什么当SQL Server 知道 Parent_ID = 2 大约有4 000 000 行时,它使用索引而不选择聚集索引扫描方法?或者它可能不知道?难道统计数据不应该“帮助”SQL Server 了解这类信息吗?

我可能错过了什么。

(我已经仔细检查过,在创建索引后统计数据 - 似乎 - 正常:

【问题讨论】:

  • (注:评论自己)也许这是一个愚蠢的案例,一切都很正常......但我真的很乐意得到大师们的一些建议^^

标签: sql-server indexing foreign-keys


【解决方案1】:

如您所见,出于删除目的和您所拥有数据的基数,parent_id 上的索引没有用处。

鉴于您知道您需要删除 99% 的行,这样做是非常无效的,原因有很多,尤其是事务日志的增长。

您执行的每条语句都是原子的,并且它自己的隐式事务,如果删除在中途失败,即断电,SQL Server 需要能够回滚不完整的删除,它使用事务日志,所以所有被删除的行都会进入事务日志。

在这样的情况下,将要保留的行插入到新表中,删除原始表,然后将新表重命名为原始表会更高效;您还可以编写原始索引/约束的脚本并将它们应用于新表。

在删除大部分行但少于表的 50% 的情况下,其他建议是将作业分成批次并一次删除

通常它可以帮助在表上创建一个 View,选择要删除的 前 n 行,按特定 key 排序,然后从视图中删除

【讨论】:

    【解决方案2】:

    也许我在这里找到了答案:https://stackoverflow.com/a/3650886

    看起来这是一种预期行为,问题实际上是在更新索引(在我的例子中是 IX_Child_Parent_ID)。

    【讨论】:

      猜你喜欢
      • 2012-05-30
      • 2023-03-16
      • 2011-05-25
      • 1970-01-01
      • 2010-09-21
      • 2020-05-04
      • 2015-01-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多