首先,感谢任何提示或建议。我不是程序员,但我也没有任何其他方式来访问我的数据进行分析,所以我一直在学习(大多数是通过搜索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的执行计划:
答案 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_DATE
和ID
。
另一件可能花费很多的事情是你生成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中组合所需的数字,或者在一组较小的数据库表中,您可以使用其他数据作为表进行更新成长。
希望其中一些有帮助。