Как получить отдельные строки на основе нескольких столбцов, включая последнюю строку из другой таблицы? - PullRequest
0 голосов
/ 14 апреля 2020

У меня есть таблица users и таблица private_messages, я хочу получить список пользователей, которым текущий пользователь отправил или получил сообщение от другого пользователя, с последним содержимым сообщения.

таблица пользователей

id | username

таблица private_messages

author_id | recipient_id | content | creation_date

Это самый близкий мне способ получить пользователей

SELECT u.id, u.username FROM users u
WHERE u.id IN 
    (SELECT p.author_id as id
     FROM private_messages p
     WHERE p.recipient_id = '6d480813-c854-40fc-a3cf-cea0944854ab' 
    );

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

Будучи новичком в SQL Я также собираюсь изменить модель таблицы.

данные таблицы пользователей

╔════════════════════════════════════════╦════════════════╗
║                  "id"                  ║   "username"   ║
╠════════════════════════════════════════╬════════════════╣
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "KayleeSchumm" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "JordonWaters" ║
║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "AsafAviv"     ║
╚════════════════════════════════════════╩════════════════╝

данные private_messages

╔════════════════════════════════════════╦════════════════════════════════════════╦═════════════════╦══════════════════════════════╗
║              "author_id"               ║             "recipient_id"             ║    "content"    ║       "creation_date"        ║
╠════════════════════════════════════════╬════════════════════════════════════════╬═════════════════╬══════════════════════════════╣
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
╚════════════════════════════════════════╩════════════════════════════════════════╩═════════════════╩══════════════════════════════╝

ожидаемый результат

╔════════════════════════════════════════╦════════════════╦═════════════════╗
║                  "id"                  ║   "username"   ║    "content"    ║
╠════════════════════════════════════════╬════════════════╬═════════════════╣
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "KayleeSchumm" ║ "hello world 0" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "JordonWaters" ║ "hello world 0" ║
╚════════════════════════════════════════╩════════════════╩═════════════════╝

Ответы [ 2 ]

1 голос
/ 14 апреля 2020

Попробуйте:

SELECT u.id, u.username, p.content 
FROM users u
INNER JOIN (
  SELECT DISTINCT
    CASE WHEN p.author_id = '6d480813-c854-40fc-a3cf-cea0944854ab' THEN p.recipient_id ELSE p.author_id END user_id,
    p.content
  FROM private_messages p
  WHERE '6d480813-c854-40fc-a3cf-cea0944854ab' IN (p.author_id, p.recipient_id)
  AND NOT EXISTS (
    SELECT 1 FROM private_messages
    WHERE (author_id, recipient_id) IN ((p.author_id, p.recipient_id), (p.recipient_id, p.author_id))
      AND creation_date > p.creation_date
  )                                                                         
) p ON u.id = p.user_id

Запрос, присоединенный к users, использует NOT EXISTS, чтобы вернуть последнее сообщение каждому из пользователей. Смотрите демо . Или с CTE s и ROW_NUMBER() оконной функцией:

WITH 
  cur_user(id) AS (VALUES('6d480813-c854-40fc-a3cf-cea0944854ab')),
  cte_all AS (
    SELECT content, creation_date,
      CASE (SELECT id FROM cur_user)
        WHEN author_id THEN recipient_id
        ELSE author_id
      END user_id
    FROM private_messages 
    WHERE (SELECT id FROM cur_user) IN (author_id, recipient_id)
  ),
  cte_last AS (
    SELECT t.content, t.creation_date, t.user_id
    FROM (
      SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY creation_date) rn
      FROM cte_all
    ) t
    WHERE t.rn = 1 
  )  
SELECT u.id, u.username, c.content 
FROM users u INNER JOIN cte_last c
ON c.user_id = u.id

См. demo . Результаты:

| id                                   | username     | content       |
| ------------------------------------ | ------------ | ------------- |
| 2f8afc87-13d3-4654-8333-465b094c9ccf | KayleeSchumm | hello world 0 |
| e5e37e98-3468-47e1-9c7e-1311988d6d79 | JordonWaters | hello world 0 |
0 голосов
/ 14 апреля 2020

Я думаю, что вы хотите:

SELECT u.id, u.username, pm.*
FROM users u CROSS JOIN
     (values ('6d480813-c854-40fc-a3cf-cea0944854ab')
     ) as v(the_user) CROSS JOIN LATERAL
     (SELECT DISTINCT ON (v2.other_user) pm.*
      FROM private_messages pm CROSS JOIN LATERAL
           (VALUES (CASE WHEN pm.recipient_id = v.the_user
                         THEN pm.author_id ELSE pm.recipient_id
                   END)
           ) v2(other_user)
      WHERE v.the_user = v2.other_user
      ORDER BY v2.other_user, pm.creation_date desc
     ) pm;

Предложения VALUES() являются просто удобством для обработки постоянного пользовательского значения.

Ключевой частью является DISTINCT ON. Это возвращает первую строку для каждого «другого пользователя» на основе ORDER BY. В этом случае ORDER BY ставит самую последнюю запись на первое место.

...