在SQL中使用CTE和预计销售量计算MTD

时间:2018-11-10 08:43:29

标签: sql sql-server sql-server-2008

我正在尝试在SQL查询中添加MTD Sales。我想出了如何使用JOINS做到这一点,但我想使用CTE来计算MTD销售额,然后使用它来计算projected_sales。预计销售额的公式为(MTD/wkdaysinmonth*wkdaystodate)[也存储在CTE表中)。有没有办法使它变得容易?我写了以下代码;

输入:

Email            PaymentAmount     orderdate
xyz@gmail.com    10                11/01/2018
xyz@gmail.com    20                11/09/2018

示例输出:

EmailAddress      MTD    Projected_sales
xyz@gmail.com      30        0.19

其中,预计销售量是根据天passed=7和11月22日中的工作日总数计算得出的。 {[30/7*22]=0.19}(Present date = 11/09/2018

with dates as(
    select dateadd(d,-day(getdate())+1,convert(date,getdate())) as startofmonth,
    dateadd(d,-1,dateadd(m,1,dateadd(d,-day(getdate())+1,convert(date,getdate())))) as endofmonth,
    convert(date,getdate()) as today
)
,daycounts as(
    select dates.*,

       (DATEDIFF(dd, startofmonth, endofmonth) + 1)
      -(DATEDIFF(wk, startofmonth, endofmonth) * 2)
      -(CASE WHEN DATENAME(dw, startofmonth) = 'Sunday' THEN 1 ELSE 0 END) 
      -(CASE WHEN DATENAME(dw, endofmonth) = 'Saturday' THEN 1 ELSE 0 END) as wkdaysinmonth,

       (DATEDIFF(dd, startofmonth, today) + 1)
      -(DATEDIFF(wk, startofmonth, today) * 2)
      -(CASE WHEN DATENAME(dw, startofmonth) = 'Sunday' THEN 1 ELSE 0 END) 
      -(CASE WHEN DATENAME(dw, today) = 'Saturday' THEN 1 ELSE 0 END) as wkdaystodate

    from dates
) 
SELECT DISTINCT Customers.EmailAddress as email,
o1.YTD
FROM
    Customers
INNER JOIN
        Orders
        ON
        Orders.CustomerID= Customers.CustomerID
JOIN
(SELECT 
c.EmailAddress,
SUM(Orders.PaymentAmount) AS YTD
FROM
Customers c
JOIN 
Orders
ON c.CustomerID=Orders.CustomerID
WHERE
Orders.OrderDate  BETWEEN '01/01/2018 00:00' AND GETDATE()
GROUP BY
EmailAddress) AS o1 ON o1.EmailAddress = Customers.EmailAddress
WHERE
Orders.OrderDate  >= (GETDATE()-7)

2 个答案:

答案 0 :(得分:1)

您可以尝试使用cte递归为orderdate startDate到endDate创建日历表。

然后OUTER JOIN基于日历表,并在子查询中执行条件聚合功能以获取工作日期。

;WITH cte 
     AS (SELECT email, 
                Dateadd(day, 1, Eomonth(Min(orderdate), -1)) minDt, 
                Dateadd(day, 1, Eomonth(Max(orderdate)))     maxDt 
         FROM   t 
         GROUP  BY email 
         UNION ALL 
         SELECT email, 
                Dateadd(day, 1, mindt), 
                maxdt 
         FROM   cte 
         WHERE  Dateadd(day, 1, mindt) < maxdt), 
     cte2 
     AS (SELECT *, 
                Count(CASE 
                        WHEN Datename(dw, t1.mindt) NOT IN ('Sunday', 'Saturday' ) 
                      THEN 
                        1 
                      END) OVER( ORDER BY t1.mindt) workdt 
         FROM   cte t1) 
SELECT t1.email, 
       t2.total, 
       Max(diffdt) / ( Max(workdt) * Max(workdtmax) * 1.0 ) Projected_sales 
FROM   (SELECT *, 
               Max(workdt) 
                 OVER( 
                   partition BY email 
                   ORDER BY workdt DESC) workdtMax, 
               Datediff(day, Min(mindt) OVER(partition BY email ORDER BY workdt) 
               , Max(mindt)  OVER(partition BY email ORDER BY workdt DESC))   + 1      diffdt 
        FROM   cte2) t1 
       LEFT JOIN (SELECT email, 
                         Sum(paymentamount) total, 
                         Min(orderdate)     minDt, 
                         Max(orderdate)     maxDt 
                  FROM   t 
                  GROUP  BY email) t2 
              ON t1.mindt BETWEEN t2.mindt AND t2.maxdt 
                 AND t1.email = t2.email 
WHERE  t2.total IS NOT NULL 
GROUP  BY t1.email, 
          t2.total 

sqlfiddle

重新使用

email           total   Projected_sales
xyz@gmail.com   30      0.19480519480519

答案 1 :(得分:0)

您可以生成一个“日历”表,其中包含一个月中每一天的工作日。

您对预测的计算对我来说没有意义。因此,我还列出了我认为更好的计算方法:

with dates as (
      select distinct dte,
             (case when datename(weekday, dte) not in ('Saturday', 'Sunday') then 1 else 0 end) as num_weekdays,
            dte as month_start
      from t cross apply
           (values (dateadd(day, 1 - day(orderdate), orderdate))) v(dte)
      union all
      select dateadd(day, 1, d.dte),
             (case when datename(weekday, dte) not in ('Saturday', 'Sunday') then 1 else 0 end) + num_weekdays,
             d.month_start
      from dates d
      where dte < dateadd(day, -1, dateadd(month, 1, month_start))
     ),
     d as (
      select d.*, max(num_weekdays) over (partition by month_start) as month_weekdays
      from dates d
     )
select d.month_start, t.email,
       sum(paymentamount) as mtd,
       sum(paymentamount) * max(month_weekdays) / max(d.num_weekdays) as my_projected,
       sum(paymentamount) * 1.0 / (max(month_weekdays) * max(d.num_weekdays)) as your_projected
from t join
     d
     on t.orderdate = d.orderdate
group by d.month_start, t.email;

Here是db <>小提琴。