Почему результаты этого запроса MySQL умножаются друг на друга? - PullRequest
2 голосов
/ 02 апреля 2009
SELECT user_id,
    SUM(COALESCE(point_points, 0)) AS total_points,
    SUM(
        CASE
            WHEN point_date > '$this_month'
            THEN point_points
            ELSE 0
        END)                AS month_points,
    COUNT(DISTINCT c_id)    AS num_comments,
    COUNT(DISTINCT rant_id) AS live_submissions
FROM users
    LEFT JOIN points
    ON  users.user_id = points.point_userid
    LEFT JOIN comments
    ON
        (
            c_userid = user_id
        )
    LEFT JOIN rants
    ON
        (
            rant_poster = user_id
        AND rant_status = 1
        )
WHERE user_id = $id
GROUP BY user_id

В основном переменная live_submissions и num_comments отображает правильные результаты, тогда как total_points и month_points отображают произведение month_points/total_points, live_submissions и num_comments. Есть идеи, почему это происходит?

Ответы [ 3 ]

9 голосов
/ 02 апреля 2009

Это называется декартово произведение . Когда вы объединяете таблицы вместе, по умолчанию получается каждая перестановка строк , для которых выполняются условия соединения. Вы используете JOIN условия для ограничения этих перестановок.

Но поскольку вы объединяете несколько таблиц в users, результат включает в себя каждую перестановку каждой соответствующей таблицы. Например, каждая совпадающая строка в points повторяется для каждой совпадающей строки в comments, и каждая из них снова умножается, повторяясь для соответствующей строки в rants.

Вы можете частично компенсировать это с помощью COUNT(DISTINCT c_id), как вы делаете, но DISTINCT необходим только потому, что у вас есть несколько строк на c_id. И это не сработает, если вы не примените его к уникальным значениям. Это средство не работает для выражений SUM().

Обычно вы пытаетесь выполнить слишком много вычислений в одном запросе. Вам нужно разделить его на отдельные запросы, чтобы он был надежным. И тогда вы также можете избавиться от модификаторов DISTINCT.

SELECT u.user_id, SUM(COALESCE(p.point_points, 0)) AS total_points, 
  SUM( CASE WHEN p.point_date > '$this_month' THEN p.point_points ELSE 0 END ) AS month_points
FROM users u LEFT JOIN points p
  ON u.user_id = p.point_userid 
WHERE u.user_id = $id
GROUP BY u.user_id;

SELECT user_id, COUNT(c.c_id) as num_comments, 
FROM users u LEFT JOIN comments c
  ON (c.c_userid = u.user_id)
WHERE u.user_id = $id
GROUP BY u.user_id;

SELECT u.user_id, COUNT(r.rant_id) as live_submissions
FROM users u LEFT JOIN rants r
  ON (r.rant_poster = u.user_id AND r.rant_status = 1)
WHERE u.user_id = $id
GROUP BY u.user_id;

Вы не должны пытаться выполнить все три из них в одном запросе.

0 голосов
/ 02 апреля 2009

Если вы посмотрите на вывод запроса до группировки, то увидите проблему. Несколько строк будут возвращены пользователю, если они имеют более 1 записи в любой из соединенных таблиц. Таким образом, если у пользователя есть 2 записи комментариев, то также будут возвращены записи с 2 точками.

В качестве упрощенного примера ...

Таблица пользователей

имя пользователя

1 Фред

Таблица очков

UserId Points

1 10

Таблица комментариев

userId Комментарий

1 Здесь

1 Там

Выбор * из этих таблиц приведет к

UserId Баллы Комментарий

1 10 Здесь

1 10 Там

Я не совсем уверен в синтаксисе MYSQL, но вы хотели бы что-то вроде

SELECT UserId, C.num_comments, P.total_points
FROM users
LEFT JOIN 
   (SELECT c_userId, COUNT(DISTINCT c_id) as num_comments
    FROM Comments
    GROUP BY c_userId)
    AS C
    ON UserId = c_userid
LEFT JOIN 
   (SELECT point_userId, sum(COALESCE(point_points, 0)) as total_points
    FROM Points
    GROUP BY point_userId)
    AS P
    ON UserId = point_userid
0 голосов
/ 02 апреля 2009

Можете ли вы предоставить пример вывода?

Я думаю, что это как-то связано с добавлением высказываний и комментариев к точкам Можете ли вы попробовать удалить таблицы рантов и комментариев?

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