@Screpka

Правильно ли я создал sql запрос?

Есть такие таблицы:
Таблица users – пользователь сайта:
  • id – уникальный идентификатор записи
  • name – имя пользователя
  • age – возраст пользователя
  • admin – true = администратор сайта, false = обычный пользователь

Таблица article – статьи:
  • id - уникальный идентификатор записи
  • user_id – идентификатор пользователя из таблицы users, написавшего статью
  • annotation – аннотация к статье
  • text – полный текст статьи

Таблица comments – комментарии к статье:
  • id - уникальный идентификатор записи
  • user_id - идентификатор пользователя из таблицы users, написавшего комментарий
  • text – текст комментария
  • is_deleted – true = комментарий помечен как удаленный, false = комментарий не помечен
  • как удаленный

Таблица article_comment_association – привязка комментариев к статьям:
  • article_id – уникальный идентификатор статьи из таблицы articles
  • comment_id – уникальный идентификатор комментария из таблицы comments


Нужно найти все имена пользователей, у которых есть статьи с комментариями.

Правильно ли сделать такой запрос?
SELECT users.name from users
INNER JOIN articles ON (articles.user_id = users.id)
INNER JOIN comments ON (comments.user_id = users.id)
GROUP BY users.name;
  • Вопрос задан
  • 169 просмотров
Решения вопроса 1
pi314
@pi314
Президент Солнечной системы и окрестностей
Нет, запрос сделан неправильно! Он, дополнительно к тому, что требуется, выберет еще и пользователей, которые написали комментарий, даже если их собственные статьи никто не комментировал... не говоря уже о том, что если нет явной необходимости аггрегирования полей в группе, то использование GROUP BY там, где можно обойтись DISTINCT - дорогое удовольствие (почему - объясню дальше, а пока, просто для сравнения, его план и "стоимость"):
5ab3f036b072d356066942.png
Чтобы просто получить нужный результат, его, конечно, можно тупо дополнить еще одним JOIN с article_comment_association, но это все еще очень плохо: во-первых, JOIN с таблицей комментариев там просто лишний, во-вторых, GROUP BY - все то же разбазаривание ресурсов:
5ab3f0802b299318193006.png
Вот, для сравнения, стоимость DISTINCT vs. GROUP BY:
5ab3f08c07b85920446589.png
(Все эти неправильные варианты приводить не буду, чтоб их случайно не скопипейстили в систему управления ядерным реактором!)

В этом смысле вариант, предложеный Rsa97 , уже лучше, т.к. дает правильный результат.
SELECT name FROM users
  WHERE id IN (
    SELECT user_id FROM article
      WHERE id IN (
        SELECT article_id FROM article_comment_association
      )
  );

Однако, использование subquery в таком порядке, действительно, не позволяет использовать distinct:
5ab3f109468d6235394789.png
Фишка в том, что subquery, как правило, создают временную таблицу, обычно, в памяти, но если ее мало, то и на диске. Так что, если есть возможность заменить их на JOIN (а она есть почти всегда!), это нужно делать, не стесняясь.

А вот феншуйная (она же - правильная, легко читаемая, очевидная и эффективная) версия запроса:
SELECT distinct users.name from users
  INNER JOIN article ON (article.user_id = users.id)
  INNER JOIN article_comment_association ON (article.id = article_comment_association.article_id)

... и ее план:
5ab3f135365b0576447042.png
Мораль истории: в реляционной базе данных самый прямой путь к нужному результату, как правило, оказывается наиболее эффективным. Как общее правило - начинать нужно с самого большого множества записей, исключая за раз как можно больше ненужного, и давая оптимизатору использовать индексы.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
SELECT `name`
  FROM `users`
  WHERE `id` IN (
    SELECT `user_id`
      FROM `article`
      WHERE `id` IN (
        SELECT `article_id`
          FROM `article_comment_association`
      )
  )
Ответ написан
@d-stream
Готовые решения - не подаю, но...
Бегло - похоже на верный вариант.
Кстати можно подойти и с обратной стороны:

комментарии - статьи - пользователи
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы