【问题标题】:Clustered index update, how changing the index only at different column value?聚集索引更新,如何仅在不同列值处更改索引?
【发布时间】:2024-05-21 01:25:02
【问题描述】:

我有桌子, 例如

Id int primary key(clustered index)
Name varchar(255)not null (non-clustered index)
..
..
Other columns

我执行存储过程为

update table1 set table1.Name=isnull(@Name,table1.Name) ,( updating other columns) where Id=@Id;

很多时候Name等于@Name; 在执行计划中我看到了

如果我将存储过程更改为

update table1 set ( updating other columns) where Id=@Id;

我看到成本:15%

问题:

我需要在以下两种情况下更改此生产者

  1. table1.Name= @Name
  2. table1.Name 名称。

在第一种情况下 table1.Name 不改变值或索引不更新并且聚集索引更新成本 15%;

在第二种情况下 table1.Name 更改值和聚集索引更新成本 50%。

为什么更新相等的值时索引会更新?

我无法选择“名称”并在更新前进行比较

我知道创建 varchar 列索引不是个好主意

更新

更新查询with更改名称列(执行CPU时间200ms

更新查询更改名称列(执行CPU时间70ms

更新

问题已结束

【问题讨论】:

  • 实际上并没有更新操作——它相当于物理上删除行并插入新行;服务器将“更新”您过滤的所有行;如果您需要避免匹配行受到影响 - 使用 WHERE 排除这些行 (table1.Name@Name);
  • @revoua 好的,为什么当我删除 table1.Name 更新时“聚集索引更新”成本发生了变化?
  • 您可以在估计和实际行中看到所有详细信息更新,分享详细信息
  • @revoua 什么细节?
  • 整个执行计划是什么?我打赌有一个索引键查找

标签: sql sql-server sql-server-2008 sql-server-2016 sql-execution-plan


【解决方案1】:

为了解决我的问题,我创建了一个触发器

CREATE TRIGGER [dbo].[trg_BeforeUpdateSubject] 
   ON  [dbo].[Subject] 
   INSTEAD OF UPDATE
AS 
BEGIN
    SET NOCOUNT ON;
        declare @old_val  nvarchar(255),@new_val nvarchar(255) ;
        select
@old_val=[Name]   
from deleted;
select 
@new_val=[Name]
from inserted;
     if (@old_val=@new_val)  
    BEGIN

        UPDATE 
            Subject
        SET
            set new values for other colums,
            Subject.DLC             =   GETDATE()
        FROM 
            inserted
        WHERE
            Subject.Id = inserted.Id            
    END ELSE
    BEGIN
        UPDATE 
            Subject
        SET
            Subject.Name = inserted.Name,
            set new values for other colums,
            Subject.DLC             =   GETDATE()
        FROM 
            inserted
        WHERE
            Subject.Id = inserted.Id                
    END

END

它的工作完美。更新查询的平均值是 90ms(CPU),旧值是 200ms

【讨论】: