目前我正在考虑使用SQL Query解决下面描述的问题。但是我还没有能够提出一个非常令人满意的解决方案。 我总是可以在查询数据库的程序中解决问题,但是首选SQL服务器端解决方案(因为它是大量数据,服务器比我的计算机快得多)。
方案(MS SQL Server 2008): 两个表,一个称为“预算”,另一个称为“里程碑”。预算包含季度花费,里程碑表包含...惊喜,里程碑。
Relation of table budget: (year, quarter, spent_money)
year | quarter | spent_money
-----|---------|------------
2014 | 1 | 1000 $
2014 | 2 | 2000 $
2014 | 3 | 1500 $
2014 | 4 | 1000 $
Relation of table milestones (name, date)
name | date
-----|-----------
MS1 | 2014-01-31
MS2 | 2014-02-28
MS3 | 2014-08-31
现在我想知道,我为每个里程碑花了多少钱。我假设每个季度的资金都是平均花费的。 示例:第1季度大约三分之一的资金将用于里程碑1,另外三分之一用于里程碑2。最后一个加上所有第二季度和第三季度的某些部分属于里程碑3。预期结果:
Milestone | spent_money
----------|------------
MS1 | 30/90 * 1000 $
MS2 | 28/90 * 1000 $
MS3 | 31/90 * 1000 $ + 2000 $ + 31/92 * 1500
将属于一个里程碑的季度天数除以该季度的总天数给出了一个因子,可以乘以每季度的spend_money。
到目前为止我的想法:
创建一个虚拟表,其中包含所有相关季度的所有日期(和日期)以及因子1 / [季度的天数],然后加入预算表并将钱与因子相乘。最后一步是加入里程碑表并使用SUM()
函数按里程碑进行分组,这会产生结构性问题:
我必须加入里程碑表,例如ON budget_per_day.day <= milestones.date AND budget_per_day.day >= milestones.[previous date]
。一种解决方案可以是为里程碑关系添加开始日期,或者首先将里程碑加入自身,并将上一个里程碑的日期作为下一个里程碑的开始日期。
生成的查询如下所示:
SELECT milestones.name,SUM(alldays.factor*budget.spent_money) FROM
((SELECT 2014-01-01 AS day_date, 1 AS day_id, 1/90 AS factor
UNION SELECT 2014-01-02, 2, 1/90
...
UNION SELECT 2014-08-15, 2) AS alldays
LEFT JOIN budget ON alldays.date>budget.quarter_start_date AND alldays.date<=budget.quarter_end_date)
LEFT JOIN milestones ON alldays.date>milestones.start_date AND alldays.date<=date
GROUP BY milestones.name
高度赞赏任何提高性能的想法。
斯文
答案 0 :(得分:0)
经过几个小时的研究,我想出了以下解决方案。希望我没有破坏任何东西,同时将查询减少到其重要的核心。我很确定那里有更好的解决方案,但现在它对我有用(4分钟内有10 Mio)。我没有为actual_per_day列包含SUM()。
WITH sample AS (
SELECT CAST('2010-01-01' AS date) AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM sample s
WHERE DATEADD(dd, 1, dt) <= CAST('2014-12-31' AS date)),
/****************************************************/
cte_gates AS (
SELECT
rownum = ROW_NUMBER() OVER (ORDER BY project,milestone),
*
FROM milestones
)
/****************************************************/
SELECT dt,b.project,b.quarter_start,quarter_days,
CAST((b.spent_money) AS money)/CAST((dates.quarter_days) AS money) AS [actual_per_day],
gates.gate,gates.cur_gate_date,gates.gate_start_date
FROM
((SELECT dt,
CAST(DATEADD(q, DATEDIFF(q, 0, dt), 0) AS date) AS [quarter_start],
DATEDIFF(day,CAST(DATEADD(q, DATEDIFF(q, 0, dt), 0) AS date),DATEADD(d, -1, DATEADD(q, DATEDIFF(q, 0, dt) + 1, 0)))+1 AS 'quarter_days'
FROM sample
) dates
LEFT JOIN budget b ON dates.quarter_start=b.quarter_start)
LEFT JOIN
(SELECT cte1.project,cte1.milestone, cte1.date as cur_gate_date,
CAST(DATEADD(DAY,1,cte2.date) AS date) AS gate_start_date
FROM cte_gates cte1
LEFT JOIN cte_gates cte2 ON cte1.rownum=cte2.rownum+1 AND cte1.project=cte2.project) gates ON gates.project=b.project AND dt<=gates.cur_gate_date AND dt>=gates.gate_start_date
ORDER BY b.project,dates.dt ASC
OPTION (MAXRECURSION 10000)