Mysql 具有多个左联接的查询-点列值不正确
我有以下数据库结构,我正在尝试运行一个查询,该查询将显示教室、教室中有多少学生、教室分配了多少奖励以及分配给单个教室的分数(基于教室id列) 使用最底部的查询,我试图收集教室分配的“totalPoints”——基于计算教室兑换代码表中的points列,并将其作为单个整数返回 由于某些原因,这些值对于totalPoints是不正确的-我做错了什么,但不确定是什么 --更新-- 以下是sqlfiddle:- 我的结构:Mysql 具有多个左联接的查询-点列值不正确,mysql,database,join,left-join,Mysql,Database,Join,Left Join,我有以下数据库结构,我正在尝试运行一个查询,该查询将显示教室、教室中有多少学生、教室分配了多少奖励以及分配给单个教室的分数(基于教室id列) 使用最底部的查询,我试图收集教室分配的“totalPoints”——基于计算教室兑换代码表中的points列,并将其作为单个整数返回 由于某些原因,这些值对于totalPoints是不正确的-我做错了什么,但不确定是什么 --更新-- 以下是sqlfiddle:- 我的结构: CREATE TABLE `organisation_classrooms`
CREATE TABLE `organisation_classrooms` (
`classroom_id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`active` tinyint(1) NOT NULL,
`organisation_id` int(11) NOT NULL,
`period` int(1) DEFAULT '0',
`classroom_bg` int(2) DEFAULT '3',
`sortby` varchar(6) NOT NULL DEFAULT 'points',
`sound` int(1) DEFAULT '0',
PRIMARY KEY (`classroom_id`)
);
CREATE TABLE organisation_classrooms_myusers (
`classroom_id` int(11) NOT NULL,
`user_id` bigint(11) unsigned NOT NULL,
);
CREATE TABLE `classroom_redeemed_codes` (
`redeemed_code_id` int(11) NOT NULL AUTO_INCREMENT,
`myuser_id` bigint(11) unsigned NOT NULL DEFAULT '0',
`ssuser_id` bigint(11) NOT NULL DEFAULT '0',
`classroom_id` int(11) NOT NULL,
`order_product_id` int(11) NOT NULL DEFAULT '0',
`order_product_images_id` int(11) NOT NULL DEFAULT '0',
`date_redeemed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`points` int(11) NOT NULL,
`type` int(1) NOT NULL DEFAULT '0',
`notified` int(1) NOT NULL DEFAULT '0',
`inactive` tinyint(3) NOT NULL,
PRIMARY KEY (`redeemed_code_id`),
);
SELECT
t.classroom_id,
title,
COALESCE (
COUNT(DISTINCT r.redeemed_code_id),
0
) AS totalRewards,
COALESCE (
COUNT(DISTINCT ocm.user_id),
0
) AS totalStudents,
COALESCE (sum(r.points), 0) AS totalPoints
FROM
`organisation_classrooms` `t`
LEFT OUTER JOIN classroom_redeemed_codes r ON (
r.classroom_id = t.classroom_id
AND r.inactive = 0
AND (
r.date_redeemed >= 1393286400
OR r.date_redeemed = 0
)
)
LEFT OUTER JOIN organisation_classrooms_myusers ocm ON (
ocm.classroom_id = t.classroom_id
)
WHERE
t.organisation_id =37383
GROUP BY title
ORDER BY t.classroom_id ASC
LIMIT 10
--编辑--
哎呀!我有时讨厌SQL。。。我犯了一个很大的错误,我试图计算教室里的学生人数,而不是组织教室里的学生人数。我真的很抱歉我应该早点把它捡起来
classroom_id | totalUniqueStudents
16 1
17 2
46 1
51 1
52 1
教室\兑换\代码表中有7行,但由于教室\ id 46有两行,尽管具有相同的myuser\ id(这是学生id),这应显示为一个唯一的学生
这有意义吗?基本上是试图根据myuser\u id列获取教室中唯一学生的数量
e、 g教室id 46在教室代码表中可以有100行,但如果每个都是相同的myuser id,则应显示totalUniqueStudents计数为1,而不是100
如果不清楚,请告诉我
--更新--
我有下面的查询,似乎是从下面的一个用户那里借用的,似乎是有效的。。。(我头疼)我会再次接受答案。抱歉搞混了,我想我只是想得太多了
select crc.classroom_id,
COUNT(DISTINCT crc.myuser_id) AS users,
COUNT( DISTINCT crc.redeemed_code_id ) AS classRewards,
SUM( crc.points ) as classPoints, t.title
from classroom_redeemed_codes crc
JOIN organisation_classrooms t
ON crc.classroom_id = t.classroom_id
AND t.organisation_id = 37383
where crc.inactive = 0
AND ( crc.date_redeemed >= 1393286400
OR crc.date_redeemed = 0 )
group by crc.classroom_id
您需要sum(r.points)和左外部联接中的一个子查询,请参见下文
SELECT
t.classroom_id,
title,
COALESCE (
COUNT(DISTINCT r.redeemed_code_id),
0
) AS totalRewards,
COALESCE(sum(r.points),0) AS totalPoints
,COALESCE(sum(T1.cnt),0) as totalStudents
FROM
`organisation_classrooms` `t`
left outer join (select classroom_id, count(user_id) cnt
from organisation_classrooms_myusers
group by classroom_id) T1 on (T1.classroom_id=t.classroom_id)
LEFT OUTER JOIN classroom_redeemed_codes r ON (
r.classroom_id = t.classroom_id
AND r.inactive = 0
AND (
r.date_redeemed >= 1393286400
OR r.date_redeemed = 0
)
)
WHERE
t.organisation_id =37383
GROUP BY title
ORDER BY t.classroom_id ASC
LIMIT 10
我首先对每个特定类的分数进行预查询聚合,然后使用左连接。我得到的结果集中的行数比您的示例预期的多,但是没有MySQL直接测试/确认。但是,通过使用点的总和进行查询,并在应用users表时获得笛卡尔结果,这可能是复制点的基础。通过预先查询兑换代码本身,您只需获取该值,然后加入用户
SELECT
t.classroom_id,
title,
COALESCE ( r.classRewards, 0 ) AS totalRewards,
COALESCE ( r.classPoints, 0) AS totalPoints,
COALESCE ( r.uniqStudents, 0 ) as totalUniqRedeemStudents,
COALESCE ( COUNT(DISTINCT ocm.user_id), 0 ) AS totalStudents
FROM
organisation_classrooms t
LEFT JOIN ( select crc.classroom_id,
COUNT( DISTINCT crc.redeemed_code_id ) AS classRewards,
COUNT( DISTINCT crc.myuser_id ) as uniqStudents,
SUM( crc.points ) as classPoints
from classroom_redeemed_codes crc
JOIN organisation_classrooms t
ON crc.classroom_id = t.classroom_id
AND t.organisation_id = 37383
where crc.inactive = 0
AND ( crc.date_redeemed >= 1393286400
OR crc.date_redeemed = 0 )
group by crc.classroom_id ) r
ON t.classroom_id = r.classroom_id
LEFT OUTER JOIN organisation_classrooms_myusers ocm
ON t.classroom_id = ocm.classroom_id
WHERE
t.organisation_id = 37383
GROUP BY
title
ORDER BY
t.classroom_id ASC
LIMIT 10
我简化了你的问题;无需将
COALLESCE
与COUNT()
一起使用,因为COUNT()
从不返回NULL
。对于SUM()。下面显示的结果仅包含教室id
16、#17和#46的数据,以便与问题中提供的示例进行比较。实际结果集更大,包含表中的所有教室id
s。然而,不需要他们的存在来理解它的工作方式和原因
SELECT
t.classroom_id,
t.title,
COUNT(DISTINCT r.redeemed_code_id) AS totalRewards,
COUNT(DISTINCT ocm.user_id) AS totalStudents,
IFNULL(SUM(r.points), 0) AS totalPoints
FROM `organisation_classrooms` t
LEFT JOIN `classroom_redeemed_codes` r
ON r.classroom_id = t.classroom_id
AND r.inactive = 0
AND (r.date_redeemed >= 1393286400 OR r.date_redeemed = 0)
LEFT JOIN `organisation_classrooms_myusers` ocm
ON ocm.classroom_id = t.classroom_id
WHERE t.organisation_id = 37383
GROUP BY t.classroom_id
ORDER BY t.classroom_id ASC
让我们试着把它分成几部分,然后再把它们放在一起。首先,让我们看看选择了哪些用户:
问题#1
我删除了教室\u兑换的\u代码
表和it字段,删除了分组依据
,并将聚合函数计数(ocm.user\u id)
替换为ocm.user\u id
,以查看选择了哪些用户
结果显示查询的这一部分是正确的:
classroom_id | title | user_id
-------------+-------+--------
16 | BLUE | 2
16 | BLUE | 1
17 | GREEN | 508835
17 | GREEN | 508826
46 | PINK | NULL
16号教室有2名用户,7号教室有2名用户,46号教室没有用户。
返回groupby
子句将使其返回totalStudents
列中的正确值(2,2,0)
现在让我们检查一下与表教室\u兑换\u代码的关系
:
问题2
结果是:
classroom_id | title | redeemed_code_id | points
-------------+-------+------------------+-------
16 | BLUE | 7 | 50
17 | GREEN | 8 | 25
17 | GREEN | 9 | 75
46 | PINK | 5 | 250
46 | PINK | 6 | 100
同样,按教室id
分组将在totalRewards
列中产生(1,2,2)和totalPoints
列中产生(50,100,350),这是正确的
当您想要将这些组合到单个查询中时,问题就开始了。无论您使用哪种联接,对于提供的输入,您将获得教室id
的(2*1、2*2、1*2)行,其值为16、17和46(按此顺序)。我在括号中乘以的值是第一个和上面的查询结果集中每个教室id
的行数
合二为一
让我们尝试在分组行之前选择行的查询:
SELECT
t.classroom_id,
t.title,
r.redeemed_code_id, ocm.user_id, r.points
FROM `organisation_classrooms` t
LEFT JOIN `classroom_redeemed_codes` r
ON r.classroom_id = t.classroom_id
AND r.inactive = 0
AND (r.date_redeemed >= 1393286400 OR r.date_redeemed = 0)
LEFT JOIN `organisation_classrooms_myusers` ocm
ON ocm.classroom_id = t.classroom_id
WHERE t.organisation_id = 37383
ORDER BY t.classroom_id ASC
它返回此结果集:
classroom_id | title | redeemed_code_id | user_id | points
-------------+-------+------------------+---------+-------
16 | BLUE | 7 | 2 | 50
16 | BLUE | 7 | 1 | 50 <- *
-------------+-------+------------------+---------+-------
17 | GREEN | 8 | 508835 | 25
17 | GREEN | 8 | 508826 | 25 <- *
17 | GREEN | 9 | 508835 | 75
17 | GREEN | 9 | 508826 | 75 <- *
-------------+-------+------------------+---------+-------
46 | PINK | 5 | NULL | 250
46 | PINK | 6 | NULL | 100
classroom_id | title | totalRewards | totalStudents | totalPoints
-------------+-------+--------------+---------------+-------------
16 | BLUE | NULL | 2 | NULL
16 | BLUE | 1 | NULL | 50
17 | GREEN | NULL | 2 | NULL
17 | GREEN | 2 | NULL | 100
46 | PINK | NULL | 0 | NULL
46 | PINK | 2 | NULL | 350
注意ORDER BY
子句适用于UNION
ed结果集。如果要对每个的行进行排序,请选择(这没有帮助,因为UNION
没有保持顺序),您需要将该查询括在括号中,并将order BY
子句放在括号中
结果集看起来很棒:
classroom_id | title | redeemed_code_id | user_id | points
-------------+-------+------------------+---------+-------
16 | BLUE | NULL | 1 | NULL
16 | BLUE | NULL | 2 | NULL
16 | BLUE | 7 | NULL | 50
-------------+-------+------------------+---------+-------
17 | GREEN | 8 | NULL | 25
17 | GREEN | 9 | NULL | 75
17 | GREEN | NULL | 508826 | NULL
17 | GREEN | NULL | 508835 | NULL
-------------+-------+------------------+---------+-------
46 | PINK | 5 | NULL | 250
46 | PINK | 6 | NULL | 100
46 | PINK | NULL | NULL | NULL
现在,我们可以在上面的查询周围加上一些括号(striporderby
),并在另一个查询中使用它,根据choork\u id
对数据进行分组,计算用户和兑换的代码,并将其积分相加
您将得到一个看起来很糟糕的查询,在当前的数据库模式下,当您的表有几百行时,查询将爬行这就是我不在这里写的原因
注意强>
在查询的on
、WHERE
、ORDER by
和GROUP by
子句中出现的字段上,通过向表中添加缺少的索引,可以提高其性能
这将带来显著的改善,但我不太相信这一点。对于真正大的表(数十万行),它仍然会爬行
另一个想法
我们还可以在Query#1和Query#2上首先添加groupby
,然后将它们合并为UNION
:
SELECT
t.classroom_id,
t.title,
NULL AS totalRewards,
COUNT(DISTINCT ocm.user_id) AS totalStudents,
NULL AS totalPoints
FROM `organisation_classrooms` t
LEFT JOIN `organisation_classrooms_myusers` ocm
ON ocm.classroom_id = t.classroom_id
WHERE t.organisation_id = 37383
GROUP BY t.classroom_id
UNION ALL
SELECT
t.classroom_id,
t.title,
COUNT(DISTINCT redeemed_code_id) AS totalRewards,
NULL AS totalStudents,
SUM(points) AS totalPoints
FROM `organisation_classrooms` t
LEFT JOIN `classroom_redeemed_codes` r
ON r.classroom_id = t.classroom_id
AND r.inactive = 0
AND (r.date_redeemed >= 1393286400 OR r.date_redeemed = 0)
WHERE t.organisation_id = 37383
GROUP BY t.classroom_id
ORDER BY classroom_id, totalRewards
这将生成一个很好的结果集:
classroom_id | title | redeemed_code_id | user_id | points
-------------+-------+------------------+---------+-------
16 | BLUE | 7 | 2 | 50
16 | BLUE | 7 | 1 | 50 <- *
-------------+-------+------------------+---------+-------
17 | GREEN | 8 | 508835 | 25
17 | GREEN | 8 | 508826 | 25 <- *
17 | GREEN | 9 | 508835 | 75
17 | GREEN | 9 | 508826 | 75 <- *
-------------+-------+------------------+---------+-------
46 | PINK | 5 | NULL | 250
46 | PINK | 6 | NULL | 100
classroom_id | title | totalRewards | totalStudents | totalPoints
-------------+-------+--------------+---------------+-------------
16 | BLUE | NULL | 2 | NULL
16 | BLUE | 1 | NULL | 50
17 | GREEN | NULL | 2 | NULL
17 | GREEN | 2 | NULL | 100
46 | PINK | NULL | 0 | NULL
46 | PINK | 2 | NULL | 350
此查询可以嵌入到另一个查询中
classroom_id | title | totalRewards | totalStudents | totalPoints
-------------+-------+--------------+---------------+-------------
16 | BLUE | NULL | 2 | NULL
16 | BLUE | 1 | NULL | 50
17 | GREEN | NULL | 2 | NULL
17 | GREEN | 2 | NULL | 100
46 | PINK | NULL | 0 | NULL
46 | PINK | 2 | NULL | 350