Как написать запрос MySQL, который ограничит результаты объединенной таблицы или таблиц, а также подсчитает количество элементов в объединенной таблице или таблицах? - PullRequest
1 голос
/ 29 января 2010

Как бы вы написали 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)

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

Ответы [ 2 ]

0 голосов
/ 29 января 2010

Я бы сказал, что вам придется использовать несколько запросов и циклов для чего-то подобного.
Там может быть способ, но его вне времени у меня есть :)
Вот некоторый код 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
    }
}
0 голосов
/ 29 января 2010

Честно говоря, я бы делал это в нескольких запросах, чтобы избежать коррелированных подзапросов.

А вот и вы:

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_tasks и 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
. . .

Обратите внимание, что это , а не коррелированный подзапрос; СУБД должна выполнить подзапрос только один раз.

Я использовал DESC, потому что я предполагаю, что вы хотите самые последние проекты.

...