添加一个连接会导致性能下降

时间:2012-05-30 19:04:44

标签: sql-server sql-server-2005

我们在SQL Server 2005上有一个相当复杂的查询,具有以下特征:

  • 11 table joins
  • 8 view joins
  • 7个非相关查询联接

所有联接都有一个共同的索引列。

它通常在30秒内运行,但是当再添加一个表连接(已连接的列索引)时,它似乎永远运行。

我注意到,如果删除一个特定的现有视图连接,即使使用新连接,它也会再次快速运行。

以下是查询的修改版本,删除了大多数联接但仍然显示性能问题。

--This runs super slow
SELECT 
    L.LoanID    
FROM  dbo.Loan L 
    JOIN dbo.Company C ON c.CompanyKey = l.CompanyKey
    JOIN dbo.Status S  on L.LoanID = S.LoanID
    JOIN dbo.Participation P on L.LoanID = P.LoanID
    JOIN dbo.Delinquent D on  L.LoanID = D.LoanID
    JOIN dbo.Property Pr on Pr.Loanid = L.Loanid
    JOIN dbo.MailingAddress ma ON ma.LoanID = L.LoanID  
    LEFT JOIN dbo.BorrowerPhonePivot bp ON bp.loanid = l.loanid
WHERE s.primstat=1 AND  DATEADD(d,16,L.DueDate) <= C.TransPostDate


--This runs fast
 SELECT 
    L.LoanID    
 FROM  dbo.Loan L 
    JOIN dbo.Company C ON c.CompanyKey = l.CompanyKey
    JOIN dbo.Status S  on L.LoanID = S.LoanID
    JOIN dbo.Participation P on L.LoanID = P.LoanID
    JOIN dbo.Delinquent D on  L.LoanID = D.LoanID
    JOIN dbo.Property Pr on Pr.Loanid = L.Loanid
    --JOIN dbo.MailingAddress ma ON ma.LoanID = L.LoanID    
    LEFT JOIN dbo.BorrowerPhonePivot bp ON bp.loanid = l.loanid
 WHERE s.primstat=1 AND  DATEADD(d,16,L.DueDate) <= C.TransPostDate


 --This runs fast
 SELECT 
    L.LoanID    
 FROM  dbo.Loan L 
    JOIN dbo.Company C ON c.CompanyKey = l.CompanyKey
    JOIN dbo.Status S  on L.LoanID = S.LoanID
    JOIN dbo.Participation P on L.LoanID = P.LoanID
    JOIN dbo.Delinquent D on  L.LoanID = D.LoanID
    JOIN dbo.Property Pr on Pr.Loanid = L.Loanid
    JOIN dbo.MailingAddress ma ON ma.LoanID = L.LoanID  
    --LEFT JOIN dbo.BorrowerPhonePivot bp ON bp.loanid = l.loanid
 WHERE s.primstat=1 AND  DATEADD(d,16,L.DueDate) <= C.TransPostDate

此问题并非特定于此查询,因为我在其他查询之前遇到过这种奇怪的行为,并且从未能完全解决它。我的理论是,SQL Server正在达到内部查询计划限制而变得愚蠢。

我将查询过滤到一个状态(返回一条记录但需要10分钟),以便通过DTA生成建议。我添加了一些所需的索引没有任何区别。

我检查了执行计划的结果,但没有发现任何异常。

关于还有什么要寻找的任何建议?

这就是我的尝试:

  • DTA - 在4个生成的建议中添加了一个所需的索引。 (其他人已经被覆盖)
  • MAXDOP 1,看看它是否是@AaronBertrand
  • 的线程问题
  • 根据@AaronBertrand
  • 对表进行更新统计
  • 根据@AaronBertrand在SQL sentry plan explorer中运行sqlplan(pastebin)。奇怪的是, Actual Rows列显示的数字比现实大一个数量级。不知何故,查询计划优化器变得混乱。

第一次查询的showplan:

  |--Nested Loops(Left Outer Join, OUTER REFERENCES:([L].[LoanID]))
   |--Nested Loops(Inner Join, WHERE:([Expr1056]<=[Service_Prod].[dbo].[Company].[TransPostDate] as [C].[TransPostDate]))
   |    |--Hash Match(Inner Join, HASH:([ma].[LoanID])=([D].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Delinquent].[LoanID] as [D].[LoanID]=[Service_Prod].[dbo].[MailingAddress].[LoanID] as [ma].[LoanID]))
   |    |    |--Hash Match(Inner Join, HASH:([ma].[LoanID])=([P].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Participation].[LoanID] as [P].[LoanID]=[Service_Prod].[dbo].[MailingAddress].[LoanID] as [ma].[LoanID]))
   |    |    |    |--Merge Join(Inner Join, MERGE:([Pr].[LoanID])=([ma].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]=[Service_Prod].[dbo].[MailingAddress].[LoanID] as [ma].[LoanID]))
   |    |    |    |    |--Merge Join(Inner Join, MERGE:([Pr].[LoanID])=([L].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Loan].[LoanID] as [L].[LoanID]=[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]))
   |    |    |    |    |    |--Merge Join(Inner Join, MERGE:([Pr].[LoanID])=([S].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Status].[LoanID] as [S].[LoanID]=[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]))
   |    |    |    |    |    |    |--Index Scan(OBJECT:([Service_Prod].[dbo].[Property].[aaaaaProperty_PK] AS [Pr]), ORDERED FORWARD)
   |    |    |    |    |    |    |--Index Seek(OBJECT:([Service_Prod].[dbo].[Status].[_dta_index_Status_114_1122103038__K13_K1_K2_K3] AS [S]), SEEK:([S].[PrimStat]=(1)) ORDERED FORWARD)
   |    |    |    |    |    |--Compute Scalar(DEFINE:([Expr1056]=dateadd(day,(16),[Service_Prod].[dbo].[Loan].[DueDate] as [L].[DueDate])))
   |    |    |    |    |         |--Index Scan(OBJECT:([Service_Prod].[dbo].[Loan].[_dta_index_Loan_K1_K66_K10_K23] AS [L]), ORDERED FORWARD)
   |    |    |    |    |--Index Scan(OBJECT:([Service_Prod].[dbo].[MailingAddress].[StatusMailingAddress] AS [ma]), ORDERED FORWARD)
   |    |    |    |--Index Scan(OBJECT:([Service_Prod].[dbo].[Participation].[Reference17] AS [P]))
   |    |    |--Index Scan(OBJECT:([Service_Prod].[dbo].[Delinquent].[aaaaaDelinquent_PK] AS [D]))
   |    |--Table Scan(OBJECT:([Service_Prod].[dbo].[Company] AS [C]))
   |--Stream Aggregate(GROUP BY:([l].[LoanID]))
        |--Filter(WHERE:([Service_Prod].[dbo].[Loan].[LoanID] as [l].[LoanID]=[Service_Prod].[dbo].[Loan].[LoanID] as [L].[LoanID]))
             |--Hash Match(Inner Join, HASH:([b].[LoanID], [b].[AssmRecCounter], [b].[BorrowerID])=([bp].[LoanID], [bp].[AssmRecCounter], [bp].[BorrowerID]), RESIDUAL:([Service_Prod].[dbo].[BorrowerPhone].[LoanID] as [bp].[LoanID]=[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID] AND [Service_Prod].[dbo].[Borrower].[AssmRecCounter] as [b].[AssmRecCounter]=[Service_Prod].[dbo].[BorrowerPhone].[AssmRecCounter] as [bp].[AssmRecCounter] AND [Service_Prod].[dbo].[Borrower].[BorrowerID] as [b].[BorrowerID]=[Service_Prod].[dbo].[BorrowerPhone].[BorrowerID] as [bp].[BorrowerID]))
                  |--Hash Match(Inner Join, HASH:([Service_Prod].[dbo].[Company].[CompanyKey])=([l].[CompanyKey]))
                  |    |--Index Scan(OBJECT:([Service_Prod].[dbo].[Company].[aaaaaCompany_PK]))
                  |    |--Merge Join(Inner Join, MERGE:([s].[LoanID])=([b].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Status].[LoanID] as [s].[LoanID]=[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID]))
                  |         |--Index Seek(OBJECT:([Service_Prod].[dbo].[Status].[_dta_index_Status_114_1122103038__K13_K1_K2_K3] AS [s]), SEEK:([s].[PrimStat]=(1)) ORDERED FORWARD)
                  |         |--Merge Join(Inner Join, MERGE:([l].[LoanID])=([b].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Loan].[LoanID] as [l].[LoanID]=[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID] AND [Service_Prod].[dbo].[Loan].[AssmRecCounter] as [l].[AssmRecCounter]=[Service_Prod].[dbo].[Borrower].[AssmRecCounter] as [b].[AssmRecCounter]))
                  |              |--Index Scan(OBJECT:([Service_Prod].[dbo].[Loan].[_dta_index_Loan_K1_K66_K10_K23] AS [l]), ORDERED FORWARD)
                  |              |--Index Scan(OBJECT:([Service_Prod].[dbo].[Borrower].[aaaaaBorrower_PK] AS [b]), ORDERED FORWARD)
                  |--Index Seek(OBJECT:([Service_Prod].[dbo].[BorrowerPhone].[_dta_index_BorrowerPhone_K12_K1_K2_K3_K5] AS [bp]), SEEK:([bp].[ForeignPhone]=(0)),  WHERE:([Service_Prod].[dbo].[BorrowerPhone].[PhoneType] as [bp].[PhoneType]=(0) OR [Service_Prod].[dbo].[BorrowerPhone].[PhoneType] as [bp].[PhoneType]=(1)) ORDERED FORWARD)

第二次查询的showplan:

  |--Parallelism(Gather Streams)
   |--Hash Match(Left Outer Join, HASH:([L].[LoanID])=([l].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Loan].[LoanID] as [l].[LoanID]=[Service_Prod].[dbo].[Loan].[LoanID] as [L].[LoanID]))
        |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([L].[LoanID]))
        |    |--Hash Match(Inner Join, HASH:([Pr].[LoanID])=([D].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Delinquent].[LoanID] as [D].[LoanID]=[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]))
        |         |--Bitmap(HASH:([Pr].[LoanID]), DEFINE:([Bitmap1065]))
        |         |    |--Hash Match(Inner Join, HASH:([Pr].[LoanID])=([P].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Participation].[LoanID] as [P].[LoanID]=[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]))
        |         |         |--Bitmap(HASH:([Pr].[LoanID]), DEFINE:([Bitmap1064]))
        |         |         |    |--Hash Match(Inner Join, HASH:([S].[LoanID])=([Pr].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Status].[LoanID] as [S].[LoanID]=[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]))
        |         |         |         |--Bitmap(HASH:([S].[LoanID]), DEFINE:([Bitmap1063]))
        |         |         |         |    |--Hash Match(Inner Join, HASH:([S].[LoanID])=([L].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Loan].[LoanID] as [L].[LoanID]=[Service_Prod].[dbo].[Status].[LoanID] as [S].[LoanID]))
        |         |         |         |         |--Bitmap(HASH:([S].[LoanID]), DEFINE:([Bitmap1062]))
        |         |         |         |         |    |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([S].[LoanID]))
        |         |         |         |         |         |--Index Seek(OBJECT:([Service_Prod].[dbo].[Status].[IDX_Status_PrimStat_INCLoanID] AS [S]), SEEK:([S].[PrimStat]=(1)) ORDERED FORWARD)
        |         |         |         |         |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([L].[LoanID]), WHERE:(PROBE([Bitmap1062],[Service_Prod].[dbo].[Loan].[LoanID] as [L].[LoanID])))
        |         |         |         |              |--Nested Loops(Inner Join, WHERE:([Expr1053]<=[Service_Prod].[dbo].[Company].[TransPostDate] as [C].[TransPostDate]))
        |         |         |         |                   |--Parallelism(Distribute Streams, RoundRobin Partitioning)
        |         |         |         |                   |    |--Table Scan(OBJECT:([Service_Prod].[dbo].[Company] AS [C]))
        |         |         |         |                   |--Compute Scalar(DEFINE:([Expr1053]=dateadd(day,(16),[Service_Prod].[dbo].[Loan].[DueDate] as [L].[DueDate])))
        |         |         |         |                        |--Index Scan(OBJECT:([Service_Prod].[dbo].[Loan].[_dta_index_Loan_K1_K66_K10_K23] AS [L]))
        |         |         |         |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([Pr].[LoanID]))
        |         |         |              |--Index Scan(OBJECT:([Service_Prod].[dbo].[Property].[aaaaaProperty_PK] AS [Pr]),  WHERE:(PROBE([Bitmap1063],[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID])))
        |         |         |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([P].[LoanID]))
        |         |              |--Index Scan(OBJECT:([Service_Prod].[dbo].[Participation].[Reference17] AS [P]),  WHERE:(PROBE([Bitmap1064],[Service_Prod].[dbo].[Participation].[LoanID] as [P].[LoanID])))
        |         |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([D].[LoanID]))
        |              |--Index Scan(OBJECT:([Service_Prod].[dbo].[Delinquent].[aaaaaDelinquent_PK] AS [D]),  WHERE:(PROBE([Bitmap1065],[Service_Prod].[dbo].[Delinquent].[LoanID] as [D].[LoanID])))
        |--Stream Aggregate(GROUP BY:([l].[LoanID]))
             |--Sort(ORDER BY:([l].[LoanID] ASC))
                  |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([l].[LoanID]))
                       |--Hash Match(Inner Join, HASH:([b].[LoanID], [b].[AssmRecCounter], [b].[BorrowerID])=([bp].[LoanID], [bp].[AssmRecCounter], [bp].[BorrowerID]), RESIDUAL:([Service_Prod].[dbo].[BorrowerPhone].[LoanID] as [bp].[LoanID]=[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID] AND [Service_Prod].[dbo].[Borrower].[AssmRecCounter] as [b].[AssmRecCounter]=[Service_Prod].[dbo].[BorrowerPhone].[AssmRecCounter] as [bp].[AssmRecCounter] AND [Service_Prod].[dbo].[Borrower].[BorrowerID] as [b].[BorrowerID]=[Service_Prod].[dbo].[BorrowerPhone].[BorrowerID] as [bp].[BorrowerID]))
                            |--Bitmap(HASH:([b].[LoanID], [b].[AssmRecCounter], [b].[BorrowerID]), DEFINE:([Bitmap1068]))
                            |    |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([b].[LoanID], [b].[AssmRecCounter], [b].[BorrowerID]))
                            |         |--Hash Match(Inner Join, HASH:([s].[LoanID], [l].[AssmRecCounter])=([b].[LoanID], [b].[AssmRecCounter]), RESIDUAL:([Service_Prod].[dbo].[Status].[LoanID] as [s].[LoanID]=[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID] AND [Service_Prod].[dbo].[Loan].[AssmRecCounter] as [l].[AssmRecCounter]=[Service_Prod].[dbo].[Borrower].[AssmRecCounter] as [b].[AssmRecCounter]))
                            |              |--Bitmap(HASH:([s].[LoanID], [l].[AssmRecCounter]), DEFINE:([Bitmap1067]))
                            |              |    |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([s].[LoanID], [l].[AssmRecCounter]))
                            |              |         |--Hash Match(Inner Join, HASH:([Service_Prod].[dbo].[Company].[CompanyKey])=([l].[CompanyKey]))
                            |              |              |--Parallelism(Distribute Streams, Broadcast Partitioning)
                            |              |              |    |--Index Scan(OBJECT:([Service_Prod].[dbo].[Company].[aaaaaCompany_PK]))
                            |              |              |--Hash Match(Inner Join, HASH:([s].[LoanID])=([l].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Loan].[LoanID] as [l].[LoanID]=[Service_Prod].[dbo].[Status].[LoanID] as [s].[LoanID]))
                            |              |                   |--Bitmap(HASH:([s].[LoanID]), DEFINE:([Bitmap1066]))
                            |              |                   |    |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([s].[LoanID]))
                            |              |                   |         |--Index Seek(OBJECT:([Service_Prod].[dbo].[Status].[IDX_Status_PrimStat_INCLoanID] AS [s]), SEEK:([s].[PrimStat]=(1)) ORDERED FORWARD)
                            |              |                   |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([l].[LoanID]))
                            |              |                        |--Index Scan(OBJECT:([Service_Prod].[dbo].[Loan].[_dta_index_Loan_K1_K66_K10_K23] AS [l]),  WHERE:(PROBE([Bitmap1066],[Service_Prod].[dbo].[Loan].[LoanID] as [l].[LoanID])))
                            |              |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([b].[LoanID], [b].[AssmRecCounter]))
                            |                   |--Index Scan(OBJECT:([Service_Prod].[dbo].[Borrower].[aaaaaBorrower_PK] AS [b]),  WHERE:(PROBE([Bitmap1067],[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID],[Service_Prod].[dbo].[Borrower].[AssmRecCounter] as [b].[AssmRecCounter])))
                            |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([bp].[LoanID], [bp].[AssmRecCounter], [bp].[BorrowerID]))
                                 |--Index Seek(OBJECT:([Service_Prod].[dbo].[BorrowerPhone].[_dta_index_BorrowerPhone_K12_K1_K2_K3_K5] AS [bp]), SEEK:([bp].[ForeignPhone]=(0)),  WHERE:(([Service_Prod].[dbo].[BorrowerPhone].[PhoneType] as [bp].[PhoneType]=(0) OR [Service_Prod].[dbo].[BorrowerPhone].[PhoneType] as [bp].[PhoneType]=(1)) AND PROBE([Bitmap1068],[Service_Prod].[dbo].[BorrowerPhone].[LoanID] as [bp].[LoanID],[Service_Prod].[dbo].[BorrowerPhone].[AssmRecCounter] as [bp].[AssmRecCounter],[Service_Prod].[dbo].[BorrowerPhone].[BorrowerID] as [bp].[BorrowerID])) ORDERED FORWARD)

3 个答案:

答案 0 :(得分:3)

这听起来很愚蠢,但尝试各种组合并添加所有连接条件,以便为优化器提供匹配的最佳机会。我已经看到这有助于优化者选择一个更好的计划,仅仅是因为表格和基数的顺序。

SELECT ac.AccountNum
    FROM dbo.Account AS ac
    INNER JOIN dbo.Address AS ad
      ON ad.AccountNum = ac.AccountNum
    INNER JOIN dbo.PhoneView AS p
      ON p.AccountNum = ac.AccountNum
      AND p.AccountNum = ad.AccountNum; -- extra help here

也尝试相反的顺序:

SELECT ac.AccountNum
    FROM dbo.Account AS ac
    INNER JOIN dbo.PhoneView AS p
      ON p.AccountNum = ac.AccountNum
    INNER JOIN dbo.Address AS ad
      ON ad.AccountNum = p.AccountNum
      AND ad.AccountNum = ac.AccountNum; -- extra help here

我已经为每个表添加了dbo.前缀(您应该get in the habit of doing),看看是否明确指出那些优化器的决定(或至少防止重复计划),并使用更短的别名是为了便于阅读。

另外不要忘记检查执行计划 - 可能是因为某些表的大小/基数,你得到了错误的连接类型。

编辑现在我们有了查询计划,我们可以看到糟糕的计划在它上面都有并行性。您可以尝试将MAXDOP 1添加到查询中,但这只是暂时的缓解,不应该是真正的修复。我怀疑你看到了不良的基数估计和跨多个线程的不正确的工作分配(通常归咎于CXPACKET等待,但他们的症状不是原因)。我会检查查询中各种表的统计信息。如果您在免费的SQL Sentry Plan Explorer中加载实际的执行计划,那么在Plan Tree选项卡上,您将能够立即看到估计的和实际的行计数在哪里可怕地关闭,这将指导您到哪个表需要statistics updated。至少,这可能是大约90%的时间都表现出可怕的并行性的原因。

一些建议。由于公司只有一行,因此绝对不需要在查询中包含公司。相反,将截止日期拉为变量,减去16天,现在可以使用Loan.DueDate上的索引(如果存在):

DECLARE @d SMALLDATETIME;

SELECT @d = DATEADD(DAY, -16, TransPostDate) FROM dbo.Company;

SELECT L.LoanID
FROM dbo.Loan AS L
  INNER JOIN dbo.[Status] AS S                 ON L.LoanID  = S.LoanID
  INNER JOIN dbo.Participation AS P            ON L.LoanID  = P.LoanID
  INNER JOIN dbo.Delinquent AS D               ON L.LoanID  = D.LoanID
  INNER JOIN dbo.Property AS Pr                ON Pr.LoanID = L.LoanID
  INNER JOIN dbo.MailingAddress AS ma          ON ma.LoanID = L.LoanID  
  LEFT OUTER JOIN dbo.BorrowerPhonePivot AS bp ON bp.loanid = L.loanid
WHERE 
  s.primstat = 1 
  AND L.DueDate <= @cutoff;

由于您只返回LoanID,这不是同样的事情吗?

DECLARE @d SMALLDATETIME;

SELECT @d = DATEADD(DAY, -16, TransPostDate) FROM dbo.Company;

SELECT L.LoanID
FROM dbo.Loan AS L
  WHERE L.DueDate <= @cutoff
  AND EXISTS (SELECT 1 FROM dbo.Status         WHERE LoanID = L.LoanID AND primstat = 1)
  AND EXISTS (SELECT 1 FROM dbo.Participation  WHERE LoanID = L.LoanID)
  AND EXISTS (SELECT 1 FROM dbo.Delinquent     WHERE LoanID = L.LoanID)
  AND EXISTS (SELECT 1 FROM dbo.Property       WHERE LoanID = L.LoanID)
  AND EXISTS (SELECT 1 FROM dbo.MailingAddress WHERE LoanID = L.LoanID);

这只是一个开始,当然不会解决你所有的问题。我不得不相信你并不真的想要一些连接表中的所有行。

答案 1 :(得分:2)

我有两点建议:

第一种方法是使用SQL Server Database Engine Tuning Advisor检查建议。它可能会检测需要添加的索引,这些索引会提高性能,而且不明显。

第二个选项只有在你可以处理稍微过时的数据时才会起作用,或者你可以触发我要描述的数据更新时发生的过程。它赢了“适用于定期更新的数据(每小时几次)。在我们的例子中,如果数据在24小时内准确就可以了,所以我们可以运行我即将每天描述一次的过程。如果你没有那种奢侈,请忽略其余部分。

我们遇到了类似的情况,无论我们做了什么,我们都无法提高性能。在很多大桌子上,连接太多了。从视图中返回431行花了五分钟左右。

因此,我们从BI(商业智能)团队的方法中抽取了一页,并根据我们的规范化数据构建了非规范化表。我们基本上创建了一个迷你版 - Data Warehouse

我们的视图(需要永远运行)被命名为“vw_StoreData”,我们使用语句将其导出

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tmpCompiledStoreData]') AND type in (N'U'))
DROP TABLE [dbo].[tmpCompiledStoreData]


Select * Into tmpCompiledStoreData FROM vw_StoreData

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[CompiledStoreData]') AND type in (N'U'))
DROP TABLE [dbo].[CompiledStoreData]


exec sp_rename 'tmpCompiledStoreData', 'CompiledStoreData'

GRANT SELECT ON [dbo].[CompiledStoreData] TO [SomeUser]
GRANT SELECT ON [dbo].[CompiledStoreData] TO [SomeOtheruser]
...etc

与视图相比,生成的表CompiledStoreData“可以快速查询。再次,只有在您的数据新鲜度需求不是实时的情况下,才能从视图创建非规范化表格。” >

答案 2 :(得分:2)

我怀疑@AaronBertrand对于过时的统计数据是正确的。

我要做的一件事就是删除一个隐式连接。我注意到当你混合使用隐式和显式连接时会发生不可思议的事情(虽然在结果集中比在性能上更多但是它仍然可以通过使优化器选择不是最优的东西来提高复杂查询的性能)。

我还想警告你,视图可能会导致缓慢,特别是如果它们是调用视图的视图。通过查看视图并查看是否可以使用查询中的直接代码替换它们,我看到查询加速了很多。如果每个视图调用相同的基表并且视图调用视图,则尤其如此。因此,如果更新统计信息没有帮助,请查看替换视图。