最近出现了一个有趣的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查询可以返回此结果?
答案 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