加速更新语句T-SQL的前1

时间:2015-11-24 15:09:16

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

我正在为ETL脚本创建一个存储过程,该脚本每小时运行一次,以便向用户提供特定操作的结果。

我需要找到当前结果的先前结果。这很好,我有一个工作查询,我导出到Excel。但是现在我希望自动化这个过程。

存储过程平均每次运行42秒。当在服务器上运行一小时时,这是不可行的,因为我还运行了其他自动脚本。

我的问题是存储过程的一小部分平均值为28秒,而其他所有内容通常只需不到一秒钟(在SSMS中显示为00:00:00)。

我设法减少了其他块的运行时间,我自己将其降低到平均42秒,但我无法做到这一点。

我想知道你们中是否有人知道加速这个小块的任何特定方法?

UPDATE #tmp
SET prev_test_date = (  
    SELECT TOP 1 r.test_date 
    FROM [dbo].[results] r (NOLOCK)
    WHERE r.number = #tmp.number
    AND r.test_date < #tmp.test_date
    ORDER BY r.test_date DESC
)       

我原本打算使用连接来加快速度,但由于查询的TOP 1部分,我无法做到这一点。

有什么想法吗?

4 个答案:

答案 0 :(得分:2)

对于此查询:

UPDATE #tmp
SET prev_test_date = (  
    SELECT TOP 1 r.test_date 
    FROM [dbo].[results] r
    WHERE r.number = #tmp.number AND
          r.test_date < #tmp.test_date
    ORDER BY r.test_date DESC
)   

您需要r(number, test_date)上的索引。

如果您使用的是SQL Server 2012+并且测试日期不重复,您也可以将其写为:

with r as (
      select r.*,
             lag(r.test_date) over (partition by r.number order by r.test_date desc) as prev_test_date
      from [dbo].[results] r
     )
update t
    set t.prev_test_date = r.prev_test_date
    from #tmp t join
         r
         on t.number = r.number;

实际上,如果是这种情况,您可能不需要临时表。您可以修改代码只是为了使用lag()

答案 1 :(得分:1)

UPDATE #tmp
   SET #tmp.prev_test_date = tt.maxdate 
from #tmp 
join 
(
select #tmp.number, max(r.test_date) maxdate 
  from #tmp
  join [dbo].[results] r (NOLOCK)
        on r.number = #tmp.number
       AND r.test_date < #tmp.test_date
 group by #tmp.number 
 ) tt
 on tt.number = #tmp.number

并且#tmp和[results]上的索引都在number,text_date

答案 2 :(得分:1)

UPDATE #tmp
SET prev_test_date = (  
    SELECT max(r.test_date) 
    FROM [dbo].[results] r (NOLOCK)
    WHERE r.number = #tmp.number
    AND r.test_date < #tmp.test_date    
)  

如果没有更多信息,很难分辨,但如果处理过多,您可能需要制作单独的预先计算的表格,并在数据更改时逐步更新。

答案 3 :(得分:0)

我不得不对表格的结构和内容做一些假设,但如果我的假设是正确的,这就是我在这种情况下经常使用的方法:

with cteOrderedResults as (
  -- Ideally R will be clustered by number, test_date for this
  select      R.number
              ,R.test_date
              ,row_number() over  (   partition by R.number
                                      order by R.test_date desc 
                                      -- So the most recent R.test_date from 
                                      -- before T.test_date gets RowNo=1
                                  ) as RowNo
  from        dbo.results R
  inner join  #tmp T            on R.number=T.number
                                and R.test_date<T.test_date

  )
update      T
set         T.prev_test_date=R.test_date
from        #tmp T
inner join  cteOrderedResults R   on T.number=R.number
                                  and 1=R.RowNo

对于我来说,这种方法对我来说很快就可以达到大约百万分之一的行集。正如我所评论的那样,我认为分区的row_number()将利用相应的聚簇索引(如果存在);如果你没有适当地聚集表,你可能会发现这不会那么快。

我同意此处其他地方的评论,如果您确定需要,则应该只添加nolock提示。如果这样做,您应该使用完整正确的语法with (nolock)。来自official MSDN page

  

省略WITH关键字是一项不推荐使用的功能:此功能将在未来版本的Microsoft SQL Server中删除。