【问题标题】:Select records based on a column value changing from the last record根据从最后一条记录更改的列值选择记录
【发布时间】:2016-10-08 20:28:30
【问题描述】:

使用 Sql Server 2008

所以我有一个名为 States 的表,它的数据如下:

DateTime                | skID | TZ | DomID | EventName | SKGrpID | State
--------------------------------------------------------------------------
2016-06-08 09:22:54.010 | 1234 | 1  | 222   | 0         | 4321    | 0
2016-06-08 09:22:54.000 | 1234 | 1  | 222   | 3         | 4321    | 3
2016-06-08 09:21:56.000 | 1234 | 1  | 222   | 8         | 4321    | 4
2016-06-08 09:19:00.000 | 1234 | 1  | 222   | 7         | 4321    | 4
2016-06-08 09:18:58.000 | 1234 | 1  | 222   | 4         | 4321    | 4
2016-06-08 09:13:06.000 | 1234 | 1  | 222   | 6         | 4321    | 6
2016-06-08 09:10:42.000 | 1234 | 1  | 222   | 4         | 4321    | 4
2016-06-08 09:10:40.000 | 1234 | 1  | 222   | 1         | 5555    | 1
2016-06-08 09:00:28.003 | 1234 | 1  | 222   | 1         | 4321    | 1

可以将行视为某人接听电话的记录。如果状态为“4”,则表示他们正在通话,如果是其他任何内容,则表示他们不是。最古老的记录在底部。所以 9:10:42 的第三条记录是通话的开始,9:13:06 的记录是通话的结束(状态从 4 变为 6。)

有时调用会持续多个事件。所以在 9:18:58 的记录中,一个呼叫开始并一直持续到 9:21:56 的记录(状态开始 ->4,4,4,3

我想要一个查询,它以某种方式选择开始记录和结束记录,将它们连接起来并获得持续时间。问题是我不知道如何根据从 4 变为其他状态的状态进行选择。我也在努力弄清楚如何选择每个呼叫的每个开始(开始记录将是状态为 4 的记录,而前面的记录是其他记录(例如状态 1、4、3、6、4 , 5 将是第二条和第 5 条记录将是通话开始,3 和 6 将是通话结束。))

理想的结果应该是

StartTime, EndTime, Duration, skID, TZ, DomID, SKGrpID, StartEvent, StartState, EndEvent, EndState 

我目前可以在 python 中通过查询所有行然后制作 dicts 并以这种方式匹配调用来完成此操作,但我想在 SQL 中做尽可能多的事情。做这种选择甚至可能吗?

【问题讨论】:

  • 有可能。但是在 Stack Overflow 上,您应该展示您尝试过的内容。但是给你一个提示:查看 LIMIT 1 和 ORDER BY ASC/DESC。祝你好运。
  • @user3741598 我可以发布我尝试过的内容,但这基本上是大量的小查询,与我想要的完全不同。我从来没有写过这样的查询,甚至找不到一个好的起点。我了解限制的工作原理,但不知道如果一天内可能有多个电话,这将有什么帮助。
  • @user3741598 意识到我放错了标签。这是 sql-server,所以我什至不能使用限制。

标签: sql sql-server


【解决方案1】:

这是一种方法:

select
  max(case when RN = 1 then DateTime end) as StartTime,
  max(case when RN = 2 then DateTime end) as EndTime,
  max(case when RN = 1 then EventName end) as StartEvent,
  max(case when RN = 2 then EventName end) as EndEvent
from
(
  select
    *,
    row_number() over (partition by CallId order by DateTime) as RN
  from
  (
    select
      *,
      sum(CallStatus) over (order by DateTime) as CallId
    from
    (
      select
        *
      from
      (
        select 
          *,
          lag(CallStatus) over (order by DateTime) as PrevCallStatus
        from
        (
          select 
            DateTime,
            State,
            EventName,
            case when State = 4 then 1 else 0 end as CallStatus
          from #Table1
        ) A
      ) B
      where CallStatus != PrevCallStatus 
    ) C
  ) D
) E
group by CallId

这从最里面的派生表A 开始,该表用于分配呼叫状态 1/0。表B 用于获取CallStatus 之前的值,然后用于过滤掉具有相同CallStatus 的行。因为 CallStatus 是 1/0,所以可以在运行总计中使用它来计算每个呼叫的单独 ID。因为来自这里的数据将始终包含 2 行,所以 row_number 用于为行分配一个数字(1=start,2=end),然后在最终选择中使用它来分别从开始行和结束行获取数据。

这没有你所有的领域,但至少它应该让你开始:)

这假设您的通话始终有结束记录,并且仅适用于 SQL Server 2012 或更高版本。对于旧版本,您需要为运行总数和滞后创建更复杂的东西。那么光标很可能是最好的选择。

【讨论】:

  • 谢谢谢谢谢谢!遗憾的是 SQL Server 2008 版本 10.5,所以我必须找到一些方法来克服没有滞后功能的问题,但这至少让我知道如何去做,再次感谢您。
【解决方案2】:
SELECT 'StartTime'
     , 'EndTime'
     , 'Duration'
     , 'skID'
     , 'TZ'
     , 'DomID'
     , 'SKGrpID'
     , 'StartEvent'
     , 'StartState'
     , 'EndEvent'
     , 'EndState';

如果这不是你的意思,请澄清。 ;-)

【讨论】:

  • 我什至没有词。
猜你喜欢
  • 2022-08-23
  • 2021-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-05
  • 2021-11-26
相关资源
最近更新 更多