Нужна помощь с запросом нескольких таблиц в MySQL - PullRequest
0 голосов
/ 20 сентября 2009

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

Для моего форума у ​​меня есть 4 основных таблицы:

  • USERS
  • TOPICS
  • POSTS
  • COMMENTS

ТЕМЫ таблица: идентификатор (с автоинкрементом), строка темы.

ПОЛЬЗОВАТЕЛИ таблица: имя пользователя, адрес электронной почты, имя и фамилия и несколько других не связанных строк

POSTS таблица: id (с автоинкрементом), post-title, post-body, идентификатор темы, id пользователя, post-date, update-date, updated-by (который будет содержать идентификатор пользователя, который сделал самый последний комментарий)

КОММЕНТАРИИ таблица: id (с автоинкрементом), post-id, id пользователя и комментарий


На главной странице форума мне хотелось бы иметь:

  • список всех тем
  • количество постов по каждой теме
  • последнее обновленное сообщение, и кто его обновил
  • самая последняя обновленная тема, которая, вероятно, стоит на вершине, скорее всего, "ORDER BY update-date"

Вот мой запрос:

SELECT topics.id AS topic-id, 
       topics.topic, 
       post-user.id AS user-id, 
       CONCAT_WS(' ', post-user.first-name, post-user.last-name) AS name, 
       recent-post.id AS post-id, 
       post-num.post-total, 
       recent-post.title AS post-title, 
       recent-post.update_date AS updated-date, 
       recent-post.updated-by AS updated-by
  FROM topics
  JOIN (SELECT posts.topic-id,
               COUNT(*) AS post-total                 
          FROM POSTS
         WHERE posts.topic-id = topic-id 
      GROUP BY posts.topic-id) AS post-num ON topics.id = post-num.topic-id
  JOIN (SELECT posts.* 
          FROM posts 
      ORDER BY posts.update-date DESC) AS recent-post ON topics.id = recent-post.topic-id 
  JOIN  (SELECT users.*, 
                posts.user-id 
           FROM users, posts 
          WHERE posts.user-id = users.id) as post-user ON recent-post.user_id = post-user.id 
GROUP BY topics.id

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

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

Ответы [ 4 ]

1 голос
/ 20 сентября 2009
  • Тире не является допустимым символом в идентификаторах SQL, но вместо него можно использовать «_».
  • Вам не обязательно получать все из одного запроса SQL. Фактически, попытка сделать это усложняет кодирование, а также иногда усложняет выполнение оптимизатора SQL.
  • Нет смысла использовать ORDER BY в подзапросе.
  • Назовите столбцы первичного ключа topic_id, user_id и т. Д. (Вместо "id" в каждой таблице), и вам не нужно будет указывать их псевдоним в списке выбора.

Вот как бы я решил это:

Сначала получите самое последнее сообщение по теме с соответствующей информацией о пользователе:

SELECT t.topic_id, t.topic,
  u.user_id, CONCAT_WS(' ', u.first_name, u.last_name) AS full_name,
  p.post_id, p.title, p.update_date, p.updated_by
FROM topics t
INNER JOIN 
  (posts p INNER JOIN users u ON (p.updated_by = u.user_id))
  ON (t.topic_id = p.topic_id)
LEFT OUTER JOIN posts p2
  ON (p.topic_id = p2.topic_id AND p.update_date < p2.update_date)
WHERE p2.post_id IS NULL;

Затем получите количество сообщений по теме в отдельном, более простом запросе.

SELECT t.topic_id, COUNT(*) AS post_total
FROM topics t LEFT OUTER JOIN posts p USING (topic_id)
GROUP BY t.topic_id;

Объедините два набора данных в вашем приложении.

0 голосов
/ 20 сентября 2009

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

select
    s.topic_id,
    s.topic,
    u.user_id as last_updated_by_id,
    u.user_name as last_updated_by,
    s.last_post,
    s.post_count
from
    (
        select
            t.id as topic_id,
            t.topic,
            t.user_id as orig_poster,
            max(coalesce(p.post_date, t.post_date)) as last_post,
            count(*) as post_count --would be p.post_id if you don't want to count the topic
        from
            topics t
            left join posts p on
                t.id = p.topic_id
        group by
            t.topic_id,
            t.topic,
            t.user_id
    ) s
    left join posts p on
        s.topic_id = p.topic_id
        and s.last_post = p.post_date
        and s.post_count > 1 --0 if you're using p.post_id up top
    inner join users u on
        u.id = coalesce(p.user_id, s.orig_poster)
order by 
    s.last_post desc

Этот запрос вводит coalesce и left join, и это очень хорошая концепция для изучения. Для двух аргументов (как здесь используется), вы также можете использовать ifnull в MySQL, поскольку это функционально эквивалентно.

Имейте в виду, что это эксклюзивно для MySQL (если вам нужно портировать этот код). Другие базы данных имеют другие функции для этого (isnull в SQL Server, nvl в Oracle и т. Д. И т. Д.). Я использовал coalesce, чтобы сохранить этот запрос в формате ANSI.

0 голосов
/ 20 сентября 2009

Это очень сложный запрос. Обратите внимание, что заявления JOIN ограничивают ваши темы тем, у которых есть сообщения. Если в теме нет сообщения, оператор JOIN отфильтрует его.

Попробуйте следующий запрос.

SELECT * 
FROM
(
  SELECT T.Topic, 
         COUNT(AllTopicPosts.ID) NumberOfPosts, 
         MAX(IFNULL(MostRecentPost.Post-Title, '') MostRecentPostTitle,
         MAX(IFNULL(MostRecentPostUser.UserName, '') MostRecentPostUser
         MAX(IFNULL(MostRecentPost.Updated_Date, '') MostRecentPostDate
  FROM TOPICS
  LEFT JOIN POSTS AllTopicPosts ON AllTopicPosts.Topic_Id = TOPICS.ID
  LEFT JOIN 
     (
       SELECT * 
       FROM Posts P
       WHERE P.Topic_id = TOPICS.id
       ORDER BY P.Updated_Date DESC
       LIMIT 1
     ) MostRecentPost ON MostRecentPost.Topic_Id = TOPICS.ID
  LEFT JOIN USERS MostRecentPostUser ON MostRecentPostUser.ID = MostRecentPost.User_Id
  GROUP BY T.Topic
)
ORDER BY MostRecentPostDate DESC
0 голосов
/ 20 сентября 2009

, чтобы получить результаты по темам без публикаций, вам нужно использовать LEFT JOIN вместо JOIN для первого объединения тем и следующей таблицы. LEFT JOIN означает «всегда возвращать строку набора результатов для каждой строки в левой таблице, даже если нет совпадения с правой таблицей».

Мне нужно идти, но я постараюсь посмотреть на вопросы эффективности позже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...