简体   繁体   English

在MySQL中需要多表查询的帮助

[英]Need help with a multiple table query in mysql

I'm working on building a forum with kohana. 我正在与kohana建立论坛。 I know there is already good, free, forum software out there, but it's for a family site, so I thought I'd use it as a learning experience. 我知道那里已经有不错的,免费的论坛软件,但这是针对家庭网站的,所以我想我会将其用作学习体验。 I'm also not using the ORM that is built into Kohana, as I would like to learn more about SQL in the process of building the forum. 我也没有使用Kohana内置的ORM,因为我想在构建论坛的过程中进一步了解SQL。

For my forum I have 4 main tables: 对于我的论坛,我有4个主要表格:

  • USERS
  • TOPICS
  • POSTS
  • COMMENTS

TOPICS table: id (auto incremented), topic row. 主题表:ID(自动递增),主题行。

USERS table: username, email, first and last name and a few other non related rows USERS表:用户名,电子邮件,姓氏和其他一些不相关的行

POSTS table: id (auto incremented), post-title, post-body, topic-id, user-id, post-date, updated-date, updated-by(which will contain the user-id of the person who made the most recent comment) POSTS表:id(自动递增),post-title,post-body,topic-id,user-id,post-date,updated-date,updated-by(其中将包含创建该人员的用户ID)最近的评论)

COMMENTS table: id (auto incremented), post-id, user-id and comment 注释表:id(自动递增),post-id,user-id和注释


On the main forum page I would like to have: 我希望在论坛主页面上:

  • a list of all of the topics 所有主题的列表
  • the number of posts for each topic 每个主题的帖子数
  • the last updated post, and who updated it 最近更新的帖子,以及谁更新的
  • the most recently updated topic to be on top, most likely an "ORDER BY updated-date" 最重要的是最近更新的主题,最有可能是“ ORDER BY Updated-date”

Here is the query I have so far: 这是我到目前为止的查询:

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

This query almost works as it will get all of information for topics that have posts. 该查询几乎可以正常工作,因为它将获取有关具有帖子主题的所有信息。 But it doesn't return the topics that don't have any posts . 但是它不会返回没有任何帖子的主题

I'm sure that the query is inefficient and wrong since it makes two sub-selects to the posts table, but it was the only way I could get to the point I'm at. 我确定查询效率低下并且是错误的,因为它对posts表进行了两个子选择,但这是我所要指出的唯一方法。

  • Dash is not a valid character in SQL identifiers, but you can use "_" instead. 破折号不是SQL标识符中的有效字符,但是您可以改用“ _”。
  • You don't necessarily have to get everything from a single SQL query. 您不必一定要从单个SQL查询中获取所有内容 In fact, trying to do so makes it harder to code, and also sometimes makes it harder for the SQL optimizer to execute. 实际上,尝试这样做会使编写代码变得更加困难,并且有时还会使SQL优化器难以执行。
  • It makes no sense to use ORDER BY in a subquery. 在子查询中使用ORDER BY没有任何意义。
  • Name your primary key columns topic_id , user_id , and so on (instead of " id " in every table), and you won't have to alias them in the select-list. 将您的主键列topic_iduser_id等(而不是每个表中的“ id ”),并且您不必在选择列表中使用别名。

Here's how I would solve this: 这是我要解决的方法:

First get the most recent post per topic, with associated user information: 首先获取每个主题的最新帖子,以及相关的用户信息:

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;

Then get the counts of posts per topic in a separate, simpler query. 然后在一个单独的,更简单的查询中获取每个主题的帖子数。

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

Merge the two data sets in your application. 合并您的应用程序中的两个数据集。

to ensure you get results for topics without posts, you'll need to use LEFT JOIN instead of JOIN for the first join between topics and the next table. 为了确保获得没有帖子的主题的结果,您将需要使用LEFT JOIN而不是JOIN进行主题和下一张表之间的第一次连接。 LEFT JOIN means "always return a result set row for every row in the left table, even if there's no match with the right table." LEFT JOIN的意思是“即使与右表不匹配,也总是为左表中的每一行返回结果集行。”

Gotta go now, but I'll try to look at the efficiency issues later. 现在要走了,但是稍后我将尝试研究效率问题。

I'd use a left join inside a subquery to pull back the correct topic, and then you can do a little legwork outside of that to get some of the user info. 我将在子查询中使用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

This query does introduce coalesce and left join , and they are very good concepts to look into. 该查询确实引入了coalesceleft join ,这是值得研究的非常好的概念。 For two arguments (like used here), you can also use ifnull in MySQL, since it is functionally equivalent. 对于两个参数(如此处使用的参数),您也可以在MySQL中使用ifnull ,因为它在功能上是等效的。

Keep in mind that that's exclusive to MySQL (if you need to port this code). 请记住,这是MySQL专有的(如果您需要移植此代码)。 Other databases have other functions for that ( isnull in SQL Server, nvl in Oracle, etc., etc.). 其它数据库有那个(其他功能isnull在SQL Server中, nvl在Oracle中,等等,等等)。 I used coalesce so that I could keep this query all ANSI-fied. 我使用了coalesce以便可以保留所有ANSI标准的查询。

This is a very complicated query. 这是一个非常复杂的查询。 You should note that JOIN statements will limit your topics to those that have posts. 您应该注意,JOIN语句会将您的主题限制为那些发表文章的主题。 If a topic does not have a post, a JOIN statement will filter it out. 如果主题没有帖子,则JOIN语句将其过滤掉。

Try the following query. 请尝试以下查询。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM