问题
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