为什么这个 MySQL 查询的结果会相互相乘?

发布于 2024-07-16 09:25:57 字数 908 浏览 5 评论 0原文

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_submissionsnum_comments 变量显示正确的结果,而 total_pointsmonth_points 显示 month_points 的乘积/total_pointslive_submissionsnum_comments。 知道为什么会发生这种情况吗?

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

Basically live_submissions and num_comments variable display proper results, while the total_points and month_points display a product of month_points/total_points, live_submissions and num_comments. Any idea why this is happening?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

嘿看小鸭子会跑 2024-07-23 09:25:57

这称为笛卡尔积。 将表连接在一起时,默认结果是连接条件成立的行的每个排列。 您可以使用 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;

您不应该尝试在单个查询中完成所有这三个操作。

This is called a Cartesian Product. When you join the tables together, the default result is every permutation of rows for which the join conditions are true. You use JOIN conditions to limit these permutations.

But since you are joining multiple tables to users, the result includes every permutation of each matching table. For example, each matching row in points is repeated per matching row in comments, and each of these is multiplied again, repeating per matching row in rants.

You can partially compensate for this with COUNT(DISTINCT c_id) as you are doing, but the DISTINCT is necessary only because you have multiple rows per c_id. And it doesn't work unless you apply it to unique values. This remedy doesn't work for the SUM() expressions.

Basically, you're trying to do too many calculations in one query. You need to split it up into separate queries for it to be reliable. And then you can get rid of the DISTINCT modifiers, too.

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;

You shouldn't try to do all three of these in a single query.

怪我入戏太深 2024-07-23 09:25:57

您能提供一些示例输出吗?

我认为这与在观点中添加咆哮和评论有关。 您能尝试删除咆哮和评论表吗?

Can you provide some sample output?

I think it has something to do with adding rants and comments in with the points. Can you try to remove the rants and comments tables?

意犹 2024-07-23 09:25:57

如果您在分组之前查看查询输出,那么您就会发现问题。 如果用户在任何连接表中拥有超过 1 条记录,则将为用户返回多行。 因此,如果用户有 2 条评论记录,那么也会返回 2 条积分记录。

作为一个简化的例子......

用户表

用户ID名称

1 弗雷德

积分表

用户ID积分

1 10

评论表

用户ID评论

这里 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

If you look at the query output before grouping then you'll see the problem. Multiple rows will be returned for a user if they have more than 1 record in any of the joined tables. So if a user has 2 comment records then 2 point records will also be returned.

As a simplified example...

User Table

userId name

1 Fred

Point table

userId Points

1 10

Comments table

userId Comment

1 Here

1 There

Selecting * from these tables will result in

userId Points Comment

1 10 Here

1 10 There

I'm not entirely sure of the MYSQL syntax but you would want something like

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文