2015-06-03 4 views
0

Мне нужно получить уникальные комбинации из двух столбцов.Уникальная комбинация из двух столбцов в mysql или postgres

Например, если значения:

sender_id recipient_id created_at 

    1   2   10/11/2014 
    2   1   10/12/2014 
    1   2   10/13/2014 
    1   3   10/14/2014 

Я хочу выход быть:

sender_id recipient_id created_at 

    1   3   10/14/2014 
    1   2   10/13/2014 

Я написал этот вопрос:

SELECT DISTINCT ON (sender_id, recipient_id) * 
FROM "messages" 
WHERE ((recipient_id = 1 and recipient_delete = false) 
    or (sender_id = 1 and sender_delete = false)) 
ORDER BY sender_id, recipient_id, created_at DESC 

Но он выдает это:

sender_id recipient_id created_at 

    1   3   10/14/2014 
    2   1   10/12/2014 
    1   2   10/13/2014 
+0

Я запутался, (1, 2) не является уникальным. Вы уверены, что это ваши ожидаемые результаты? – AdamMc331

+0

@ McAdam331 Извините, я не получил вас ... –

+0

Вы сказали, что хотите уникальные комбинации из двух столбцов, но (1, 2) не уникальны, он появляется дважды? Или вы имеете в виду уникальную комбинацию (1, 2) ИЛИ (2, 1)? – AdamMc331

ответ

2

Один из вариантов получения всех пар, независимо от того, являются ли они прямыми или обратными (например (1, 2) == (2, 1)), состоит в том, чтобы выбрать LEAST() и GREATEST() из каждой строки, а затем выбрать различные значения , Используя этот запрос:

SELECT DISTINCT LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id) 
FROM myTable; 

Вы получите следующий результат:

| 1 | 2 | 
| 1 | 3 | 

После того, как вы есть, что вы можете GROUP этими получить максимальную дату для каждой пары:

SELECT LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id), MAX(created_at) 
FROM myTable 
GROUP BY LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id); 

Этот запрос предоставит вам данные, необходимые для каждой пары, но не вернет фактическую строку из вашей исходной таблицы. Если есть строка формата | 2 | 1 | 2014-10-15 |, этот запрос вернет | 1 | 2 | 2014-10-15.

Чтобы получить оригинальную строку из таблицы, вы должны JOIN при условии, что все необходимые столбцы совпадают:

SELECT m.* 
FROM myTable m 
JOIN(
    SELECT LEAST(sender_id, recipient_id) AS least, 
    GREATEST(sender_id, recipient_id) AS greatest, 
    MAX(created_at) AS maxDate 
    FROM myTable 
    GROUP BY LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id)) tmp 
ON tmp.least = LEAST(m.sender_id, m.recipient_id) AND tmp.greatest = GREATEST(m.sender_id, m.recipient_id) AND tmp.maxDate = m.created_at; 

Вот SQL Fiddle пример, который соответствует вашим ожидаемым результатам.

+0

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

+0

@SachinPrasad, если у вас возникли проблемы, просто вернитесь и спросите, я постараюсь помочь. – AdamMc331

3

Первоначальная идея с DISTINCT ON хорошо, но:

  • он работает с Postgres, но не с MySQL, DISTINCT ON() быть нестандартным расширением PostgreSQL.
  • ON() должен применяться к выражению, где (1,2) и (2,1) эквивалентны.

Так близкий запрос, который должен работать и быть эффективным для Postgres является:

SELECT DISTINCT ON (pair) *, 
    CASE WHEN sender_id<recipient_id 
     THEN (sender_id,recipient_id) 
     ELSE (recipient_id,sender_id) 
    END AS pair 
FROM messages 
ORDER BY pair, created_at DESC ; 
+0

ничего себе не может быть проще и короче! –

+0

+ Действительно элегантный. – klin

+0

Мне также нужен счетчик непрочитанных сообщений с тем же запросом. Не могли бы вы помочь мне в этом? : http://stackoverflow.com/questions/31381765/rows-count-without-distinct –

Смежные вопросы