这个简短的SQL脚本在生产中运行的哪个部分不是最佳的?

时间:2013-01-24 20:35:08

标签: sql sql-server database tsql

我正在我们的生产数据库上运行一个脚本,它有两个表:我们的用户表(其中3700个)和他们所做的引用表(280000个)。 Quote是我们应用程序中的主要对象,一个非常大的对象,为其创建和填充了许多数据表。我的目标是从所有引号清除数据库,但是由一小组用户组成。

我首先创建一个临时表,其中包含这些用户的id(在脚本中也使用了其他用户),然后是一个游标,它通过主表运行引号,列出了它们,以及那些从中创建的引号用户组进行必要的清理。

我看到这个脚本将被执行大约26个小时,我认为这是特殊的,因为我需要大约15分钟来恢复数据库,我想那里最重的sql被执行了。然而,数据库的重量超过100GB。

脚本的某些部分是否非常不合理,或者您有一些建议如何通过更短的执行来完成此操作。

我们正在运行SQL Server 2008 R2。

这是剧本的草图。

CREATE table #UsersIdsToStay(user_id int)
INSERT INTO #UsersIdsToStay
select user_id 
from users 
where user_name like '%SOMESTRING '
-----
declare @QuoteId int
declare @UserId int

declare QuoteCursor cursor for 
select DISTINCT QuoteId, UserId
from QuotesTable
where UserId not in 
    (
        select * from #UsersIdsToStay
    )

open QuoteCursor
while 1=1
begin
    fetch QuoteCursor into @QuoteId, @UserId
    if @@fetch_status != 0 break

    -- all the deletions from related tables are executed here using @QuoteId and @UserId
    exec('delete from QuoteHistory where QuoteId = ' + @QuoteId + ' and UserId = ' + @UserId )
    exec('delete from QuoteRevisions where QuoteId = ' + @QuoteId + ' and UserId = ' + @UserId )
    exec('delete from QuoteItems where QuoteId = ' + @QuoteId + ' and UserId = ' + @UserId )
    ....

end
close QuoteCursor;
deallocate QuoteCursor

2 个答案:

答案 0 :(得分:1)

光标限制您在每个相关表上一次只删除一个User_Id / Quote_Id组合。通过使用连接,您将能够大量删除。

您还可以使用公用表表达式(CTE)切换临时表。如果这是一个一次性脚本,临时表应该没问题,但对于生产代码,我会创建一个CTE。

    if OBJECT_ID('tempdb..#quotesToDelete') is not null
            drop table #quotesToDelete


    select distinct 
                ut.user_id, 
                qt.quote_id
        into #quotesToDelete
    from    dbo.QuotesTable qt (nolock)
        inner join dbo.UsersTable ut (nolock)
            on qt.user_id = ut.user_id
    where ut.user_name not like '%SOMESTRING '

    -- all the deletions from related tables are executed here using @QuoteId and @UserId

    -- relatedtableA
    delete a
    from relatedtableA a
        inner join #quotesToDelete b
        on a.user_id = b.user_id
        and a.quote_id = b.quote_id

    -- relatedtableB
    ...

答案 1 :(得分:0)

由于您没有显示删除,因此无法向您展示如何避免光标。

但是如果没有临时性的话,可以做到这一点

select DISTINCT QuoteId, UserId
from QuotesTable
where UserId not in 
    (
        select user_id 
        from users 
        where user_name like '%SOMESTRING '
    )

select DISTINCT QuoteId, UserId
from QuotesTable 
left join UserId 
  on UserId.user_id = QuotesTable.UserId 
 and user_name like '%SOMESTRING '
where UserId.user_id is null 

问题是cusor,你不需要它

CREATE table #QuotesToDelete(QuoteId int, UserID int)
insert into #QuotesToDelete
select DISTINCT QuoteId, UserId
    from QuotesTable 
    left join UserId 
      on UserId.user_id = QuotesTable.UserId 
     and user_name like '%SOMESTRING '
    where UserId.user_id is null 
delete  QH
from QuoteHistory QH 
join #QuotesToDelete 
  on #QuotesToDelete.QuoteId = QH.QuoteId 
 and #QuotesToDelete.UserID  = QH.UserID
delete  QR
from QuoteRevisions QR 
join #QuotesToDelete 
  on #QuotesToDelete.QuoteId = QR.QuoteId 
 and #QuotesToDelete.UserID  = QR.UserID