【问题标题】:Select for Opened, Closed and Backlog选择打开、关闭和积压
【发布时间】:2014-05-25 14:33:18
【问题描述】:

我有下表:

+-------------+---------------------+---------------------+
| 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 |
+-------------+---------------------+---------------------*

我想展示这个结果:

+-------------+---------------+--------------+---------+
| 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)
+--------------+--------------+--------------+---------+

其实我面临两种情况:

  • 情况 #1:我无法为 Closed Cases 获得正确的值。尝试使用 where 中的子句,但没有成功。

  • 情况 #2:在检索 Closed Cases 之后,我应该执行 (Opened - Closed) 并在几个月内累积,所以这将是积压。

对于情况 #1:

如果我执行以下选择:

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

我明白了(这是已结案的正确值):

+-------------+---------------+--------------+
| 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 |
+--------------+--------------+--------------+

但如果我这样做:

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

我明白了:

+-------------+---------------+--------------+
| 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 |
+--------------+--------------+--------------+

【问题讨论】:

  • 所需结果表的积压列,第 2014.3 行显示 57。它应该是 54 = 52 + 2。

标签: sql sql-server select group-by


【解决方案1】:

这是解决问题的一种方法:选择每月的所有期初计数和每月的所有期末计数。然后全外加入他们。累积是所谓的运行总计,您可以通过 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;

这是 SQL 小提琴:http://sqlfiddle.com/#!6/68dcf/7

另一种方法是使用 UNION ALL 将开始事件与结束事件粘合,然后计数:

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;

这是第二个 SQL 小提琴:http://sqlfiddle.com/#!6/68dcf/18

编辑:没有总和?这太糟糕了。因此,您将不得不对计数进行子选择。这很慢,因为必须每个月再次扫描该表。

对于每个月,我们必须找到到那时为止的所有开始日期和到那时为止的所有结束日期。由于结束永远不会在开始之前发生,我们可以选择开始日期匹配的所有记录并计算这些记录。在这些记录中,我们还将找到所有可能的结束日期。我们计算这些(加上和和大小写),减去,我们就完成了。

因此,您将不得不替换 SUM() OVER as backlog 部分:

  (
    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

然而,SQL Server,即使是在 2012 版本,也不能执行这个,我认为这是一个 dbms 缺陷。 y.ano 和 y.mes(或第一条语句的 coalesce(opened.ano, closed.ano) 和 coalesce(opened.mes, closed.mes) )应该被视为内部查询的常量,因为评估是按外部记录,即月份,但它们不是。我不知道如何克服这个问题。也许 SQL Server 方面的一些专家可以在这里为您提供帮助。

以下是导致语法错误的两个小提琴:http://sqlfiddle.com/#!6/68dcf/32http://sqlfiddle.com/#!6/68dcf/31。对不起,我不能再帮你了。

【讨论】:

  • 首先感谢您的宝贵时间。这两个示例都正是我所需要的,并且在小提琴中运行良好,但在我的场景中我得到了一个错误,我认为这是因为 sql-server 版本。我的是 10.50.2500(我认为是 2008 年)。在第一个示例中,除了以下行之外所有运行正常:sum(coalesce(opened.cnt, 0) - coalesce(closed.cnt, 0)) over (order by coalesce(opened.ano, closed.ano), coalesce(opened.mes, closed.mes)) as backlog.
  • 好的,我已经编辑了我的答案。但是,dbms 甚至在那个老式的解决方案上也有问题,所以你需要关于 SQL Server 的专家建议,我无法提供。对不起,祝你好运。
【解决方案2】:

试试这个。

;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;

partition by groupdesc 然后按groupdesc 分组并不是绝对必要的,但它演示了如何使用row_number() 同时运行一个查询来为多个groupdesc 执行相同的操作。

您可能只使用SUM() OVER() 就可以侥幸逃脱,但我对OVER() 子句的经验并不丰富。见http://technet.microsoft.com/en-us/library/ms189461.aspx,具体例子C。

【讨论】:

  • open_count 列仅显示所有行的 0。见:oi60.tinypic.com/2v3ork5.jpg
  • 是否可以使用日期来求和而不是状态?
  • 那么,status 中实际存储了什么?它真的是'open' 还是其他价值?是否有应该修剪的空白填充 (' open')?您是否使用区分大小写的排序规则?
  • 原因是我有其他身份。抱歉,我不应该在示例中显示该列,这就是我在示例中使用日期的原因,抱歉。
  • "是否可以使用日期来求和而不是状态?"可能是。这使逻辑大大复杂化。我不确定如何使用这样的聚合数据来做到这一点。我可能会创建一个仅计算已打开票证的子查询和一个仅计算已关闭票证的查询。然后加入这些并使用ROW_NUMBER() 功能。然后在第三个查询中进行积压计算。
猜你喜欢
  • 1970-01-01
  • 2013-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-06
  • 1970-01-01
  • 2011-05-09
  • 2012-05-07
相关资源
最近更新 更多