Получение уникальных записей на основе множественного соединения с условными обозначениями

#mysql

#mysql

Вопрос:

У меня есть 4 таблицы: posts , users , mentions , following

 posts
----------------------------
id  |  user_id  |  post_text
1      1           foo
2      1           bar
3      2           hello
4      3           jason

users
------------
id  |  name
1      jason
2      nicole
3      frank

mentions
--------------------------
id  |  post_id  |  user_id
1      4           1


following
-------------------------------------------------
id  |  user_id  |  user_id_of_user_being_followed
1      1           2
  

posts включает идентификатор пользователя пользователя, который опубликовал некоторый текст
users , имеет идентификатор пользователя и имя пользователя
mentions , имеет идентификатор сообщения и идентификатор пользователя любого сообщения, в котором упоминается 1 или более других пользователей
following , имеет идентификатор пользователя и пользователя, за которым они следуют (пользователь может следовать от 0 до многих пользователей)

То, что я пытаюсь сделать, это вернуть все сообщения от пользователей a, которым следует данный пользователь, ПЛЮС любые сообщения, в которых упоминается этот пользователь (независимо от того, следит данный пользователь или нет), без возврата каких-либо дубликатов.

 SELECT p.id, p.post, u.name, 
FROM following f 
JOIN posts p ON f.following = p.user_id
JOIN users u ON u.id = p.user_id 
WHERE f.user_id = :user;
  

Приведенное выше возвращает все сообщения от пользователей, на которые подписывается данный пользователь, но я изо всех сил пытаюсь понять, как также включить упоминания (помните, пользователю не нужно подписываться на кого-то, чтобы иметь возможность видеть сообщение, в котором они были упомянуты).

ОБНОВЛЕНИЕ: благодаря Джону Р. я смог это выяснить:

 SELECT DISTINCT(p.id), p.post, u.name 
FROM posts p 
LEFT JOIN following f ON f.following = p.user_id
LEFT JOIN mentions m ON m.posts_id = p.id 
JOIN users u ON u.id = p.user_id
WHERE (f.user_id = :user_id OR m.user_id = :user_id)
  

Ответ №1:

если я правильно понимаю ваш вопрос, вы бы хотели, чтобы левое соединение включало любые упоминания.. но не отфильтровывать подписчиков / сообщения

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

 SELECT 
    if(p.id is not null, p.id, p1.id) as post_id, 
    if(p.post is not null, p.post, p1.post) as post_text, 
    u.username, m.id, m.user_id
FROM posts p
JOIN users u on u.id = p.user_id
JOIN following f on f.user_id_of_user_being_followed = u.id
LEFT JOIN mentions m on m.user_id = f.user_id
LEFT JOIN posts p1 on p1.id = m.post_id
WHERE f.user_id = :user or m.user_id = :user;
  

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


РЕДАКТИРОВАТЬ: РАБОЧАЯ СКРИПКА

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

 (
    SELECT p.id, p.post_text, u.name
    FROM posts p
    JOIN users u on u.id = p.user_id
    JOIN following f on f.user_id_of_user_being_followed = u.id
    WHERE f.user_id = 1
)
UNION
(
    SELECT p.id, p.post_text, u.name
    FROM following f
    JOIN mentions m on m.user_id = f.user_id
    JOIN posts p on p.id = m.post_id
    join users u on u.id = p.user_id
    WHERE f.user_id = 1
);
  

Комментарии:

1. хм, похоже, это не работает, потому что мы уже ограничили количество возвращаемых сообщений на основе JOIN posts p ON f.following = p.user_id — по крайней мере, я думаю, именно поэтому.

2. можете ли вы предоставить мне несколько строк данных для работы? я могу видеть, где мне этого не хватает в теории, если вы это сделаете

3. отредактировал исходное сообщение. как вы можете видеть, пользователь 1 следует только за пользователем 2, однако пользователь 3 упомянул пользователя 1 в сообщении 4. Таким образом, пользователь 1 должен видеть все сообщения от пользователя 2 И сообщение, в котором они были упомянуты (как определено в таблице упоминаний).

4. @kylex Я только что отредактировал свой запрос.. sql fiddle иногда дает некоторые странные результаты, поэтому он не отображался точно правильно.. но данные все еще там … попробуйте выполнить этот запрос..

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

Ответ №2:

Возможно, вы унаследовали эту базу данных; но последняя таблица на самом деле не соответствует хорошей нормализации данных. В таблице должны быть id и following_id; при настройке у вас в конечном итоге закончатся столбцы (или вам придется продолжать добавлять их, когда пользователь получит сообщение об ошибке) — новые пользователи не смогут ни за кем следить.