计算状态变化

时间:2013-03-08 09:38:01

标签: sql sql-server

我想知道是否可以计算记录中值之间的变化次数

例如我有以下数据

| radio | name | state | timestamp           |
|-------|------|-------|---------------------|
| 1     | AP1  | down  | 2013-03-07 10:00:00 |
| 1     | AP1  | down  | 2013-03-07 10:15:00 |
| 1     | AP1  | up    | 2013-03-07 10:30:00 |
| 1     | AP1  | down  | 2013-03-07 10:45:00 |
| 1     | AP1  | pend  | 2013-03-07 11:00:00 |
| 1     | AP1  | pend  | 2013-03-07 11:15:00 |
| 1     | AP1  | pend  | 2013-03-07 11:30:00 |
| 1     | AP1  | pend  | 2013-03-07 11:45:00 |

我需要进行一个执行以下操作的查询

| name | S1     | S2     | S3       | S4         | (other time range) |
| AP1  | A      | A      | B        | A          | C                  |
                     \      /    \      /    \      /
                      DETECT      DETECT      DETECT    
                            \        \       / 
                             \------- \     /
                                       COUNT = 3 <-- this is the number i want

到目前为止,我陷入了基于计数的SQL。

SELECT radio, name, state, COUNT(state) AS count FROM data
WHERE timestamp BETWEEN '2013-03-07 10:00:00' AND '2013-03-07 11:00:00'
GROUP BY radio, name, channel

结果将是

| radio | name | state | count |
|-------|------|-------|-------|
| 1     | AP1  | down  | 3     |
| 1     | AP1  | up    | 1     |
| 1     | AP1  | pend  | 1     |

所以结果现在将是3行又名3状态变化,但后来我意识到这不是真的,因为这个数字 只会告诉我不同​​州名的数量,在这种情况下为3。

所以我的问题是这在SQL中是否可行,或者我是否需要将结果集拉入循环并通过外部脚本或视图检测更改。

2 个答案:

答案 0 :(得分:3)

您可以使用以下内容:

with Changes as
(
  select name
    , stateChange = case when prev.state is null then 0
      when sc.state <> prev.state then 1
      else 0
      end
  from data sc
    outer apply
    (
      select top 1 prev.state
      from data prev
      where sc.timestamp > prev.timestamp
        and sc.name = prev.name
      order by prev.timestamp desc
    ) prev
)
select name, stateChanges = sum(stateChange)
from Changes
group by name

SQL Fiddle with demo

此外,在您的问题中,您的数据有点不一致;有时您会提到 AP1 ,有时候会提到 AP2 。如果上述查询/小提琴不够,请提供更大的数据集。

答案 1 :(得分:2)

您可以使用LAG():

SELECT sq.RADIO,
  sq.NAME,
  COUNT(CASE WHEN sq.STATE != sq.PREV_STATE THEN 1 ELSE NULL END) CHG_COUNT
FROM
  (SELECT 
    td.RADIO,
    td.NAME,
    td.STATE,
    COALESCE(LAG(td.STATE,1) OVER(PARTITION BY td.RADIO, td.NAME ORDER BY td.TIMESTAMP ASC),td.STATE) PREV_STATE
  FROM DATA td) sq
GROUP BY sq.RADIO, 
  sq.NAME