T-SQL在视图中长时间运行查询

时间:2012-04-17 19:33:24

标签: sql-server tsql

我的视图有一个非常复杂的查询(见下文)。然后我使用此视图将数据插入另一个表。我在数据库中有很多数据,当我运行插入查询时,它会运行8个小时,并且最后只进行实际插入。它似乎首先从视图中获取所有结果,然后才将它们插入到我的表中。是否可以单独插入每条记录?

以下是观点:

CREATE VIEW [dbo].[Enrollment]
AS
WITH CTE AS (SELECT     RN = ROW_NUMBER() OVER (PARTITION BY PRIMARYPROVIDERCODE, CLIENTNUMBER
                               ORDER BY PRIMARYPROVIDERCODE, CLIENTNUMBER, STARTINGDATE), ID, PRIMARYPROVIDERCODE, CLIENTNUMBER, STARTINGDATE, ENDINGDATE
FROM         AUTHORIZE
WHERE     DOCREVNO = 0 AND CMT = 'N'
GROUP BY PRIMARYPROVIDERCODE, CLIENTNUMBER, STARTINGDATE, ENDINGDATE, ID)
    SELECT     [Current Row].ID, [Current Row].RN, [Current Row].PRIMARYPROVIDERCODE, [Current Row].CLIENTNUMBER, [Current Row].STARTINGDATE, 
                            ENDINGDATE =
                                (SELECT     TOP 1 [Next Ending].ENDINGDATE
                                  FROM          CTE[Next Ending]
                                  WHERE      [Next Ending].RN >= [Current Row].RN AND [Next Ending].ENDINGDATE IS NOT NULL AND 
                                                         [Next Ending].PRIMARYPROVIDERCODE = [Current Row].PRIMARYPROVIDERCODE AND 
                                                         [Next Ending].CLIENTNUMBER = [Current Row].CLIENTNUMBER
                                  ORDER BY [Next Ending].RN)
     FROM         CTE[Current Row] INNER JOIN
                            CTE[Previous Row] ON ([Previous Row].PRIMARYPROVIDERCODE = [Current Row].PRIMARYPROVIDERCODE AND 
                            [Previous Row].CLIENTNUMBER = [Current Row].CLIENTNUMBER) AND (([Previous Row].RN = [Current Row].RN AND [Current Row].RN = 1) OR
                            ([Previous Row].RN = [Current Row].RN - 1 AND [Previous Row].ENDINGDATE IS NOT NULL))

以下是INSERT查询:

INSERT INTO [dbo].[clientenrollment]
           ([DOCSERNO]
           ,[DOCREVNO]
           ,[USERID]
           ,[SIGNED]
           ,[TIMESTAMP]
           ,[ClientNumber]
           ,[enrollmentdate]
           ,[terminationdate]
           ,[PrimaryProviderCode]
           ,[Action]
           ,[Providername]
           ,[SERVICEMAPCODE])
SELECT REPLACE(STR(6620100322000000 + row_number() over (order by ev.ID asc), 17, 0), ' ', '0')
           ,0
           , NULL
           , 0
           , GETDATE()
           ,ev.CLIENTNUMBER
           ,ev.STARTINGDATE
           ,ev.ENDINGDATE
           ,ev.PRIMARYPROVIDERCODE
           ,auth.ACTION
           ,p.PROVIDERNAME
           ,ms.MapCode  
FROM [dbo].[Enrollment] AS ev
JOIN AUTHORIZE auth ON auth.ID = ev.ID
LEFT JOIN MasterService ms ON ms.Code = auth.SERVICECODE
LEFT JOIN PROVIDER p ON p.PROVIDERCODE = ev.PRIMARYPROVIDERCODE
WHERE p.DOCREVNO = 0

4 个答案:

答案 0 :(得分:2)

  

是否可以单独插入每条记录

是和否。

分别插入每条记录,但它只在最后提交。有时在非常长的插入中,如果使用脏读,则可以在单个非常长的插入上看到行计数增加。在提交之前,没有其他事务通常会看到记录,因为这是一个ACID事件,整个事务可以回滚。

至于如何确定要插入的行取决于执行计划。因为你有一些排序(ORDER BY)和一些常见的表表达式,所以确定行可能需要一段时间,并且肯定有可能大部分操作正在确定要插入的行(可能在此操作期间假脱机到临时存储器) )然后他们很快插​​入。

通过以某种方式中断操作,可以在单个事务中插入单行。当然,如果操作停止,您可能只完成整个操作的一部分。

或者,我认为8小时对于这样的查询来说太长了,并且鉴于你没有提到关于索引策略或任何表中的行数或执行计划的任何内容,我会先看看那些东西并了解它们。

你有PRIMARYPROVIDERCODE,CLIENTNUMBER,STARTINGDATE的索引吗?另请注意WHERE子句:DOCREVNO = 0 AND CMT ='N'也可能需要在该索引中排在第一位。查看执行计划,看看发生了什么。即使您无法访问生产,生产DBA也应该能够为您提供执行计划。

答案 1 :(得分:1)

你的部分问题是在这里使用视图是个不好的主意。您必须生成整个视图,然后加入视图中的表并过滤掉。我要做的第一件事是放弃观点。然后我会抛弃相关的子查询,因为那些必须逐行运行,你可以从连接中获得更好的performacen形式的派生表。我没有时间仔细研究这个问题并弄清楚你要完成什么,但我会指出,当你必须努力完成某些事情时,你的数据库结构通常会遇到设计问题。

答案 2 :(得分:0)

答案 3 :(得分:0)

我会说Cade Roux说,似乎8小时比我预期的优化查询要长,你当然应该检查表上的索引。

我还要补充一点,也许简化JOIN子句和子查询WHERE /隐式JOIN子句可能是值得的。您可以使用RANK()函数对相似的行进行分组,然后连接这些组号而不是当前使用的多个列。

不保证是完美的,但是这样的事情:

CREATE VIEW [dbo].[Enrollment]
AS
WITH CTE AS (SELECT     GN = RANK() OVER (ORDER BY PRIMARYPROVIDERCODE, CLIENTNUMBER) , RN = ROW_NUMBER() OVER (PARTITION BY PRIMARYPROVIDERCODE, CLIENTNUMBER
                               ORDER BY PRIMARYPROVIDERCODE, CLIENTNUMBER, STARTINGDATE), ID, PRIMARYPROVIDERCODE, CLIENTNUMBER, STARTINGDATE, ENDINGDATE
FROM         AUTHORIZE
WHERE     DOCREVNO = 0 AND CMT = 'N'
GROUP BY PRIMARYPROVIDERCODE, CLIENTNUMBER, STARTINGDATE, ENDINGDATE, ID)
    SELECT     [Current Row].ID, [Current Row].RN, [Current Row].PRIMARYPROVIDERCODE, [Current Row].CLIENTNUMBER, [Current Row].STARTINGDATE, 
                            ENDINGDATE =
                                (SELECT     TOP 1 [Next Ending].ENDINGDATE
                                  FROM          CTE[Next Ending]
                                  WHERE      [Next Ending].RN >= [Current Row].RN AND [Next Ending].ENDINGDATE IS NOT NULL AND 
                                                         [Next Ending].GN = [Current Row].GN
                                  ORDER BY [Next Ending].RN)
     FROM         CTE[Current Row] INNER JOIN
                            CTE[Previous Row] ON ([Previous Row].GN = [Current Row].GN) AND (([Previous Row].RN = [Current Row].RN AND [Current Row].RN = 1) OR
                            ([Previous Row].RN = [Current Row].RN - 1 AND [Previous Row].ENDINGDATE IS NOT NULL))

在类似的情况下,我已经用这种方法将小时+查询变成分钟+查询,但当然每种情况都不同......