繁体   English   中英

在这个MYSQL查询中发生了什么,左边的连接和一个分组(在错误的列上)?

[英]What's happening in this MYSQL query with a left join and a group by (on the wrong column)?

我有以下表格:

create temporary table Items (item_id int, item_name varchar(10));
create temporary table ItemRating (item_id int, rating int);

有以下数据:

insert into Items (item_id, item_name) values (1,'Item 1'),(2,'Item 2'),(3,'Item 3'),(4,'Item 4'),(5,'Item 5');
insert into ItemRating values (1,9),(1,6),(3,10);

然后我运行以下查询:

select i.item_id, i.item_name, avg(ir.rating) from Items i left join ItemRating ir ON ir.item_id = i.item_id group by ir.item_id;

这是我得到的结果:

+---------+-----------+----------------+
| item_id | item_name | avg(ir.rating) |
+---------+-----------+----------------+
|       2 | Item 2    |           NULL |
|       1 | Item 1    |         7.5000 |
|       3 | Item 3    |        10.0000 |
+---------+-----------+----------------+

现在,我完全理解查询写错了,我想要的是在i.item_id上做一个组。 但我不明白这种行为。 为什么MYSQL在结果中显示item_id 2,而不是4或5? 我实际上只希望看到第1和第3项,因为它们是唯一在ItemRating中具有相应记录的项目。

那么,任何人都可以向我解释一下MYSQL在做什么吗?

这是正在发生的事情。 逐个考虑查询以及MySQL正在处理的内容。

首先,您要从项目中进行select i.item_id, i.item_name, avg(ir.rating) from Items i ):

+---------+-----------+
| item_id | item_name |
+---------+-----------+
|       1 | Item 1    |
|       2 | Item 2    |
|       3 | Item 3    |
|       4 | Item 4    |
|       5 | Item 5    |
+---------+-----------+

然后你将加入评级( left join ItemRating ir ON ir.item_id = i.item_id )。 请注意, 项目1在连接后出现在两行中,因为这是JOIN的定义方式 - 它为每个连接条件匹配返回一行(而LEFT基本上意味着“即使第一个表中的每一行都返回一次,即使该行没有连接条件匹配“)。

+---------+-----------+-----------+------------+
| item_id | item_name | ir.rating | ir.item_id |
+---------+-----------+-----------+------------+
|       1 | Item 1    |         9 |          1 |
|       1 | Item 1    |         6 |          1 |
|       2 | Item 2    |      NULL |       NULL |
|       3 | Item 3    |        10 |          3 |
|       4 | Item 4    |      NULL |       NULL |
|       5 | Item 5    |      NULL |       NULL |
+---------+-----------+-----------+------------+

最后,您按分级进行分组( group by ir.item_id分组)。 这将为每个唯一的ir.item_id返回一行。 有三个唯一的ir.item_ids(正如你在最后一列中看到的那样): 1NULL3 对于其中的每一个,它返回一行并平均评级。

所以,对于1我们有:

+---------+-----------+-----------+------------+
| item_id | item_name | ir.rating | ir.item_id |
+---------+-----------+-----------+------------+
|       1 | Item 1    |         9 |          1 |
|       1 | Item 1    |         6 |          1 |
+---------+-----------+-----------+------------+

崩溃成:

+---------+-----------+----------------+------------+
| item_id | item_name | avg(ir.rating) | ir.item_id |
+---------+-----------+----------------+------------+
|       1 | Item 1    |            7.5 |          1 |
+---------+-----------+----------------+------------+

对于NULL我们有:

+---------+-----------+-----------+------------+
| item_id | item_name | ir.rating | ir.item_id |
+---------+-----------+-----------+------------+
|       2 | Item 2    |      NULL |       NULL |
|       4 | Item 4    |      NULL |       NULL |
|       5 | Item 5    |      NULL |       NULL |
+---------+-----------+-----------+------------+

崩溃成:

+---------+-----------+----------------+------------+
| item_id | item_name | avg(ir.rating) | ir.item_id |
+---------+-----------+----------------+------------+
|        2| Item 2    |           NULL |       NULL |
+---------+-----------+----------------+------------+

对于3我们有:

+---------+-----------+-----------+------------+
| item_id | item_name | ir.rating | ir.item_id |
+---------+-----------+-----------+------------+
|       3 | Item 3    |        10 |          3 |
+---------+-----------+-----------+------------+

崩溃成:

+---------+-----------+----------------+------------+
| item_id | item_name | avg(ir.rating) | ir.item_id |
+---------+-----------+----------------+------------+
|       3 | Item 3    |             10 |          3 |
+---------+-----------+----------------+------------+

结合三个折叠结果给出:

+---------+-----------+----------------+------------+
| item_id | item_name | avg(ir.rating) | ir.item_id |
+---------+-----------+----------------+------------+
|       1 | Item 1    |            7.5 |          1 |
|       3 | Item 3    |             10 |          3 |
|       2 | Item 2    |           NULL |       NULL |
+---------+-----------+----------------+------------+

这是你得到的。

一个棘手的部分是NULL行折叠的方式。 回想一下,这些是空行:

+---------+-----------+-----------+------------+
| item_id | item_name | ir.rating | ir.item_id |
+---------+-----------+-----------+------------+
|       2 | Item 2    |      NULL |       NULL |
|       4 | Item 4    |      NULL |       NULL |
|       5 | Item 5    |      NULL |       NULL |
+---------+-----------+-----------+------------+

当您执行分组时,大多数数据库系统甚至不允许您选择不属于该组的列。 MySQL是个例外。 由于您只对ir.rating进行分组,因此这是唯一一个允许您选择的方法,因为没有明确的方法以非聚合方式折叠三行。 MySQL所做的只是选择它遇到的第一个,并使用该行中的值作为折叠值。 所以(2,4,5)=>(2)和(第2项,第4项,第5项)=>第2项和(NULL,NULL,NULL)=> NULL。 这就是为什么你只看到第2行(你实际上看到三个折叠的行看起来像第2行)。

要真正看到这一点并将其推向主场,请考虑以下问题:

select group_concat(i.item_id), group_concat(i.item_name), avg(ir.rating) from Items i left join ItemRating ir ON ir.item_id = i.item_id group by ir.item_id;

这与原始查询类似,但所有三个选定列现在都具有组聚合函数。 我正在使用GROUP_CONCAT ,它只是连接字符串以形成折叠版本(除了MySQL之外,这在其他SQL系统中也是有效的)。 这返回了:

+-------------------------+---------------------------+----------------+
| group_concat(i.item_id) | group_concat(i.item_name) | avg(ir.rating) |
+-------------------------+---------------------------+----------------+
| 2,4,5                   | Item 2,Item 4,Item 5      |           NULL |
| 1,1                     | Item 1,Item 1             |         7.5000 |
| 3                       | Item 3                    |        10.0000 |
+-------------------------+---------------------------+----------------+

这是您在加入之后和分组之前的结果集

+---------+-----------+----------------+-----------+
| i.item_id | i.item_name | ir.rating | ir.item_id |
+---------+-----------+----------------+-----------+
|       1   | Item 1      |         9 | 1          |
|       1   | Item 1      |         6 | 1          |
|       2   | Item 2      |      null | null       |
|       3   | Item 3      |        10 | 3          |
|       4   | Item 4      |      null | null       |
|       5   | Item 5      |      null | null       |
+---------+-----------+----------------+-----------+

您通过ir.item_id列进行分组,该列只有3个不同的值... 1,3和null。

显然它采取了第一个item_name虽然我怀疑它记录它正在做什么所以这不能依赖。 底线是应该抛出错误。

你真正想要的是i.item_id,i.item_name分组

左连接带来了所有值,但是您在item_id上对ItemRating表进行分组,因此您只能获得3个值

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM