DB2查询以查找1年前每个项目的平均销售额

时间:2016-06-11 03:36:55

标签: sql db2

在确定如何进行查询时遇到一些麻烦。

一般来说,我有一张表

  • sales_ID
  • Employee_ID
  • sale_date
  • SALE_PRICE

我想要做的是有一个视图,显示每个销售项目员工平均在sale_date之前的1年内销售的数量。

示例:假设我在销售表中有这个

sales_ID    employee_id    sale_date    sale_price
1           Bob            2016/06/10    100
2           Bob            2016/01/01    75
3           Bob            2014/01/01    475
4           Bob            2015/12/01    100
5           Bob            2016/05/01    200
6           Fred           2016/01/01    30
7           Fred           2015/05/01    50

对于sales_id 1记录我想将Bob的所有销售额从销售月份提取1年(因此2015-05-01至2016-05-31,其中有3个销售为75,100,200)所以最终的输出是

sales_ID    employee_id    sale_date    sale_price    avg_sale
1           Bob            2016/06/10    100          125
2           Bob            2016/01/01    75           275
3           Bob            2014/01/01    475          null
4           Bob            2015/12/01    100          475
5           Bob            2016/05/01    200          87.5
6           Fred           2016/01/01    30           50
7           Fred           2015/05/01    50           null

我尝试做的是这样的事情

select a.sales_ID, a.sale_price, a.employee_ID, a.sale_date, b.avg_price
from sales a
left join (
     select employee_id, avg(sale_price) as avg_price
     from sales 
     where sale_date between Date(VARCHAR(YEAR(a.sale_date)-1) ||'-'|| VARCHAR(MONTH(a.sale_date)-1) || '-01')
                  and Date(VARCHAR(YEAR(a.sale_date)) ||'-'|| VARCHAR(MONTH(a.sale_date)) || '-01') -1 day
    group by employee_id
) b on a.employee_id = b.employee_id

DB2不喜欢在子查询中使用父表a,但我不能想到如何正确编写此查询。有什么想法吗?

2 个答案:

答案 0 :(得分:1)

确定。我想我明白了。请注意3件事。

  1. 我无法在DB2中测试它,所以我使用了Oracle。但语法或多或少相同。
  2. 我没有完全使用你的1年逻辑。我正在计算current_date减去365天,但您可以更改内部查询中的between子句中的where部分,如问题中所述。
  3. 您提到的预期输出不正确。因此,对于每个sale_id,我取了日期,发现employee_id,将该员工的所有销售额保留了最近1年​​,不包括当前日期,然后取平均值。如果要更改它,可以在子查询中更改where子句。

    select t1.*,t2.avg_sale 
    from 
    sales t1
    left join 
    (
        select a.sales_id
        ,avg(b.sale_price) as avg_sale
        from sales a
            inner join 
        sales b
         on a.employee_id=b.employee_id
            where b.sale_date between  a.sale_date - 365 and  a.sale_date -1
        group by a.sales_id
    ) t2
    on t1.sales_id=t2.sales_id
    order by t1.sales_id
    
  4. 输出

    +----------+-------------+-------------+------------+----------+
    | SALES_ID | EMPLOYEE_ID |  SALE_DATE  | SALE_PRICE | AVG_SALE |
    +----------+-------------+-------------+------------+----------+
    |        1 | Bob         | 10-JUN-2016 |        100 | 125      |
    |        2 | Bob         | 01-JAN-2016 |         75 | 100      |
    |        3 | Bob         | 01-JAN-2014 |        475 |          |
    |        4 | Bob         | 01-DEC-2015 |        100 |          |
    |        5 | Bob         | 01-MAY-2016 |        200 | 87.5     |
    |        6 | Fred        | 01-JAN-2016 |         30 | 50       |
    |        7 | Fred        | 01-MAY-2015 |         50 |          |
    +----------+-------------+-------------+------------+----------+
    

答案 1 :(得分:1)

您可以通过LATERAL加入几乎修复原始查询。 Lateral允许您引用先前声明的表,如:

select a.sales_ID, a.sale_price, a.employee_ID, a.sale_date, b.avg_price
from sales a
left join LATERAL (
     select employee_id, avg(sale_price) as avg_price
     from sales 
     where sale_date between Date(VARCHAR(YEAR(a.sale_date)-1) ||'-'|| VARCHAR(MONTH(a.sale_date)-1) || '-01')
                  and Date(VARCHAR(YEAR(a.sale_date)) ||'-'|| VARCHAR(MONTH(a.sale_date)) || '-01') -1 day
    group by employee_id
) b on a.employee_id = b.employee_id

但是,我从日期算术中得到语法错误,所以使用@Utsav解决方案会产生这样的结果:

select a.sales_ID, a.sale_price, a.employee_ID, a.sale_date, b.avg_price
from sales a
left join lateral (
    select employee_id, avg(sale_price) as avg_price
    from sales b
    where a.employee_id = b.employee_id 
     and b.sale_date between  a.sale_date - 365 and a.sale_date -1
    group by employee_id
) b on a.employee_id = b.employee_id

由于我们已经在LATERAL连接中推送了谓词,因此严格来说不必使用on子句:

select a.sales_ID, a.sale_price, a.employee_ID, a.sale_date, b.avg_price
from sales a
left join lateral (
    select employee_id, avg(sale_price) as avg_price
    from sales b
    where a.employee_id = b.employee_id 
     and b.sale_date between  a.sale_date - 365 and a.sale_date -1
    group by employee_id
) b on 1=1

通过使用LATERAL联接,我们删除了对sales表的一次访问。计划的比较显示:

No LATERAL Join

访问计划:

        Total Cost:             20,4571
        Query Degree:           1

            Rows 
           RETURN
           (   1)
            Cost 
             I/O 
             |
              7 
           >MSJOIN
           (   2)
           20,4565 
              3 
         /---+----\
        7        0,388889 
     TBSCAN       FILTER
     (   3)       (   6)
     6,81572      13,6402 
        1            2 
       |            |
        7         2,72222 
     SORT         GRPBY 
     (   4)       (   7)
     6,81552      13,6397 
        1            2 
       |            |
        7         2,72222 
     TBSCAN       TBSCAN
     (   5)       (   8)
     6,81488      13,6395 
        1            2 
       |            |
        7         2,72222 
 TABLE: LELLE     SORT  
      SALES       (   9)
       Q6         13,6391 
                     2 
                    |
                  2,72222 
                  HSJOIN
                  (  10)
                  13,6385 
                     2 
              /-----+------\
             7                7 
          TBSCAN           TBSCAN
          (  11)           (  12)
          6,81488          6,81488 
             1                1 
            |                |
             7                7 
      TABLE: LELLE     TABLE: LELLE   
           SALES            SALES
            Q2               Q1

LATERAL加入

访问计划:

    Total Cost:         13,6565
    Query Degree:       1

            Rows
           RETURN
           (   1)
            Cost
             I/O
             |
              7
          >^NLJOIN
          (   2)
           13,6559
              2
         /---+----\
        7          0,35
     TBSCAN       GRPBY
     (   3)       (   4)
     6,81488      6,81662
        1            1
       |            |
        7          0,35
 TABLE: LELLE     TBSCAN
      SALES       (   5)
       Q5         6,81656
                     1
                    |
                     7
              TABLE: LELLE
                   SALES
                    Q1

具有框架的窗口功能

DB2还不支持日期范围框架,但是使用@mustaccio的巧妙技巧:

https://dba.stackexchange.com/questions/141263/what-is-the-meaning-of-order-by-x-range-between-n-preceding-if-x-is-a-dat

我们实际上只能使用一个表访问并解决问题:

select a.sales_ID, a.sale_price, a.employee_ID, a.sale_date
     , avg(sale_price) over (partition by employee_id 
                             order by julian_day(a.sale_date) 
                             range between 365 preceding
                                       and 1 preceding
                            ) as avg_price 
from sales a 

访问计划:

    Total Cost:             6.8197
    Query Degree:           1

      Rows 
     RETURN
     (   1)
      Cost 
       I/O 
       |
        7 
     TBSCAN
     (   2)
     6.81753 
        1 
       |
        7 
     SORT  
     (   3)
     6.81703 
        1 
       |
        7 
     TBSCAN
     (   4)
     6.81488 
        1 
       |
        7 
 TABLE: LELLE   
      SALES
       Q1