优化慢速T-SQL存储过程

时间:2015-07-17 14:49:59

标签: sql-server tsql

我正在为数据库中的表创建存储过程;但是,由于必须执行大量连接,它运行速度极慢。我试图想出一种优化查询的方法,这样我就不需要执行这么多左连接,但是我找不到这样做的方法。我的代码如下所示:

ALTER PROCEDURE [dbo].[STS]
AS
DECLARE @t0 table(Id nvarchar(7), Date1 date, TIV float, [1mo] float, RAN nvarchar(50), SAN nvarchar(50))
INSERT INTO @t0(Id, Date1, TIV, [1mo], RAN, SAN)
SELECT Id, Date1, TIV, TMR, RAN, SAN
FROM dbo.History
WHERE (Date1 IS NOT NULL) AND (Valid IS NULL OR Valid <> 0) AND (include <> 0)

DECLARE @t1 table(Id nvarchar(7), Date1 date, TIV float, [3mo] float, RAN nvarchar(50), SAN nvarchar(50))
INSERT INTO @t1(Id, Date1, TIV, [3mo], RAN, SAN) 
SELECT * FROM dbo.Series(3)

DECLARE @t2 table(Id nvarchar(7), Date1 date, TIV float, [6mo] float, RAN nvarchar(50), SAN nvarchar(50))
INSERT INTO @t2(Id, Date1, TIV, [6mo], RAN, SAN) 
SELECT * FROM dbo.Series(6)

DECLARE @t3 table(Id nvarchar(7), Date1 date, TIV float, [9mo] float, RAN nvarchar(50), SAN nvarchar(50))
INSERT INTO @t3(Id, Date1, TIV, [9mo], RAN, SAN) 
SELECT * FROM dbo.Series(9)


 SELECT t0.*, Join2.[3mo], Join2.[6mo], Join2.[9mo]
 FROM @t0 as t0
 LEFT OUTER JOIN
     (SELECT t1.*, Join1.[6mo], Join1.[9mo]
     FROM @t1 as t1
     LEFT OUTER JOIN
         (SELECT t2.*, t3.[9mo]
         FROM @t2 as t2
         LEFT OUTER JOIN @t3 as t3 
         ON t2.Id = t3.Id AND t2.Date1 = t3.Date1 AND t2.RAN = t3.RAN AND 
             t2.SAN = t3.SAN) as Join1
     ON t1.Id = Join1.Id AND t1.Date1 = Join1.Date1 AND t1.RAN = Join1.RAN AND
             t1.SAN = Join1.SAN) as Join2
 ON t0.Id = Join2.Id AND t0.Date1 = Join2.Date1 AND t0.RAN = Join2.RAN AND
             t0.SAN = Join2.SAN

是否有一种简单的方法可以优化这种慢速查询,或者我是否需要考虑一种新方法来完成此操作?

5 个答案:

答案 0 :(得分:3)

通常由表变量引起的问题是因为没有统计信息,估计的行数是1,这可能导致非常糟糕的连接性能。我建议做的第一件事是尝试将它们更改为临时表。由于你没有提到有多少行,所以很难说这些临时索引是否存在。表格会有所帮助,但这也是你可以尝试的东西。

如果查询仍然很慢,查看统计信息输出以查看I / O计数和查询计划以查看发生的情况应该有助于了解导致问题的原因。

答案 1 :(得分:3)

这似乎过于复杂,表变量对于较大的数据集效率不高。

   SELECT h.Id, h.Date1, h,TIV, h.[1mo], h.RAN, h.SAN, 
                threemonth.somefield as [3mo], sixmonth.somefield as[6mo],  ninemonth.somefield as[9mo]
    FROM dbo.History h
    LEFT OUTER JOIN dbo.Series(3) threemonth 
        ON h.Id = threemonth.Id AND h.Date1 =threemonth.Date1 
            AND h.RAN = threemonth.RAN AND   h.SAN = threemonth.SAN
    LEFT OUTER JOIN dbo.Series(6) sixmonth 
    ON sixmonth.Id = threemonth.Id AND sixmonth.Date1 =threemonth.Date1 
            AND sixmonth.RAN = threemonth.RAN AND  sixmonth.SAN = threemonth.SAN
    LEFT OUTER JOIN dbo.Series(9) ninemonth  
    ON sixmonth.Id = ninemonth.Id AND sixmonth.Date1 =ninemonth.Date1 
            AND sixmonth.RAN = ninemonth.RAN AND   sixmonth.SAN = ninemonth.SAN
    WHERE (h.Date1 IS NOT NULL) 
            AND (h.Valid IS NULL OR h.Valid <> 0) 
            AND (h.include <> 0)

现在它已经简化了,你可以更好地看到痛点。

首先,表函数不会对连接使用任何索引。因此,如果返回大量记录,最好不要使用它。

即使在使用索引时,正在连接的字段效率也不高,因为您正在加入日期和变量以及整数。这可能需要在表结构或表函数中重新设计,以便为连接提供更好的结果。

如果不了解dbo.series()函数的作用,很难做出具体的建议。但是,如果h.Id是唯一的(并且它可能是PK),则可能没有必要执行所有其他连接条件。同样,这取决于在表值函数中发生的工作。我只是根据多年的数据库经验和数百个数据库的知识进行猜测。如果你能以任何方式简单地加入你将获得更好的性能。记住,在尝试简化时,您需要检查每个步骤,以确保您仍然返回相同的结果。

最后你遇到了Where子句的问题。这里你的两个问题是OR条件,它通常可以用UNION ALL替换以获得更好的性能和&lt;&gt;条件。我可以想到一种改进&lt;&gt;的方法问题是填充一个使用可接受值索引的临时表,然后使用它。如果您有数千个可能的值,您可能只会看到性能大幅提升。如果可能的值在查找表中,您可以使用not exists子句连接到该表,以过滤掉0.

答案 2 :(得分:1)

尝试在最终加入结束时添加OPTION(RECOMPILE)

本文解释了很多关于表变量和临时表的内容:https://www.simple-talk.com/sql/t-sql-programming/temporary-tables-in-sql-server/

答案 3 :(得分:1)

您可以尝试在表变量上放置一个主键。像这样:

DECLARE @t1 
table   (
          Id nvarchar(7), 
          Date1 date, 
          TIV float, 
          [3mo] float, 
          RAN nvarchar(50), 
          SAN nvarchar(50),
          Primary Key (Id, Date1, RAN, SAN)
        )
INSERT INTO @t1(Id, Date1, TIV, [3mo], RAN, SAN) 
SELECT * FROM dbo.Series(3)

这会导致表变量中的插入稍慢,但连接速度要快得多。

答案 4 :(得分:0)

不幸的是,你不能真正切断连接,因为你需要它们来获得正确的结果。我会尝试查看连接的内容,然后检查该列是否有索引,如果不看创建一个。这是我真正看到的可能增加查询性能的唯一方法。虽然保留了所做更改的记录,因为某些索引可能导致它运行得更慢。只需保留一些运行时间指标。

相关问题