1

您将如何编写一个 MySQL 查询来限制连接表的结果(或子选择,如果效果更好)并计算连接表中的项目数?

例如,假设您有三个表:项目、任务和评论,其中一个项目有 0 个或多个任务,一个任务有 0 个或多个评论。您如何将每个项目返回的任务数限制为 3,并返回每个项目的任务总数和每个任务的评论?

这是我想象的结果集的样子:

project_id, project_title, task_id, task_title, num_tasks, num_comments
------------------------------------------------------------------------
1, Project1, 1, Task1, 4, 3
1, Project1, 2, Task2, 4, 0
1, Project1, 3, Task3, 4, 9
2, Project2, 10, Task10, 20, 0
2, Project2, 11, Task11, 20, 0
2, Project2, 12, Task12, 20, 2
3, Project3, 20, Task20, 17, 5
3, Project3, 21, Task21, 17, 1
3, Project3, 22, Task22, 17, 2

其中“Project1”、“Project2”等仅代表项目的标题,“Task1”、“Task2”等代表任务的标题。

最终,(在解析查询结果后)我希望能够显示如下内容:

 Project1 (4 tasks)
     Task1 (3 comments)
     Task2 (0 comments)
     Task3 (9 comments)
 Project2 (20 tasks)
     Task10 (0 comments)
     Task11 (0 comments)
     Task12 (2 comments)
 Project3 (17 tasks)
     Task20 (5 comments)
     Task21 (1 comments)
     Task22 (2 comments)

我猜这必须通过子选择来完成(这很好),但我似乎无法弄清楚如何仅使用连接来完成此操作,而且我对子选择的处理还不够好做这样的事情。

4

2 回答 2

0

老实说,我会在多个查询中这样做,以避免相关的子查询。

但是给你:

SELECT p.project_id, p.project_title,
    t1.task_id, t1.task_title,
    (SELECT COUNT(*) FROM tasks t 
       WHERE t.project_id = p.project_id) AS num_tasks,
    COALESCE((SELECT COUNT(*) FROM comments c
       WHERE c.task_id = t1.task_id), 0) AS num_comments
FROM projects p
JOIN tasks t1 ON (p.project_id = t1.project_id)
LEFT OUTER JOIN tasks t2 
  ON (p.project_id = t2.project_id AND t1.task_id > t2.task_id)
GROUP BY t1.task_id
HAVING COUNT(*) < 3;

考虑到像上面 ( num_tasksand num_comments)这样的相关子查询必须执行很多次——对于t1.

您可以通过单独运行这些查询并在应用程序代码中组合结果来获得结果:

SELECT p.project_id, p.project_title,
    t1.task_id, t1.task_title
FROM projects p
JOIN tasks t1 ON (p.project_id = t1.project_id)
LEFT OUTER JOIN tasks t2 
  ON (p.project_id = t2.project_id AND t1.task_id > t2.task_id)
GROUP BY t1.task_id
HAVING COUNT(*) < 3;

SELECT task_id, COUNT(*) AS num_comments
FROM comments
WHERE task_id IN (...list of task_id values from first query...)
GROUP BY task_id;

SELECT project_id, COUNT(*) AS num_tasks
FROM tasks
GROUP BY project_id;

即使像这样运行三个单独的查询,总体上也可能比运行将所有结果放在一起的更复杂的查询要快。我说可能是因为这取决于我们谈论的数据量。可以肯定的是,您必须使用自己的数据库测试这两种解决方案。


关于您的后续问题,我将在子查询中执行此操作:

SELECT p.project_id, p.project_title,
    t1.task_id, t1.task_title
FROM (SELECT * FROM projects ORDER BY last_updated DESC LIMIT 5) p
. . .

请注意,这不是相关子查询;RDBMS 只需执行一次子查询。

我使用DESC是因为我假设您想要最新的项目。

于 2010-01-29T01:52:58.030 回答
0

我会说你必须为这样的事情使用多个查询和循环。
可能有一种方法,但它超出了我的时间:)
这里有一些 suedo 代码来展示我如何完成这个

select project_id, project_title from projects
select project_id, count(*) As num_tasks from tasks group by project_id
select task_id, count(*) As num_comment from comments group by task_id

foreach (int projectId in projects.Rows)
{
    select task_id, task_title from tasks where project_id = projectID limit 3
    foreach (int taskID in tasks.Rows)
    {
        select comment_id, comment from comments limit 3
    }
}
于 2010-01-29T02:07:46.177 回答