自定义金额计算

时间:2016-03-04 09:43:50

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

我有下表

Date      Value promo   item
01/01/2011  626 0   1230
01/02/2011  231 1   1230
01/03/2011  572 1   1230
01/04/2011  775 1   1230
01/05/2011  660 1   1230
01/06/2011  662 1   1230
01/07/2011  541 1   1230
01/08/2011  849 1   1230
01/09/2011  632 1   1230
01/10/2011  906 1   1230
01/11/2011  961 1   1230
01/12/2011  361 0   1230
01/01/2012  461 0   1230
01/02/2012  928 1   1230
01/03/2012  855 0   1230
01/04/2012  605 0   1230
01/05/2012  83  0   1230
01/06/2012  44  0   1230
01/07/2012  382 0   1230
01/08/2012  862 0   1230
01/09/2012  549 0   1230
01/10/2012  632 0   1230
01/11/2012  2   0   1230
01/12/2012  26  0   1230

我尝试计算最小日期和最长日期之间的平均总和(SoldAmt)/数字od天数,按照文章

回滚促销= 1的前28行(4周)

无论日折扣如何,平滑期均为4周。

也就是说,如果此项目在过去4周内推广一周,则平滑时间超过5周,而不考虑促销周。

如何计算促销= 1的前4周/ 28周数据顺序?

我试试

         CREATE TABLE #RollingTotalsExample
(
    [Date]     DATE 
    ,[Value]   INT
    ,promo float 
    ,item int 
);

INSERT INTO #RollingTotalsExample
SELECT '2011-01-01',626,1,1230
UNION ALL SELECT '2011-02-01',231,1,1230 UNION ALL SELECT '2011-03-01',572,1,1230
UNION ALL SELECT '2011-04-01',775,1,1230 UNION ALL SELECT '2011-05-01',660,1,1230
UNION ALL SELECT '2011-06-01',662,1,1230 UNION ALL SELECT '2011-07-01',541,1,1230
UNION ALL SELECT '2016-08-01',849,1,1230 UNION ALL SELECT '2016-09-01',632,1,1230
UNION ALL SELECT '2016-10-01',906,1,1230 UNION ALL SELECT '2016-11-01',961,1,1230
UNION ALL SELECT '2016-04-01',775,1,1230 UNION ALL SELECT '2016-05-01',660,1,1230
UNION ALL SELECT '2016-06-01',662,1,1230 UNION ALL SELECT '2016-07-01',541,1,1230
UNION ALL SELECT '2016-08-01',849,1,1230 UNION ALL SELECT '2016-09-01',632,1,1230
UNION ALL SELECT '2016-10-01',906,1,1230 UNION ALL SELECT '2016-11-01',961,1,1230
UNION ALL SELECT '2016-12-01',361,0,1230 UNION ALL SELECT '2012-01-01',461,0,1230
UNION ALL SELECT '2012-02-01',928,0,1230 UNION ALL SELECT '2012-03-01',855,0,1230
UNION ALL SELECT '2012-04-01',605,0,1230 UNION ALL SELECT '2012-05-01',83,0,1230
UNION ALL SELECT '2012-06-01',44,0,1230 UNION ALL SELECT '2012-07-01',382,0,1230
UNION ALL SELECT '2012-08-01',862,0,1230 UNION ALL SELECT '2012-09-01',549,0,1230
UNION ALL SELECT '2012-10-01',632,0,1230 UNION ALL SELECT '2012-11-01',2,0,1230
UNION ALL SELECT '2012-12-01',26,0,1230;
 SELECT * FROM #RollingTotalsExample;

    -- Rolling twelve month total by using INNER JOIN
    SELECT a.[Date]
        ,Value=MAX(CASE WHEN a.[Date] = b.[Date] THEN a.Value END)
        ,Rolling12Months=CASE
                            WHEN ROW_NUMBER() OVER (ORDER BY a.[Date]) < 12
                            THEN NULL
                            ELSE SUM(b.Value)
                            END
    FROM #RollingTotalsExample a
    JOIN #RollingTotalsExample b ON b.[Date] BETWEEN DATEADD(month, -11, a.[Date]) AND a.[Date]
    GROUP BY a.[Date]
    ORDER BY a.[Date];

现在如何修改查询以计算最小日期和最大日期之间的平均总和(SoldAmt)/数字od天数,按照文章

回滚促销= 1的28个第一行数据顺序

3 个答案:

答案 0 :(得分:1)

因为没有陈述/显示&#34;预期结果&#34;我猜的很多。虽然我已经阅读了这个问题的文字,the duplicate,但我真的不理解这个描述。有了这些保留,我建议如下:

结果:

| item | promo_start |    max_date |  sum | count | avg |
|------|-------------|-------------|------|-------|-----|
| 1230 | Jan 02 2011 | Jan 12 2011 | 7150 |    11 | 650 |
| 1230 | Jan 02 2012 | Jan 12 2012 | 4968 |    11 | 451 |

由以下查询生成(使用Postgres):

select
      item
    , promo_start
    , max(thedate) max_date
    , sum(value)
    , count(distinct thedate)
    , sum(value) / count(distinct thedate) avg
from (
      select *
      from table1 t1
      cross join lateral (
            select min(t2.thedate) promo_start
            from table1 t2
            where promo = 1
            and t1.item = t2.item
            and t2.thedate <= t1.thedate
            and t2.thedate >= t1.thedate - INTERVAL '28 DAYS'
            ) a
      ) d
where promo_start is NOT NULL
group by
      item
    , promo_start
;

将此视为a demo at sqlfiddle(nb:SQL Server已经/不在该网站上工作数周,因此Postgres代替使用)

注意:对于SQL Server

  1. 而不是`cross join lateral&#39;使用 CROSS APPLY
  2. 而不是t1.thedate - INTERVAL '28 DAYS'使用 dateadd(day,-28,t1.thedate)
  3. 逻辑如下:

    1. 交叉连接横向(交叉应用)子查询定位项目在前28天内促销的最短日期
    2. 对于促销受影响的时段,每行重复最小日期
    3. 仅选择受促销影响的行
    4. 然后项目和促销开始日期用于对所选行进行分组,其中聚合用于计算平均值
    5. 在计算平均值时使用不同天数,因为28天内每天都不会出现销售。
    6. +我用了#34; theDate&#34;作为列名仅仅是因为&#34; date&#34;在代码和描述代码中都太混乱了。我真的不能推荐使用&#34; date&#34;作为列名。

答案 1 :(得分:1)

这是另一种需要LAG()的方法,可以从SQL 2012获得,但请注意,样本数据在每个日期之前不包含“28个不同的日期”。此外,正在使用的实际数据类型是未知的(date / smalldatetime / datetime / datetime2),也不知道是否需要从日期截断时间。因此,有一些警告,这种方法为28个不同的日期创建了一系列日期范围(但是数据不提供这些日期范围,然后它们是28天过去的日期)。这是一个sqlfiddle demo

PostgreSQL 9.3架构设置: (因为SQL Server不在sqlfiddle上运行)

media-type = type "/" subtype *( OWS ";" OWS parameter )

查询1

CREATE TABLE Table1
    (theDate timestamp, Value int, promo int, item int)
;

INSERT INTO Table1
    (theDate, Value, promo, item)
VALUES
    ('2011-01-01 00:00:00', 626, 0, 1230),
    ('2011-01-02 00:00:00', 231, 1, 1230),
    ('2011-01-03 00:00:00', 572, 1, 1230),
    ('2011-01-04 00:00:00', 775, 1, 1230),
    ('2011-01-05 00:00:00', 660, 1, 1230),
    ('2011-01-06 00:00:00', 662, 1, 1230),
    ('2011-01-07 00:00:00', 541, 1, 1230),
    ('2011-01-08 00:00:00', 849, 1, 1230),
    ('2011-01-09 00:00:00', 632, 1, 1230),
    ('2011-01-10 00:00:00', 906, 1, 1230),
    ('2011-01-11 00:00:00', 961, 1, 1230),
    ('2011-01-12 00:00:00', 361, 0, 1230),
    ('2012-01-01 00:00:00', 461, 0, 1230),
    ('2012-01-02 00:00:00', 928, 1, 1230),
    ('2012-01-03 00:00:00', 855, 0, 1230),
    ('2012-01-04 00:00:00', 605, 0, 1230),
    ('2012-01-05 00:00:00', 83, 0, 1230),
    ('2012-01-06 00:00:00', 44, 0, 1230),
    ('2012-01-07 00:00:00', 382, 0, 1230),
    ('2012-01-08 00:00:00', 862, 0, 1230),
    ('2012-01-09 00:00:00', 549, 0, 1230),
    ('2012-01-10 00:00:00', 632, 0, 1230),
    ('2012-01-11 00:00:00', 2, 0, 1230),
    ('2012-01-12 00:00:00', 26, 0, 1230)
;

<强> Results

select
      t1.item
    , ranges.theStart
    , ranges.theEnd
    , sum(t1.value)
    , sum(t1.value) / 28 avg
from (
      select
            coalesce(lag(theDay,28) over(order by theDay) , theDay - INTERVAL '28 DAYS') as theStart
          , theDay as theEnd
      from (
            select distinct cast(thedate as date) theDay from Table1
            ) days
      ) ranges
inner join table1 t1 on theDate between ranges.theStart and ranges.theEnd
group by
      t1.item
    , ranges.theStart
    , ranges.theEnd

注意:对于SQL Server

  • 而不是| item | thestart | theend | sum | avg | |------|----------------------------|---------------------------|------|-----| | 1230 | December, 04 2010 00:00:00 | January, 01 2011 00:00:00 | 626 | 22 | | 1230 | December, 05 2010 00:00:00 | January, 02 2011 00:00:00 | 857 | 30 | | 1230 | December, 06 2010 00:00:00 | January, 03 2011 00:00:00 | 1429 | 51 | | 1230 | December, 07 2010 00:00:00 | January, 04 2011 00:00:00 | 2204 | 78 | | 1230 | December, 08 2010 00:00:00 | January, 05 2011 00:00:00 | 2864 | 102 | | 1230 | December, 09 2010 00:00:00 | January, 06 2011 00:00:00 | 3526 | 125 | | 1230 | December, 10 2010 00:00:00 | January, 07 2011 00:00:00 | 4067 | 145 | | 1230 | December, 11 2010 00:00:00 | January, 08 2011 00:00:00 | 4916 | 175 | | 1230 | December, 12 2010 00:00:00 | January, 09 2011 00:00:00 | 5548 | 198 | | 1230 | December, 13 2010 00:00:00 | January, 10 2011 00:00:00 | 6454 | 230 | | 1230 | December, 14 2010 00:00:00 | January, 11 2011 00:00:00 | 7415 | 264 | | 1230 | December, 15 2010 00:00:00 | January, 12 2011 00:00:00 | 7776 | 277 | | 1230 | December, 04 2011 00:00:00 | January, 01 2012 00:00:00 | 461 | 16 | | 1230 | December, 05 2011 00:00:00 | January, 02 2012 00:00:00 | 1389 | 49 | | 1230 | December, 06 2011 00:00:00 | January, 03 2012 00:00:00 | 2244 | 80 | | 1230 | December, 07 2011 00:00:00 | January, 04 2012 00:00:00 | 2849 | 101 | | 1230 | December, 08 2011 00:00:00 | January, 05 2012 00:00:00 | 2932 | 104 | | 1230 | December, 09 2011 00:00:00 | January, 06 2012 00:00:00 | 2976 | 106 | | 1230 | December, 10 2011 00:00:00 | January, 07 2012 00:00:00 | 3358 | 119 | | 1230 | December, 11 2011 00:00:00 | January, 08 2012 00:00:00 | 4220 | 150 | | 1230 | December, 12 2011 00:00:00 | January, 09 2012 00:00:00 | 4769 | 170 | | 1230 | December, 13 2011 00:00:00 | January, 10 2012 00:00:00 | 5401 | 192 | | 1230 | December, 14 2011 00:00:00 | January, 11 2012 00:00:00 | 5403 | 192 | | 1230 | December, 15 2011 00:00:00 | January, 12 2012 00:00:00 | 5429 | 193 | 使用 dateadd(day,-28,theDay)

答案 2 :(得分:0)

如果您正在寻找除了前12天之外的所有日子的累计金额......

with cte
as
(
select dateadd(day,12,min(date)) as mindate,max(date) as maxdate,
datediff(day,dateadd(day,12,min(date)),max(date)) as n

from #RollingTotalsExample
)
select 
date,
(select sum(value) from #RollingTotalsExample t2 where t2.date<=t1.date) as rollingsum,
promo,
item,
mindate,
maxdate
from 
#RollingTotalsExample t1
join
cte c
on  t1.date >=c.mindate and t1.date<= c.maxdate
order by date

- 这符合您的确切输出

with cte
as
(
select dateadd(day,12,min(date)) as mindate,max(date) as maxdate,
datediff(day,dateadd(day,12,min(date)),max(date)) as n

from #RollingTotalsExample
)
select 
date,
sum(value) as avgsum,
promo,
item
from 
#RollingTotalsExample t1
join
cte c
on  t1.date >=c.mindate and t1.date<= c.maxdate
where promo=1
group by date,promo,item
order by date

---如果您正在寻找平均值/天数

with cte
as
(
select dateadd(day,12,min(date)) as mindate,max(date) as maxdate,
datediff(day,dateadd(day,12,min(date)),max(date)) as n

from #RollingTotalsExample
)
select 
date,
sum(value)/n*1.0 as avgsum,
promo,
item
from 
#RollingTotalsExample t1
join
cte c
on  t1.date >=c.mindate and t1.date<= c.maxdate
where promo=1
group by date,promo,item,n
order by date