查询以获取给定时间范围内具有给定状态的项目的计数

时间:2017-01-03 20:21:43

标签: sql sql-server tsql

最近出现了一个有趣的SQL问题,这个问题在过去没有多个步骤我无法解决。

假设您有一个简单的“ItemStatus”表,该表用于跟踪不同项的状态。列是“itemId”,“日期状态已更改”和“状态”。例如:

ItemId      DateStatusChanged    Status
1           09/01/2016             New
1           10/15/2016             Complete
2           10/20/2016             New
2           10/25/2016             Complete

在更改项目之前,该项目被假定为相同的状态。所以第1项是从9/1开始的“新”,并且在10月15日之前保持“新”状态,当时它变为“完成”。第2项从10/20到10/25为“新”。

所以说你想查询表格,以计算2016年10月内任何时候有多少独特物品的状态为“新”(在这种情况下为“2”)。是否有一个SQL查询可以返回此结果?

5 个答案:

答案 0 :(得分:1)

这是与时间轴相关的常见SQL问题之一;是的,有一个解决方案。如果记录也有终止日期,则可以编写更简单,更有效的查询,但当然这意味着当您只查看一条记录时,您不会推断得太多,它也会产生潜在无效序列的问题(例如时间轴中的重叠状态或间隙)。所以......

select count(distinct ItemId)
  from ItemStatus is1
 where status = 'New'
   and DateStatusChanged < '2016-11-01' -- syntax may vary
   and not exists
       (select 1 
          from itemstatus is2
         where is2.itemid = is1.itemid
           and is2.status != 'New'
           and is2.datestatuschanged > is1.datestatuschanged
           and is2.datestatuschanged < '2016-10-01')

您可能需要调整一些<<=等,具体取决于所需的逻辑,时间戳粒度等。

答案 1 :(得分:1)

您可以使用lead根据datestatuschanged的升序获取每个itemid的下一个状态更改日期。然后检查下一个更改或现有更改是否在给定日期之间并计算这些项目。

select count(distinct ItemID) 
from (select i.*
     ,lead(datestatuschanged) over(partition by itemid order by datestatuschanged) as next_change
      from itemstatus i
      ) x
where status = 'New'
and ( (next_change >= '2016-10-01' and next_change <= '2016-10-31')
      or 
      (datestatuschanged >= '2016-10-01' and datestatuschanged <= '2016-10-31')
    )

答案 2 :(得分:0)

您可以使用条件聚合使每行具有“新建”和“完成”日期。从那里查询实际上非常简单。

这样的事情应该指向正确的方向。

select count(distinct ItemID) as ItemCount
from 
(
    select ItemID
        , max(case when Status = 'New' then DateStatusChanged end) as NewDate
        , Max(case when Status = 'Complete' then DateStatusChanged end) as CompleteDate
    from YourTable
    group by ItemID
) MyItems
where NewDate >= '2016-10-01'
    and CompleteDate >= '2016-10-01'
    and CompleteDate >= NewDate --just to ensure that is wasn't marked complete before it was marked new

答案 3 :(得分:0)

您可以尝试这样的查询

select ItemId from (
select itemid, status, RowN = row_number() over (partition by itemid order by status) from youritem where MONTH(datestatuschanged) = 10 and year(datestatuschanged) = 2016  ) as SourceTable
pivot(max(status) for RowN in ([1],[2])) p
where [1] = 'Complete' and [2] = 'New'

想法是将两种状态转换为列,并仅比较和选择所需的月份和年份

答案 4 :(得分:0)

通过COUNT(DISTINCT ...)轻松适应WHERE Status = 'New',以下CTE将为您提供10月份每一天每件商品的状态:

;WITH DATE_CTE ( aDate ) AS (
    SELECT CAST('2016-10-01' AS DATETIME)

    UNION ALL

    SELECT DATEADD(d, 1, cte.aDate)
      FROM DATE_CTE cte
     WHERE cte.aDate < CAST('2016-10-31' AS DATETIME)
)
SELECT i.itemid, dates.aDate, i.status 
  FROM DATE_CTE dates
        INNER JOIN itemstatus i 
            ON i.DateStatusChanged <= dates.aDate
        LEFT OUTER JOIN itemstatus i2 
            ON i.ItemId = i2.ItemId 
           AND i.DateStatusChanged < i2.DateStatusChanged 
           AND dates.aDate >= i2.DateStatusChanged
 WHERE i2.DateStatusChanged IS NULL
 ORDER BY i.itemid, dates.aDate

正确答案:

;WITH DATE_CTE ( aDate ) AS (
    SELECT CAST('2016-10-01' AS DATETIME)

    UNION ALL

    SELECT DATEADD(d, 1, cte.aDate)
      FROM DATE_CTE cte
     WHERE cte.aDate < CAST('2016-10-31' AS DATETIME)
)
SELECT COUNT(DISTINCT i.itemid)
  FROM DATE_CTE dates
        INNER JOIN itemstatus i 
            ON i.DateStatusChanged <= dates.aDate
        LEFT OUTER JOIN itemstatus i2 
            ON i.ItemId = i2.ItemId 
           AND i.DateStatusChanged < i2.DateStatusChanged 
           AND dates.aDate >= i2.DateStatusChanged
 WHERE i.Status = N'New'
   AND i2.DateStatusChanged IS NULL