数据库 SQL 脚本非常非常慢(超过 70 万行的字符串操作)

Chr*_*ium 0 performance sql-server query-performance

我有一个包含 700,000 行的表,其中包含一个字符串 id 字段,其值例如 rec-232276-dup-0 和 rec-354240-org。rec- 是常数,但 id 的其他部分可以改变。

我想拆分这个字符串,以便我只有整数部分和一个布尔值,具体取决于下一部分是否等于 dup 或 org(1 表示 dup,0 表示 org),我将其插入回表中。

我编写了以下循环来执行此操作,并且在功能上它运行良好,但是当我在完整的 700,000 行上运行它时,它需要非常长的时间(+12 小时和计数)。

我做错了什么导致它花费这么多时间?这是导致这种情况的字符串操作吗?我可以做些什么来改善这种情况?

谢谢你的帮助。

我的脚本如下:

select id
into #ControlTable 
from [dbo].[original_test_dataset]

declare @TableID varchar(20)

while exists (select * from #ControlTable)
begin

   select @TableID = (select top 1 id
                    from #ControlTable
                    order by id asc)

   declare @duplicate bit
   declare @id_only varchar(10)

   --1. Find id only
   -- Trim off rec-
   set @id_only = REPLACE(@TableID,'rec-','')
   -- Find position of first - and then take everything before it
   set @id_only = LEFT(@id_only,CHARINDEX('-',@id_only,0))
   set @id_only = REPLACE(@id_only,'-','')
   UPDATE original_test_dataset set id_only = @id_only WHERE id = @TableID;

   --2. Find if duplicate
   IF(PATINDEX('%dup%',@TableID) = 0)   
        BEGIN
           -- No duplicate so original file
           UPDATE original_test_dataset set duplicate = 0 WHERE id = @TableID;
        END
   ELSE
       BEGIN
           -- Duplicate
           UPDATE original_test_dataset set duplicate = 1 WHERE id = @TableID;
       END

   delete #ControlTable
   where id = @TableID
END

drop table #ControlTable
Run Code Online (Sandbox Code Playgroud)

根据要求添加表定义:

CREATE TABLE [dbo].[original_test_dataset](
    [id] [varchar](50) NULL,
    [ FirstName] [varchar](50) NULL,
    [ LastName] [varchar](50) NULL,
    [ Phone1] [varchar](50) NULL,
    [ Phone2] [varchar](50) NULL,
    [ Phone3] [varchar](50) NULL,
    [ No] [varchar](50) NULL,
    [ Road] [varchar](50) NULL,
    [ Village] [varchar](50) NULL,
    [ Town] [varchar](50) NULL,
    [ PC] [varchar](50) NULL,
    [ County] [varchar](50) NULL,
    [ DOB] [varchar](50) NULL,
    [id_only] [varchar](10) NULL,
    [duplicate] [bit] NULL
) ON [PRIMARY]

GO

/****** Object:  Index [PK_ORIGINAL_TEST_DATASET_ID]    Script Date: 03/09/2014  07:47:19 ******/
CREATE CLUSTERED INDEX [PK_ORIGINAL_TEST_DATASET_ID] ON [dbo].[original_test_dataset]
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON   [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)

Rem*_*anu 5

您有一个性能问题,因此我建议您首先阅读如何分析 SQL Server 性能

至于您的脚本:您正在尽可能慢地进行。一排排痛苦地慢排。甚至游标慢也不比游标慢。您在这里拥有的是一个更新:

update original_test_dataset
set duplicate= case when PATINDEX('%dup%', id)=0 then 0 else 1 end,
 id_only = REPLACE((LEFT(REPLACE(id,'rec-',''), CHARINDEX('-',REPLACE(id,'rec-',''),0)),'-','');
Run Code Online (Sandbox Code Playgroud)

就是这样,没有别的。

原始脚本中的内容很慢,因为:

  • 它是逐行操作的,而不是集合。SQL 旨在操作集,而不是行。
  • 它在独立提交中执行大量写入,导致巨大的刷新提交等待。应该使用批量提交。
  • 它执行多个无索引的端到端扫描 #ControlTable每个操作 #ControlTable都是无索引的端到端扫描,它应该有一个索引id,是的 #temp 表确实需要索引)
  • 它每行进行两次更新,而不是对两个字段进行一次更新
  • 我不知道访问original_test_dataset是否以任何方式优化,因为缺少索引id可能不是。

declare @TableID varchar(20)vs. [id] [varchar](50) NULL:您正在默默地截断数据。基本上,您的脚本会破坏表格。