0

我有一个具有相当复杂查询的视图(见下文)。然后我使用这个视图将数据插入到另一个表中。我在数据库中有很多数据,当我运行插入查询时,它运行了 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 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

4 回答 4

2

是否可以让它分别插入每条记录

是和不是。

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

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

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

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

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

于 2012-04-17T19:44:08.640 回答
1

您的部分问题是在这里使用视图是一个糟糕的主意。您必须生成整个视图,然后加入视图中的表并向下过滤。我要做的第一件事就是放弃视图。然后我会放弃相关的子查询,因为它们必须逐行运行,并且你会在连接中从派生表中获得更好的性能。我没有时间详细讨论并弄清楚你想要完成什么,但我会指出,当你必须如此努力地完成某件事时,你的数据库结构通常会出现设计问题。

于 2012-04-17T21:06:04.330 回答
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))

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

于 2012-04-17T19:52:28.403 回答