如何加快这个SQL查询

时间:2013-06-18 13:42:31

标签: sql sql-server sql-server-2008 query-optimization

首先,感谢任何提示或建议。我不是程序员,但我也没有任何其他方式来访问我的数据进行分析,所以我一直在学习(大多数是通过搜索StackOverflow和Google)。

因此,以下查询按预期工作,但速度很慢。我认为我有可以优化代码的地方,但我已经在拍摄自己的背面,因为它使它工作,所以我没有想法。关于如何加快速度的任何想法?

基本思想是需要预算数据和ID的实际数据,每个时间的零时间(因此这是一个与时间无关的比较),并计算预算与实际累计绩效的比率。

编辑:使用SQL Server Management Studio 2008 R2,添加了执行计划

注意:表变量仅用于测试代码。全尺寸代码中使用的永久表。

DECLARE @DailyBudget TABLE ( ID varchar(30), D_Date datetime, A float, B float) 
DECLARE @DailyActuals TABLE ( ID varchar(30), D_Date datetime, A float, B float) 

Insert into @DailyActuals (ID, D_Date, A, B) 
Values
('J3PJKFWDBK',  '5/20/2013', 300,1301)
,('J3PJKFWDBK', '5/21/2013', 290,1351)
,('J3PJKFWDBK', '5/23/2013', 283,1320)

Insert into @DailyBudget (ID, D_Date, A, B) 
Values
('J3PJKFWDBK',  '5/1/2013', 263,1401)
,('J3PJKFWDBK', '5/2/2013', 260,1390)
,('J3PJKFWDBK', '5/3/2013', 257,1380)

;WITH Budgets AS
(SELECT ID, D_Date, A, B,
        ROW_NUMBER() OVER(PARTITION BY ID ORDER BY D_DATE ASC) as 'RowNum'  from @DailyBudget where not (A = 0 and B = 0) and D_Date > CONVERT(datetime, '2013-01-01 00:00:00.000', 102)
)
, Actuals AS
(SELECT ID, D_DATE, A, B, 
        ROW_NUMBER() OVER(PARTITION BY ID ORDER BY D_DATE ASC) as 'RowNum'  from @DailyActuals where not (A = 0 and B = 0) and D_Date > CONVERT(datetime, '2013-01-01 00:00:00.000', 102)
)
, BudgetSum AS
(select t1.ID, t1.RowNum, SUM(t2.A) as [A], SUM(t2.B) as [B]
  from Budgets as t1
    inner join Budgets as t2 on t1.RowNum >= t2.RowNum and t1.ID = t2.ID
  group by t1.ID, t1.RowNum, t1.A
)
, ActualSum AS
(select t1.ID, t1.RowNum, SUM(t2.A) as [A], SUM(t2.B) as [B]
  from Actuals as t1
    inner join Actuals as t2 on t1.RowNum >= t2.RowNum and t1.ID = t2.ID
  group by t1.ID, t1.RowNum, t1.A
)
SELECT Budgets.ID, Budgets.D_DATE as [Budget_Date], Actuals.D_DATE as [Actual_Date], 
--A
Budgets.A as [Budget_A], BudgetSum.A as [SumBudget_A], 
Actuals.A as [Actual_A], ActualSum.A as [SumActual_A],
(case BudgetSum.A when 0 then 0 else (ActualSum.A/BudgetSum.A)end) as [A_Ratio],
--B
Budgets.B as [Budget_B], BudgetSum.B as [SumBudget_B], 
Actuals.B as [Actual_B], ActualSum.B as [SumActual_B],
(case BudgetSum.B when 0 then 0 else (ActualSum.B/BudgetSum.B)end) as [B_Ratio]
FROM Budgets 
inner join Actuals on (Actuals.RowNum = Budgets.RowNum and Actuals.ID = Budgets.ID) 
inner join BudgetSum on (Actuals.RowNum = BudgetSum.RowNum and Actuals.ID = BudgetSum.ID)
inner join ActualSum on (Actuals.RowNum = ActualSum.RowNum and Actuals.ID = ActualSum.ID) 
order by Budgets.ID, Budgets.RowNum

SQL Server 2008的执行计划:

http://s11.postimg.org/ierhjgvv7/6_18_2013_10_17_26_AM.jpg

2 个答案:

答案 0 :(得分:1)

有6个表扫描占据了最昂贵查询的18%。这些表扫描全部针对您的表变量@DailyBudget@DailyActual。遗憾的是,您无法在表变量上创建索引,除非它们是创建唯一索引的副作用,但我怀疑这不会对您有所帮助。

您可以在临时表上创建索引,我建议您尝试将代码转换为使用临时表,创建缺少的索引并查看是否有帮助。创建适当的索引也可能有助于您的分类成本占据yoru最昂贵查询的63%。

答案 1 :(得分:1)

我建议,如果您被允许这样做,您可以设置这些表的一些较小版本,并尝试添加其他索引。每个表可能有10,000条记录,ID和D_DATE的值不同,因此您可以获得一些有代表性的数据。也许可以创建一个单独的,较小的数据库,您可以自由地统治。

我怀疑你需要一些额外的索引。例如,以下代码按D_DATE排序(这来自您的预算CTE):

 SELECT ID, D_Date, A, B,
 ROW_NUMBER() OVER(PARTITION BY ID ORDER BY D_DATE ASC) as 'RowNum'  
 from @DailyBudget 
 where not (A = 0 and B = 0) 
     and D_Date > CONVERT(datetime, '2013-01-01 00:00:00.000', 102)

尝试使用相同的列创建第二个非主要索引,但顺序为D_DATEID

另一件可能花费很多的事情是你生成RowNum然后对其进行分组,这要求查询引擎按RowNum顺序对所有这些记录进行排序。我会尝试这样的事情:

 WITH Budgets AS
  (SELECT ID, D_Date, A, B
   from @DailyBudget 
   where not (A = 0 and B = 0) 
   and D_Date > CONVERT(datetime, '2013-01-01 00:00:00.000', 102)
 )
, BudgetSum AS
 (select t1.ID, T1.d_date, SUM(t2.A) as [A], SUM(t2.B) as [B]
  from Budgets as t1
  inner join Budgets as t2 on t1.D_DATE >= t2.D_DATE and t1.ID = t2.ID
 group by t1.ID, T1.D_DATE
)

它几乎相同,但它利用了您已有的索引(主键)而不需要计算,然后按RowNum排序。

最后,您用来按日期获取YTD数据的技术是完全有效的,但由于您的表有数百万条记录,您可能正在谈论数十亿条连接记录要处理。这需要很长时间,这并不奇怪!考虑使用一些临时表来保存数据的子集,而不是一次性处理进入最终数字的每个记录。或者对查询进行分区(按日期或按ID范围),以便您可以多次运行更快的查询并在Excel中组合所需的数字,或者在一组较小的数据库表中,您可以使用其他数据作为表进行更新成长。

希望其中一些有帮助。