Select for Opened, Closed and Backlog

三世轮回 提交于 2020-01-06 01:56:05

问题


I have the following table:

+-------------+---------------------+---------------------+
| status      | open_time           | close_time          |
+-------------+---------------------+---------------------+
|      closed | 01-11-2014 19:32:44 | 01-11-2014 20:32:44 |
|        open | 01-12-2014 22:33:49 | 02-12-2014 22:33:49 |
|        open | 01-23-2014 22:08:24 | 03-23-2014 22:08:24 |
|      closed | 02-01-2014 22:33:57 | 03-01-2014 22:33:57 |
|        open | 02-01-2013 22:37:34 | 02-01-2013 23:37:34 |
|      closed | 04-20-2013 15:23:00 | 05-20-2013 15:23:00 |
|        open | 04-20-2013 12:21:49 | 05-20-2013 12:21:49 |
|      closed | 04-25-2013 11:22:00 | 06-25-2013 11:22:00 |
|      closed | 05-20-2013 14:23:49 | 10-20-2013 14:23:49 |
|      closed | 04-20-2013 16:33:49 | 04-25-2013 16:33:49 |
+-------------+---------------------+---------------------*

I would like to show this result:

+-------------+---------------+--------------+---------+
| Year | Month | Opened Cases | Closed Cases | Backlog |
+-------------+---------------+--------------+---------+
| 2014 |     4 |           10 |            5 |      62 | (57 + 5)
| 2014 |     3 |            9 |            7 |      57 | (52 + 2)    
| 2014 |     2 |           15 |           20 |      52 | (57 - 5)
| 2014 |     1 |           12 |            1 |      57 | (46 + 11)
| 2013 |    12 |           10 |            9 |      46 | (45 + 1)
| 2013 |    11 |           50 |            5 |      45 | (45)
+--------------+--------------+--------------+---------+

Actually I am facing 2 situations:

  • Situation #1: I am not able to get the right value for the Closed Cases. Tried to work with clauses in where but no luck.

  • Situation #2: After retrive the Closed Cases, I should do the (Opened - Closed) and accumulate it over the months, so this will be the backlog.

For the Situation #1:

If I do the following select:

SELECT
  YEAR(open_time) AS Ano,
  MONTH(open_time) AS Mes,
  sum(CASE WHEN DATEPART(YYYY, open_time)= 2013 AND DATEPART(MM, open_time)= 11 THEN 1 ELSE 0 END) Abertos,
  sum(CASE WHEN DATEPART(YYYY, close_time)= 2013 AND DATEPART(MM, close_time)= 11 THEN 1 ELSE 0 END) Fechados
FROM
  TABLE
WHERE
  GROUPDESC= 'SUPPORT'
GROUP BY
  MONTH(open_time),
  YEAR(open_time)
ORDER BY
  Ano DESC,
  Mes DESC

I get (this is the right value for closed cases):

+-------------+---------------+--------------+
| Year | Month | Opened Cases | Closed Cases |
+-------------+---------------+--------------+
| 2014 |     4 |            0 |            0 |
| 2014 |     3 |            0 |            0 |
| 2014 |     2 |            0 |            0 |
| 2014 |     1 |            0 |            0 |
| 2013 |    12 |            0 |            0 |
| 2013 |    11 |           50 |            5 |
+--------------+--------------+--------------+

But if I do:

SELECT
  YEAR(open_time) AS Ano,
  MONTH(open_time) AS Mes,
  sum(CASE WHEN DATEPART(YYYY, open_time)= YEAR(open_time) AND DATEPART(MM, open_time)= MONTH(open_time) THEN 1 ELSE 0 END) Abertos,
  sum(CASE WHEN DATEPART(YYYY, close_time)= YEAR(close_time) AND DATEPART(MM, close_time)= YEAR(close_time) THEN 1 ELSE 0 END) Fechados
FROM
  TABLE
WHERE
  GROUPDESC= 'SUPPORT'
GROUP BY
  MONTH(open_time),
  YEAR(open_time)
ORDER BY
  Ano DESC,
  Mes DESC

I get:

+-------------+---------------+--------------+
| Year | Month | Opened Cases | Closed Cases |
+-------------+---------------+--------------+
| 2014 |     4 |            0 |            0 |
| 2014 |     3 |            0 |            0 |
| 2014 |     2 |            0 |            0 |
| 2014 |     1 |            0 |            0 |
| 2013 |    12 |            0 |            0 |
| 2013 |    11 |           50 |           50 |
+--------------+--------------+--------------+

回答1:


Here is one way to approach the problem: select all opening counts per month and all closing counts per month. Then full outer join them. The cumulation is a so-called running total, which you get with SUM OVER.

select
  coalesce(opened.ano, closed.ano) as ano,
  coalesce(opened.mes, closed.mes) as mes,
  coalesce(opened.cnt, 0) as opened_cases,
  coalesce(closed.cnt, 0) as closed_cases,
  sum(coalesce(opened.cnt, 0) - coalesce(closed.cnt, 0)) over (order by coalesce(opened.ano, closed.ano), coalesce(opened.mes, closed.mes)) as backlog
from
(
  select 
    year(open_time) as ano, 
    month(open_time) as mes,
    count(*) as cnt
  from probsummarym1
  where groupdesc = 'SUPPORT'
  group by year(open_time), month(open_time)
) opened
full outer join
(
  select 
    year(close_time) as ano, 
    month(close_time) as mes,
    count(*) as cnt
  from probsummarym1
  where groupdesc = 'SUPPORT'
  and status = 'closed'
  group by year(close_time), month(close_time)
) closed
  on opened.ano = closed.ano and opened.mes = closed.mes
order by coalesce(opened.ano, closed.ano) desc, coalesce(opened.mes, closed.mes) desc;

Here is the SQL fiddle: http://sqlfiddle.com/#!6/68dcf/7.

Another way would be to glue the opening events with the closing events with UNION ALL and then count:

select 
  ano,
  mes,
  opened_cases,
  closed_cases,
  sum(opened_cases - closed_cases) over (order by ano, mes) as backlog
from
(
  select 
    year(fecha) as ano,
    month(fecha) as mes,
    sum(case when evento = 'opened' then 1 else 0 end) as opened_cases,
    sum(case when evento = 'closed' then 1 else 0 end) as closed_cases
  from
  (
    select 'opened' as evento, open_time as fecha
    from probsummarym1
    where groupdesc = 'SUPPORT'
    union all
    select 'closed' as evento, close_time as fecha
    from probsummarym1
    where groupdesc = 'SUPPORT'
    and status = 'closed'
  ) x 
  group by year(fecha), month(fecha)
) y
order by ano desc, mes desc;

And here is the second SQL fiddle: http://sqlfiddle.com/#!6/68dcf/18.

EDIT: No SUM OVER? That's too bad. So you will have to subselect the count. That is slow, because the table must be scanned again for every single month.

For every month we must find all start dates until then and all end dates till then. As an end can never occur before start, we can select all records where the start date matches and count these. Within these records we will also find all potential end dates. We count these (with sum and case), subtract, and we're done.

So you would have to replace the SUM() OVER as backlog part thus:

  (
    select
      count(*)
      -
      sum
      (
        case 
          when eventsuntil.status = 'closed' 
          and year(eventsuntil.close_time) * 100 + month(eventsuntil.close_time) >= 
              y.ano * 100 + y.mes 
        then 1 else 0
        end
      )    
    from probsummarym1 eventsuntil
    where eventsuntil.groupdesc = 'SUPPORT'
    and year(eventsuntil.open_time) * 100 + month(eventsuntil.open_time) >= 
        y.ano * 100 + y.mes
  ) as backlog

However, SQL Server, even in version 2012, is not capable of executing this, which I consider a dbms flaw. y.ano and y.mes (or coalesce(opened.ano, closed.ano) and coalesce(opened.mes, closed.mes) for the first statement) should be considered constants for the inner query, because the evaluation is done per outer record, i.e. month, but they are not. I don't know how to overcome this problem. Maybe some expert on SQL Server can help you here.

Here are the two fiddles that result in syntax errors: http://sqlfiddle.com/#!6/68dcf/32 and http://sqlfiddle.com/#!6/68dcf/31. Sorry I cannot help you here any further.




回答2:


Try this.

;with CTE as (
    SELECT
        row_number() over (partition by groupdesc order by YEAR(open_time), MONTH(open_time)) "Date_Order",
        YEAR(open_time) "Year",
        MONTH(open_time) "Month",
        SUM(CASE status WHEN 'open' THEN 1 ELSE 0 END) "open_count",
        SUM(CASE status WHEN 'closed' THEN 1 ELSE 0 END) "closed_count",
        SUM(CASE status WHEN 'open' THEN 1 ELSE 0 END) - SUM(CASE status WHEN 'closed' THEN 1 ELSE 0 END) "monthly_change"
    FROM PROBSUMMARYM1
    WHERE groupdesc = 'SUPPORT'
    GROUP BY groupdesc,
        YEAR(open_time),
        MONTH(open_time)
)
select c1.Date_Order,
    c1.Year,
    c1.Month,
    c1.open_count,
    c1.closed_count,
    sum(c2.monthly_change) "Backlog"
from CTE c1
join CTE c2
    on c2.Date_Order <= c1.Date_Order
group by c1.Date_Order,
    c1.Year,
    c1.Month,
    c1.open_count,
    c1.closed_count
order by c1.Year desc,
    c1.Month desc;

The partition by groupdesc and then grouping by groupdesc is not strictly necessary, but it demonstrates how you'd use row_number() to simultaneously run one query to do the same thing for multiple groupdesc.

You may be able to get away with just using SUM() OVER(), but I'm not that experienced with the OVER() clause. See http://technet.microsoft.com/en-us/library/ms189461.aspx , specifically example C.



来源:https://stackoverflow.com/questions/23012547/select-for-opened-closed-and-backlog

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!