【问题标题】:How can I speed up this SQL update query?如何加快此 SQL 更新查询的速度?
【发布时间】:2023-12-04 14:19:01
【问题描述】:

A 表是巨大的(50+ 百万行)表。所以这种更新方式对于 MSSQL 服务器来说太慢了。除了它在存储过程的 while 循环中运行。我认为这个解决方案不是很有效。
有人有什么好主意来解决这个问题吗?
谢谢!

表的定义

CREATE TABLE [dbo].[A](
    [E_NUM] [int] NOT NULL,
    [ID] [char](1) NOT NULL,
    [NUM] [int] NOT NULL,
    [h_out] [smallint] NOT NULL,
    [TAG] [smallint] NOT NULL,
    [TAG1] [smallint] NOT NULL,

 CONSTRAINT [PK_A] PRIMARY KEY CLUSTERED 
(
    [E_NUM] ASC,
    [ID] ASC,
    [NUM] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

B表的定义

CREATE TABLE [dbo].[B](
    [NUM] [int] NOT NULL,
    [NUM2] [int] NOT NULL,
    [TYPE] [char](1) NOT NULL,
    [R_DATE] [datetime] NULL,
    [Note] [varchar](100) NULL,
 CONSTRAINT [PK_B] PRIMARY KEY CLUSTERED 
(
    [NUM] ASC,
    [NUM2] ASC,
    [TYPE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] 

更新

 UPDATE A_1 
 SET A_1.TAG1 = -1
 FROM A
 INNER JOIN B ON A.NUM = B.NUM
 INNER JOIN A AS A_1 ON B.NUM2 = A_1.NUM 
 WHERE A.TAG1 = -1 AND A.TAG = -1 AND B.TYPE='X'
 AND  A.E_num = A_1.E_num AND A.ID=A_1.[ID];

【问题讨论】:

  • 索引和查询调优是提高性能的第一步。将实际执行计划上传到brentozar.com/pastetheplan,如果您需要帮助,请将链接添加到您的问题中。
  • 顺便说一句,A.TAG1 = -1 AND A.TAG = -1 是多余的,但可能与性能问题无关,除非您的意图是 B.TAG = -1
  • 为什么是多余的?它们是不同的列。
  • B.NUM, B.NUM2, B.TYPE 已经有索引了。感谢您的建议!
  • WHERE 子句中的列呢?他们也被编入索引了吗?

标签: sql sql-server performance query-optimization rows


【解决方案1】:

MS SQL Server 中的主键(PK 默认为集群)具有非常高的性能(与 Oracle 相比)。因此,使用正确的 PK 并避免更新它们很重要。

在连接表时,最好以一个表为目标并通过 where 子句缩小范围,然后使用 PK 到达其他表中的记录。您可以通过执行计划找出 MS SQL Server 选择(目标)的表。

我认为您必须在表 A 中的 TAG 和 TAG1 列上声明组合索引以定位表 A。如果 A.TAG1 = -1 AND A.TAG = -1 标准正确缩小表 A 例如低于 10000 行具有此值。

【讨论】:

    【解决方案2】:

    假设我没有犯任何错误,这应该等同于您的 UPDATE 语句:

     UPDATE upd 
        SET upd.TAG1 = -1
       FROM A upd
    
       JOIN A 
         ON A.ID    = upd.ID
        AND A.E_NUM = upd.E_NUM
        AND A.TAG1 = -1 
        AND A.TAG  = -1 
    
       JOIN B 
         ON B.NUM  = A.NUM
        AND B.NUM2 = upd.NUM
        AND B.TYPE = 'X'
    

    由于您没有像B.TYPEA.TAG 等这样的索引。系统几乎有义务遍历整个表以查找适用的记录。如果您的桌子很大,那么是的,这需要一些时间。

    添加这些索引,然后重试:

    CREATE INDEX idx1_A ON A (TAG, TAG1, ID, E_NUM) INCLUDE (NUM) -- NUM is implicit due to PK if you think about it
    CREATE INDEX idx1_B ON B (TYPE, NUM, NUM2)
    

    您认为更新TAG1 也需要更新idx1_A 是正确的,但这仍然比扫描整个表要快得多。 (**)

    你可能想添加

      WHERE upd.TAG1 <> -1 
    

    避免在正确的值已经存在时进行更新,以最大程度地减少影响。

    (**: 如果您的更新影响了 80% 的记录,则可能不值得在表 A 上建立索引,但请记住,这将是一个巨大的操作,因此需要大量时间,因为工作量很大!)

    【讨论】: