Mysql 具有多个左联接的查询-点列值不正确

Mysql 具有多个左联接的查询-点列值不正确,mysql,database,join,left-join,Mysql,Database,Join,Left Join,我有以下数据库结构,我正在尝试运行一个查询,该查询将显示教室、教室中有多少学生、教室分配了多少奖励以及分配给单个教室的分数(基于教室id列) 使用最底部的查询,我试图收集教室分配的“totalPoints”——基于计算教室兑换代码表中的points列,并将其作为单个整数返回 由于某些原因,这些值对于totalPoints是不正确的-我做错了什么,但不确定是什么 --更新-- 以下是sqlfiddle:- 我的结构: CREATE TABLE `organisation_classrooms`

我有以下数据库结构,我正在尝试运行一个查询,该查询将显示教室、教室中有多少学生、教室分配了多少奖励以及分配给单个教室的分数(基于教室id列)

使用最底部的查询,我试图收集教室分配的“totalPoints”——基于计算教室兑换代码表中的points列,并将其作为单个整数返回

由于某些原因,这些值对于totalPoints是不正确的-我做错了什么,但不确定是什么

--更新-- 以下是sqlfiddle:-

我的结构:

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
现在,我们可以在上面的查询周围加上一些括号(strip
orderby
),并在另一个查询中使用它,根据
choork\u id
对数据进行分组,计算用户和兑换的代码,并将其积分相加

您将得到一个看起来很糟糕的查询,在当前的数据库模式下,当您的表有几百行时,查询将爬行这就是我不在这里写的原因

注意 在查询的
on
WHERE
ORDER by
GROUP by
子句中出现的字段上,通过向表中添加缺少的索引,可以提高其性能

这将带来显著的改善,但我不太相信这一点。对于真正大的表(数十万行),它仍然会爬行

另一个想法 我们还可以在Query#1Query#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