【问题标题】:Effective way to determine time between posts?确定职位之间时间的有效方法?
【发布时间】:2017-05-11 10:51:08
【问题描述】:

我有一个场景,我希望有更有效的方法来优化它的代码,我们开始吧。

假设有一个名为 ticket_thread 的表,其中包含以下字段

  • 线程ID
  • ticketID
  • threadType - 可以是 c2s、s2s、s2c
  • postTime - 日期时间
  • 留言

所有数据都按照ticketID排序,后跟postTime

我的工作是确定每个 c2s 到 s2c 所需的时间,也就是响应时间。

我目前的方法是将过滤后的表转储到两个列表中 - c2s 和 s2c

while (!isempty($c2s) || !isempty($s2c)) {

  // popping first record from c2s
  $c2sRecord = array_shift($c2s);

  if (!$c2sRecord['ticketID'] == $s2c[0]['ticketID']) {

    // cannot find a response to the ticket
    echo $c2sRecord['ticketID'] . "<br>";

  } else {

    echo $c2sRecord['ticketID'];

    // popping first response from s2c
    $s2cRecord = array_shift($s2c);

    // print out the response time
    echo " " . date_diff($s2cRecord['postTime'], $c2sRecord['postTime']);

    $filter = true;
    while ($filter) {

      // checking the next record in c2s, if it is a different ticket 
      // OR the new post is placed AFTER service has responded.
      if (($c2s[0]['ticketID'] <> $s2cRecord['ticketID']) 
          or ($c2s[0]['postTime'] > $s2cRecord['postTime'])) {

        // stops the filter
        $filter = false;

      } else {

        // pop out unneeded records (supplementary questions) 
        $c2sRecord = array_shift($c2s);

      }
    }
  }

我的问题是,这需要的时间太长,有没有更快的方法可以使用 SQL 来生成我需要的东西?

table generated from SQL

ticket_id | c2sTime  | s2cTime  | timeTaken | rank
  0012    | 12:20:20 | 12:30:20 | 00:10:00  |   1
  0012    | 12:40:00 | 12:55:30 | 00:15:30  |   2
  0012    | 13:10:20 |   null   |   null    |   3
  0013    | 12:20:20 |   null   |   null    |   1

编辑:根据要求提供示例表

threadID | ticketID | threadType | postTime | message
  3012   |   0012   |    c2s     | 12:20:20 | customer A's 1st post
  3014   |   0012   |    c2s     | 12:20:30 | Added info to A's 1st post, should not be included
  3015   |   0012   |    s2c     | 12:30:20 | Support responding to A's 1st post
  3016   |   0012   |    s2s     | 12:30:30 | internal chat, should not be included
  3017   |   0012   |    s2s     | 12:30:40 | internal chat, should not be included
  3018   |   0012   |    c2s     | 12:40:00 | A's 2nd post
  3019   |   0012   |    s2c     | 12:55:30 | Support responding to A's 2nd post
  3020   |   0012   |    s2c     | 13:00:00 | Added info to Support's 2nd response, should not be included
  3021   |   0012   |    c2s     | 13:10:00 | A's 3nd post
  3013   |   0013   |    c2s     | 12:20:20 | customer B's 1st post

【问题讨论】:

  • 编辑问题并添加表格中的示例数据以获得所需的输出。
  • 您的输出中的rank 是什么?它只是一些编号,基于排序吗? (又名。row_number()/rank()/dense_rank() 那种东西?)
  • @pozs 是的,它已被使用,因此我可以轻松判断客户和支持成员之间来回回复了多少次

标签: php sql postgresql


【解决方案1】:

如果所有window functions 都可以支持FILTER() 子句(就像基于聚合的变体一样),您的任务可能会简单得多。 IE。您只需要:

-- won't work, unfortunately
first_value(post_time) filter (where thread_type = 's2c')
                         over (partition by ticket_id
                               order by post_time
                               rows between current row and unbounded following)

在此之前,您可以使用自联接:

select  t.*, row_number() over (partition by t.ticket_id order by t.c2s_time) rank
from    (select    distinct on (coalesce(s2c.thread_id, c2s.thread_id))
                   c2s.ticket_id,
                   c2s.post_time c2s_time,
                   c2s.message c2s_message,
                   s2c.post_time s2c_time,
                   s2c.message s2c_message,
                   s2c.post_time - c2s.post_time time_taken
         from      ticket_thread c2s
         left join ticket_thread s2c on  c2s.ticket_id = s2c.ticket_id
                                     and s2c.thread_type = 's2c'
                                     and c2s.post_time < s2c.post_time
                                     and not exists(select 1
                                                    from   ticket_thread
                                                    where  post_time > c2s.post_time
                                                    and    post_time < s2c.post_time
                                                    and    ticket_id = c2s.ticket_id
                                                    and    thread_type = 's2c')
         where     c2s.thread_type = 'c2s'
         order by  coalesce(s2c.thread_id, c2s.thread_id), c2s.post_time) t
order by t.ticket_id, t.c2s_time;

或者,您可以使用array_agg() 作为窗口函数:

select  t.*, row_number() over (partition by t.ticket_id order by t.c2s_time) rank
from    (select    distinct on (coalesce((m).thread_id, (t).thread_id))
                   (t).ticket_id,
                   (t).post_time c2s_time,
                   (t).message c2s_message,
                   (m).post_time s2c_time,
                   (m).message s2c_message,
                   (m).post_time - (t).post_time time_taken
         from      (select t, array_agg(t) filter (where thread_type = 's2c')
                                             over (partition by ticket_id
                                                   order by     post_time
                                                   rows between current row and unbounded following) a
                    from   ticket_thread t) t
         left join lateral  (select   m
                             from     unnest(a) m
                             order by (m).post_time
                             limit    1) m on true
         where     (t).thread_type = 'c2s'
         order by  coalesce((m).thread_id, (t).thread_id), (t).post_time) t
order by t.ticket_id, t.c2s_time;

从我的内部测试来看,自联接变体似乎要快一些,而且它还可以在(ticket_id, post_time) 上使用索引。 (但如果性能对您来说真的很重要,您应该同时测试两者)。

或者,您也可以添加缺少的功能(即创建 first_agg 聚合并将其用作窗口函数):

create or replace function first_agg_val(anyelement, anyelement)
  returns anyelement
  language sql
  immutable
  strict
  as 'select $1';

create aggregate first_agg(
  sfunc    = first_agg_val,
  basetype = anyelement,
  stype    = anyelement
);

select  t.*, row_number() over (partition by t.ticket_id order by t.c2s_time) rank
from    (select    distinct on (coalesce((s2c).thread_id, (c2s).thread_id))
                   (c2s).ticket_id,
                   (c2s).post_time c2s_time,
                   (c2s).message c2s_message,
                   (s2c).post_time s2c_time,
                   (s2c).message s2c_message,
                   (s2c).post_time - (c2s).post_time time_taken
         from      (select t c2s, first_agg(t) filter (where thread_type = 's2c')
                                                 over (partition by ticket_id
                                                       order by     post_time
                                                       rows between current row and unbounded following) s2c
                    from   ticket_thread t) t
         where     (c2s).thread_type = 'c2s'
         order by  coalesce((s2c).thread_id, (c2s).thread_id), (c2s).post_time) t
order by t.ticket_id, t.c2s_time;

如果你不需要rank,你可以去掉外部查询(它们是存在的,纯粹是为了rank)。 (相反,这通常很容易在客户端计算。)

http://rextester.com/BUY9309

PS:我的查询的time_taken 列是interval。如果您不喜欢/无法解析该列,则可以改用以下公式来获取时间差以秒为单位

extract(epoch from <interval expresssion>)

【讨论】:

  • 嗨@pozs,感谢您的及时回复。您的脚本几乎可以工作,但如果客户在工作人员回复之前发送了两个或更多帖子(例如:rextester.com/BLIJ77843),您的两个脚本都失败了。好吧,第二种方法几乎奏效了,如果没有显示附加的帖子消息,那么这正是我想要的。编辑:我在消息中添加只是为了将其用作评论。
  • @Willy_Sunny 我明白了;然后,您将需要更高级的查询。我更新了我的帖子,请查看。
  • 嗯。出于某种原因,当我尝试在我的工作机器上使用该脚本时,它一直告诉我在过滤器语句处或附近存在语法错误,不确定是否是因为我正在使用 9.3 而网站使用 9.6 = \
  • @Willy_Sunny 是的,filter 子句是 9.4+ 的功能。对于 9.3,您可以使用第一个变体(模拟 filter 将需要更复杂的查询,如果我们有替代方法,这是不值得的)。
  • 感谢您的脚本运行良好,但我注意到一个错误(我已修复)是​​您的不存在过滤器应在过滤器中包含“ticket_id = c2s.ticket_id”,或者在支持先回答第二个客户再回答第一个客户的情况下,会导致错误(错误版本:rextester.com/BTB49241)(固定版本:rextester.com/BUY9309
【解决方案2】:

主要使用窗口函数的替代解决方案:

select ticketid, c2stime, s2ctime, s2ctime- c2stime as timetaken, rank() over w
from (
    select ticketid, threadtype, posttime as c2stime, lead(posttime) over w as s2ctime
    from (
        select *, lag(threadtype) over w
        from ticket_thread
        where threadtype <> 's2s'
        window w as (partition by ticketid order by threadid)
        ) s
    where threadtype <> coalesce(lag, '')
    window w as (partition by ticketid order by threadid)
    ) s
where threadtype = 'c2s'
window w as (partition by ticketid order by c2stime)
order by ticketid, c2stime;

 ticketid | c2stime  | s2ctime  | timetaken | rank 
----------+----------+----------+-----------+------
       12 | 12:20:20 | 12:30:20 | 00:10:00  |    1
       12 | 12:40:00 | 12:55:30 | 00:15:30  |    2
       12 | 13:10:00 |          |           |    3
       13 | 12:20:20 |          |           |    1
(4 rows)

【讨论】:

    猜你喜欢
    • 2018-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-13
    • 2020-07-30
    • 1970-01-01
    • 2016-12-30
    • 1970-01-01
    相关资源
    最近更新 更多