【问题标题】:Optimising SQL Procedure in SQL Server 2008在 SQL Server 2008 中优化 SQL 过程
【发布时间】:2014-01-03 05:30:16
【问题描述】:

我有两个表 Old_Table 和 New_Table。我的旧表目前有 1300 万条记录,我需要处理旧表中的数据并将其转储到新表中。数据将不断插入到我的旧表中。于是,我写了一个存储过程,并创建了一个 sql 作业,每晚运行并处理当天插入旧表中的数据。

我的程序要花很多时间。就像,我的程序每天需要处理 1300 万条记录,并且 sql 作业自启动以来就一直在运行。如何优化我的以下程序以使其更快

select @From = Max(InsertTime) From [New_Table];
set @To = GETDATE(); 

declare @ID as int;
set @ID = 0;

SET @ID = (SELECT MIN(Id) FROM Old_Table where TimeStamp > @From and TimeStamp < @To)


WHILE @ID IS NOT NULL
BEGIN
    --Get the row data
    SELECT @col1 = COLUMN1,
           @col2 = case when CHARINDEX('?', COLUMN2) > 0 
                              THEN SUBSTRING(COLUMN2, 1,CHARINDEX('?', COLUMN2)-1)
                              else COLUMN2 
                         END,
           @col3 = [dbo].[ModifyString] (COLUMN3) 
    from Old_Table with(nolock)
    where Id = @ID;

            --Few if conditions
                    select @rowID = ID from [dbo].[New_Table] with(nolock)
                    where [COL1] = @col1 and [COL2] = @col2 and [COL3] = @col3

            --If exists update the row else insert as new row
            If @rowID > 0
            Begin
                -- Update my New_Table
            End
            Else
            Begin
                -- Insert into my new table
            End

            --delete from Old_Table
            delete from [dbo].[Old_Table] where id = @ID


    --Fetch next record
    SET @ID = (SELECT MIN(Id) FROM Old_Table where TimeStamp > @From and TimeStamp < @To);
END

我的函数 [dbo].[ModifyString] 有 5 个 IF 条件,其中使用了 CHARINDEX 和 STUFF 函数。

添加表定义:

    [New_Table](
    [ID] [int] IDENTITY(1,1) NOT NULL,  --Primary Key
    [COL1] [varchar](max) NULL,
    [COL2] [varchar](max) NULL,
    [COL3] [varchar](max) NULL,
    [Count] [int] NULL,
    [TimeImported] [datetime] NULL,  -- Non-Clustered Index
    [COL5_NEW] [bit] NULL,
    [COL6_NEW] [bit] NULL,
    [COL7_NEW] [bit] NULL,



[Old_Table]( 
    [id] [int] IDENTITY(1,1) NOT NULL, -- Primary Key
    [TimeStamp] [datetime] NOT NULL,  -- Non-Clustered Index
    [COL1] [varchar](max) NULL,
    [COL2] [varchar](max) NULL,
    [COL3] [varchar](max) NULL,

编辑:我的旧表有重复记录,新表中的 [Count] 需要为旧表中的每个重复记录递增。

谢谢。

【问题讨论】:

  • 你有没有查看查询执行计划,看看查询的哪一部分是瓶颈?还发布包含索引的表定义可能会有所帮助。
  • 经典 RBAR(逐行)代码。 :
  • 请考虑将其发布到dba.stackexchange.com 以获得更好的目标受众。
  • 您提到 old_table 有 1300 万行,每天都有新记录进来。在处理和转储到新表期间,您只需处理今天加载的数据。那么平均每天加载的新数据是多少。如果存储空间不是关键问题,我建议也将日常数据转储到暂存区,然后让夜间工作从那里跑掉。工作完成后,为新的一天清理那个集结区。如果作业失败或最多运行一天左右,您将在暂存区域拥有 1 到 3 天的价值数据,而不是每天 13M+ 数据。
  • @AnupShah 我不会每天查询 13M 条记录。我正在开始一项新工作来清理旧表中的数据,一旦完成了给定日期之前的所有历史数据,它将只处理当天插入其中的记录。

标签: sql-server sql-server-2008 tsql stored-procedures


【解决方案1】:

将查询转换为基于集合的操作。我没有看到任何阻止它工作的东西。出于性能和可维护性的原因,不建议在 T-SQL 中使用基于迭代的代码。

您可以使用MERGE 语句一次执行所有更改,或者运行一个insert、update 和delete 语句。

我认为您没有理由不能在一个(或三个)语句中更新所有 1300 万条记录。这将实现巨大的效率提升(数量级)。

【讨论】:

  • 是的,数据库查询应该尽可能长时间地以集合方式编写。但是我看到另一个问题是我为什么要每天查询 13M 表。如果我只需要处理每日数据。
  • @PushCode 您可以使用 MERGE 语句更新Count 列,以防目标行已经存在。你熟悉 MERGE 语句吗?看看它,它是为你想要的。
  • @usr 我不知道 MERGE。这似乎是我在这种情况下需要的。我会试一试。谢谢。
【解决方案2】:

循环在 SQL 中是不可取的。如果可以,请避免使用它们......这个伪代码应该让你走上正确的道路:

--Insert new
INSERT INTO NewTable
   SELECT * FROM OldTable
   EXCEPT 
   SELECT * FROM NewTable

--Update existing
UPDATE NewTable as nt
   INNER JOIN OldTable as ot
      ON nt.id = ot.id
SET nt.value = ot.value

大多数现代 DBMS 旨在处理基于集合的操作,因此如果您看到循环,则可能会出现性能问题。

【讨论】:

  • 我对原始问题进行了编辑。如何在不循环遍历数据集的情况下考虑重复记录?
【解决方案3】:

除了其他人提到的 RBAR(需要解决)之外,您还在 New_Table 中搜索与 3 个不可索引的 varchar(MAX) 字段的匹配项。

我建议将 OldTableID 作为索引 int 列添加到 New_Table,然后修改脚本以将其用作链接(将其填充为使用的 MERGE 或 INSERT 的一部分)而不是三个 varchar 列(以及排序RBAR)。 Old_Table.TimeStamp 上的索引也会有所帮助。

【讨论】:

    【解决方案4】:

    这是插入的模板

    insert into New_Table 
    SELECT  Old_Table.ID, Old_Table.COLUMN1,
            case when CHARINDEX('?', Old_Table.COLUMN2) > 0 
                                  THEN SUBSTRING(Old_Table.COLUMN2, 1,CHARINDEX('?', COLUMN2)-1)
                                  else Old_Table.COLUMN2 
                             END,
            Old_Table.[ModifyString] (COLUMN3) 
     from Old_Table 
     left join New_Table
       on Old_Table.[COL1] = New_Table.[COL1] 
      and Old_Table.[COL2] = New_Table.[COL2]
      and Old_Table.[COL3] = New_Table.[COL3]
    where Old_Table,TimeStamp > @From and Old_Table.TimeStamp < @To 
      and New_Table.ID is null
    

    有关删除和插入的组合,请参阅此
    enter link description here

    那么更新就是一个join(没有左)并且没有New_Table.ID为null

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-26
      • 2011-05-29
      • 2011-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多