按月统计当前项目

时间:2020-06-22 19:10:27

标签: sql oracle

我正在尝试建立一个月度活动设备计数,根据数据库日志表中的服务区域进行分组。我想我已经走了90%。我有一个月列表,以及已存在的项目总数,并按地区分组。

但是,我还需要像每个月的第一天一样了解每个项目的状态,这是我坚持的部分。例如,项目1在1月位于区域A,但在2月移至区域B。第2项在2月被标记为“无效”,因此不应计为。我现有的查询将始终将区域A中的项目1计数为2,将项目2视为“有效”。

我可以正确显示项目3在3月被删除,而项目4直到4月才出现。我意识到我得到了第一个值,因为我的查询指定了最小日期,但我不确定是否需要更改它以获取所需的日期。

我想我正在寻找一种每月对Max(OperationDate)进行分组的方法。

表格如下:

| EQUIPID | EQUIPNAME | EQUIPACTIVE | DISTRICT | REGION |        OPERATIONDATE | OPERATION |
|---------|-----------|-------------|----------|--------|----------------------|-----------|
|       1 |    Item 1 |           1 |        1 |      A | 2015-01-01T00:00:00Z |       INS |
|       2 |    Item 2 |           1 |        1 |      A | 2015-01-01T00:00:00Z |       INS |
|       3 |    Item 3 |           1 |        1 |      A | 2015-01-01T00:00:00Z |       INS |
|       2 |    Item 2 |           0 |        1 |      A | 2015-02-10T00:00:00Z |       UPD |
|       1 |    Item 1 |           1 |        1 |      B | 2015-02-15T00:00:00Z |       UPD |
|       3 |    (null) |      (null) |   (null) | (null) | 2015-02-21T00:00:00Z |       DEL |
|       1 |    Item 1 |           1 |        1 |      A | 2015-03-01T00:00:00Z |       UPD |
|       4 |    Item 4 |           1 |        1 |      B | 2015-03-10T00:00:00Z |       INS |

还有一个子表,其中包含我关心的属性。它的结构是相似的。不幸的是,由于先前的设计决策,两个表之间的操作没有关联。任何联接都需要使用EquipmentID进行,并且每个日期的重叠状态都要匹配。

当前查询:

--cte to build date list
 WITH calendar (dt) AS
 (SELECT &fromdate from dual
      UNION ALL
    SELECT Add_Months(dt,1)
    FROM calendar
    WHERE dt < &todate)
 
SELECT dt, a.district, a.region, count(*)
FROM
  (SELECT EQUIPID, DISTRICT, REGION, OPERATION, MIN(OPERATIONDATE ) AS FirstOp, deleted.deldate
    FROM Equipment_Log
    LEFT JOIN
      (SELECT EQUIPID,MAX(OPERATIONDATE) as DelDate
        FROM Equipment_Log
        WHERE OPERATION = 'DEL'
        GROUP BY EQUIPID
      ) Deleted
    ON  Equipment_Log.EQUIPID = Deleted.EQUIPID
    WHERE OPERATION <> 'DEL' --AND additional unimportant filters
    GROUP BY EQUIPID,DISTRICT, REGION , OPERATION, deldate
  ) a
  INNER JOIN calendar
  ON  (calendar.dt >= FirstOp AND calendar.dt < deldate) 
    OR (calendar.dt >= FirstOp AND deldate is null)
  LEFT JOIN 
      ( SELECT EQUIPID, MAX(OPERATIONDATE) as latestop
      FROM SpecialEquip_Table_Log
      --where SpecialEquip filters
      group by EQUIPID
      ) SpecialEquip
    ON a.EQUIPID = SpecialEquip.EQUIPID and calendar.dt >= SpecialEquip.latestop
    
GROUP BY dt, district, region
ORDER BY dt, district, region

1 个答案:

答案 0 :(得分:0)

仅对每个ID执行最后一个操作。这就是row_number()和where rn = 1的作用。

我们有日历和数据。制作partitioned join

我假设您需要填写缺少ID条目的月份的值。因此,nvl(lag() ignore nulls)是必需的,因为如果1月份出现某种情况,则2月,3月仍然存在,并且我们需要从最后一个非空行开始的地区,地区值。

现在,您拥有一切值得计数的东西。您提到SpecialEquip_Table_Log的那部分取决于您,因为您左联接了此表,以后不再使用它了,那么它的作用是什么?如果需要,请加入,您有ID。

db<>fiddle

with 
  calendar(mth) as (
    select date '2015-01-01' from dual union all
    select add_months(mth, 1) from calendar where mth < date '2015-05-01'),
  data as (
    select id, dis, reg, dt, op, act
      from (
        select equipid id, district dis, region reg,
               to_char(operationdate, 'yyyy-mm') dt, 
               row_number() 
                   over (partition by equipid, trunc(operationdate, 'month') 
                   order by operationdate desc) rn, 
               operation op, nvl(equipactive, 0) act
          from t)
      where rn = 1 )
select mth, dis, reg, sum(act) cnt
  from (
    select id, mth, 
           nvl(dis, lag(dis) ignore nulls over (partition by id order by mth)) dis, 
           nvl(reg, lag(reg) ignore nulls over (partition by id order by mth)) reg,
           nvl(act, lag(act) ignore nulls over (partition by id order by mth)) act
      from calendar
      left join data partition by (id) on dt = to_char(mth, 'yyyy-mm') )
  group by mth, dis, reg
  having sum(act) > 0
  order by mth, dis, reg

这似乎很复杂,因此请首先分别运行子查询以查看发生了什么。并测试:)希望对您有所帮助。

相关问题