【问题标题】:Select partitions based on matches in other table根据其他表中的匹配选择分区
【发布时间】:2016-09-19 15:39:42
【问题描述】:

有下表(conversations):

 id | record_id  |  is_response  |         text         |
 ---+------------+---------------+----------------------+
 1  |     1      |      false    | in text 1            |
 2  |     1      |      true     | response text 3      |
 3  |     1      |      false    | in text 2            |
 4  |     1      |      true     | response text 2      |
 5  |     1      |      true     | response text 3      |
 6  |     2      |      false    | in text 1            |
 7  |     2      |      true     | response text 1      |
 8  |     2      |      false    | in text 2            |
 9  |     2      |      true     | response text 3      |
 10 |     2      |      true     | response text 4      |

还有另一个帮助表 (responses):

 id |         text         |
 ---+----------------------+
 1  | response text 1      |
 2  | response text 2      |
 3  | response text 4      |

我正在寻找一个 SQL 查询来输出以下内容:

  record_id |       context
  ----------+-----------------------+---------------------
       1    | in text 1 response text 3 in text 2 response text 2
  ----------+-----------------------+---------------------
       2    | in text 1 response text 1
  ----------+-----------------------+---------------------
       2    | in text 2 response text 3 response text 4

所以每次is_responsetrue 并且text响应表中,聚合到目前为止的对话上下文,忽略对话部分不会以池中的响应结束。

在上面的例子中,响应文本 3record_id 1 中。

我尝试了以下复杂的 SQL,但有时会错误地聚合文本:

with context as(
    with answers as (

       SELECT record_id, is_response, id as ans_id
        , max(id)
          OVER (PARTITION BY record_id ORDER BY id
          ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS previous_ans_id
       FROM (select * from conversations where text in (select text from responses)) ans
       ),
     lines as (
      select answers.record_id, con.id, COALESCE(previous_ans_id || ',' || ans_id, '0') as block, con.text as text from answers, conversations con where con.engagement_id = answers.record_id and ((previous_ans_id is null and con.id <= ans_id) OR (con.id > previous_ans_id and con.id <= ans_id)) order by engagement_id, id asc
      )

      select record_id, block,replace(trim(both ' ' from string_agg(text, E' ')) ,'  ',' ') ctx from lines group by record_id, block order by record_id,block
      )

select * from context

我相信有更好的方法。

【问题讨论】:

    标签: sql postgresql window-functions


    【解决方案1】:

    这是我的看法:

    SELECT
        record_id,
        string_agg(text, ' ' ORDER BY id) AS context
    FROM (
        SELECT
            *,
            coalesce(sum(incl::integer) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),0) AS grp
        FROM (
            SELECT *, is_response AND text IN (SELECT text FROM responses) as incl
            FROM conversations
             ) c
         ) c1
    GROUP BY record_id, grp
    HAVING bool_or(incl)
    ORDER BY max(id);
    

    这将扫描表conversations 一次,但我不确定它是否会比您的解决方案执行得更好。基本思想是使用窗口函数来计算同一记录中的前行如何结束对话。然后我们可以按该号码和record_id 分组并丢弃不完整的对话。

    【讨论】:

    • 感谢您的回复,不幸的是,在您的解决方案中,聚合文本不保留行的顺序,我将文本和响应混淆
    • @ShlomiSchwartz 我加了一个ODER BY,现在应该可以了。
    • 像魅力一样工作。不熟悉聚合函数中的排序。谢谢。
    • 如果我想更改查询怎么办,所以当 incl 字段按顺序 (F,F,F,T,T,T) 为真时,文本将聚合在同一组中,而不是一个独立的行?
    • @ShlomiSchwartz 我不确定我理解你的意思。您是否只想在 incl 为 true 并且后面的记录为 incl=false 的地方进行聚合?
    【解决方案2】:

    有一个简单快速的解决方案:

    SELECT record_id, string_agg(text, ' ') As context
    FROM  (
       SELECT c.*, count(r.text) OVER (PARTITION BY c.record_id ORDER BY c.id DESC) AS grp
       FROM   conversations  c
       LEFT   JOIN responses r ON r.text = c.text AND c.is_response
       ORDER  BY record_id, id
       ) sub
    WHERE  grp > 0  -- ignore conversation part that does not end with a response
    GROUP  BY record_id, grp
    ORDER  BY record_id, grp;
    

    count() 只计算非空值。如果LEFT JOINresponses 为空,则r.text 为NULL:

    grp(“组”的缩写)中的值仅在触发新的输出行时才会增加。属于同一输出行的所有行都以相同的grp 编号结束。然后很容易在外部 SELECT 中聚合。

    特殊的技巧是以相反的顺序计算会话结束。最后一个 end 之后的所有内容(从末尾开始时首先出现)获取 grp = 0 并在外部 SELECT 中删除。

    更多解释的类似案例:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-13
      • 2012-12-24
      • 2014-02-06
      • 2019-12-13
      • 1970-01-01
      • 2012-09-20
      • 2019-06-28
      相关资源
      最近更新 更多