2016-09-19 1 views
2

Имея следующую таблицу (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_response является true и text: в таблица ответов, совокупность контекст разговора до этой точки, игнорируя часть разговора, которая не заканчивается ответом в пуле.

В приведенном выше примере жизни текст ответа 3 в record_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 

я уверен, Существует лучший способ.

ответ

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 и отказаться от неполных разговоров.

+0

Спасибо за ответ, к сожалению, в вашем решении агрегированного текст не сохраняет порядок строк, и я получаю текст и ответы смешаны –

+0

@ShlomiSchwartz Я добавил «ODER BY» в совокупности, теперь все должно быть в порядке. – redneb

+0

Работает как очарование. Не было знакомо с заказом внутри агрегатной функции. Спасибо. –

0

Существует простое и быстрое решение:

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() подсчитывает только ненулевые значения. r.text является NULL, если LEFT JOIN к responses приходит пустым:

Значение в grp (сокращенно «группа») только увеличивается, когда новый выходной строки запускается. Все строки, принадлежащие одной и той же выходной строке, заканчиваются тем же номером grp. Затем легко агрегировать во внешнем SELECT.

Специальный трюк состоит в том, чтобы подсчитывать разговор заканчивается в обратном порядке. Все после последнего конец (на первом месте, начиная с конца) получает grp = 0 и снимается во внешнем SELECT.

Подобные случаи с более подробным объяснением: