我正在为数据库中的表创建存储过程;但是,由于必须执行大量连接,它运行速度极慢。我试图想出一种优化查询的方法,这样我就不需要执行这么多左连接,但是我找不到这样做的方法。我的代码如下所示:
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
是否有一种简单的方法可以优化这种慢速查询,或者我是否需要考虑一种新方法来完成此操作?
答案 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)
不幸的是,你不能真正切断连接,因为你需要它们来获得正确的结果。我会尝试查看连接的内容,然后检查该列是否有索引,如果不看创建一个。这是我真正看到的可能增加查询性能的唯一方法。虽然保留了所做更改的记录,因为某些索引可能导致它运行得更慢。只需保留一些运行时间指标。