Получение разговоров из строковой формы в postgresql - PullRequest
3 голосов
/ 09 июля 2020

У меня есть таблица со следующими данными:

id | client_id | message                       | user_id | incoming
1  | 1         | Hi, anybody there?            | 2       | True
2  | 1         | I need help                   | 2       | True
3  | 1         | Yes, I am here to help you.   | 2       | False
4  | 2         | Did you solve it yet?         | 5       | True
5  | 3         | Is my issue resolved?         | 5       | True
6  | 2         | yes, it is solved             | 5       | False
7  | 5         | Are you happy with us?        | 3       | False
8  | 5         | yes, very much                | 3       | True

Клиенты разговаривают с пользователями, и input = True означает, что сообщение от клиента, тогда как False означает, что пользователь отправил сообщение. Я хочу, чтобы результат был таким:

client_id | client_message                    | user_id  | user_message
1         | Hi, anybody there? I need help    | 2        | Yes, I am here to help you.
2         | Did you solve it yet?             | 5        | yes, it is solved

Я хочу, чтобы разговоры были прикреплены в одну строку, где клиент сначала отправил сообщение, а затем пользователь ответил на это. Обратите внимание, что в конце строк 7,8 кажется, что есть диалог, но, поскольку пользователь начал его, он не добавляется в результат. Строка с ID 5 отбрасывается, так как на нее нет ответа.

Мой запрос в настоящее время выглядит следующим образом:

SELECT client_id, t1.message as client_message, user_id, t2.message as user_message
FROM conversations c1 INNER JOIN conversations c2
ON c1.client_id=c2.client_id AND c1.user_id=c2.user_id AND c1.incoming=True

, но он не дает правильного ответа. Любая помощь будет принята с благодарностью. Спасибо!

Ответы [ 2 ]

2 голосов
/ 09 июля 2020

Это пример учебника ИМХО для демонстрации функций аналити c. Я предполагаю, что несколько сообщений могут повторяться между одной и той же парой клиент-пользователь в обоих направлениях, и вы хотите сгруппировать последующие сообщения, идущие в одном направлении вместе, используя идентификатор в качестве порядкового номера. Возможным решением может быть (несколько строк добавлены мной):

with t (id, client_id, message, user_id, incoming) as (values
(1  , 1         , 'Hi, anybody there?'            , 2       , True),
(2  , 1         , 'I need help'                   , 2       , True),
(3  , 1         , 'Yes, I am here to help you.'   , 2       , False),
(4  , 2         , 'Did you solve it yet?'         , 5       , True),
(5  , 3         , 'Is my issue resolved?'         , 5       , True),
(6  , 2         , 'yes, it is solved'             , 5       , False),
(7  , 5         , 'Are you happy with us?'        , 3       , False),
(8  , 5         , 'yes, very much'                , 3       , True),
(9  , 1         , 'Hi, anybody there again?'      , 2       , True),
(10 , 1         , 'I need help'                   , 2       , True),
(11 , 1         , 'Yes, I am here to help you.'   , 2       , False),
(12 , 1         , 'Again.'                        , 2       , False)
), ch as (
  select t.*
       , case coalesce(incoming !=
             lag(incoming) over (
               partition by client_id, user_id 
               order by id
             )
             , true) 
           when true then 1
           else 0
         end as incoming_changed
  from t
), groups as (
  select ch.*
       , sum(incoming_changed) over (
           partition by client_id, user_id
           order by id
         ) as grp
  from ch
), grouped as (
  select client_id
       , string_agg(message, ' ') as message
       , user_id
       , incoming
       , min(id) as min_id
  from groups
  group by client_id, user_id, grp, incoming
  order by client_id, user_id, min_id
), paired as (
  select grouped.*
       , lead(min_id) over (
           partition by client_id, user_id
           order by min_id
         ) as response_id
  from grouped
)
select pc.client_id
     , pc.message as client_message
     , pu.user_id
     , pu.message as user_message
from paired pc
join paired pu on pc.response_id = pu.min_id
where pc.incoming

Объяснение: сначала определить, когда меняется направление разговора. Он разделяет строки на группы. Сообщения объединяются в каждой группе (см. CTE select * from grouped). Затем соедините каждое сообщение клиента с ответом пользователя (если есть), который является следующим по идентификатору (я надеюсь, что этот столбец играет роль метки времени).

Здесь скрипт БД

2 голосов
/ 09 июля 2020

Это интересная проблема.

См. Это dbfiddle . После первого запуска раскомментируйте insert для id 15, чтобы увидеть, что такое dialog_id.

with responses as (
  select id, client_id, user_id, 
         row_number() 
           over (order by client_id, user_id, id) 
           as dialog_id
    from conversations
   where incoming = false
), dialogs as (
  select c.*, min(dialog_id) as dialog_id
    from conversations c
    join responses r
      on r.client_id = c.client_id
     and r.user_id = c.user_id
     and r.id >= c.id
   group by c.id, c.client_id, c.message, c.user_id, c.incoming
)
select dialog_id, client_id,
       array_agg(message order by id) 
         filter (where incoming = true) 
           as client_message,
       user_id,
       max(message) 
         filter (where incoming = false) 
           as user_message  
  from dialogs
 group by dialog_id, client_id, user_id
order by dialog_id;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...