Mysql JOIN with IN 和 RIGHT 表的多个结果
Posted
技术标签:
【中文标题】Mysql JOIN with IN 和 RIGHT 表的多个结果【英文标题】:Mysql JOIN with IN and multiple results of RIGHT table 【发布时间】:2012-09-13 04:17:09 【问题描述】:LEFT 表(简化)是:
id
name (ex:Bob)
tags (ex:1,4,6)
正确的表是:
id
tag (ex:Sailing)
我想像这样得到 LEFT 表的结果:
row 1:
name: Bob
tags: Sailing,Snowboard,...
我得到的最近的是那里:
SELECT `name`, GROUP_CONCAT(`right`.`tag`) AS tags
FROM (`left`)
LEFT OUTER JOIN `right` ON `right`.`id` IN(left.tags)
ORDER BY `name` asc
但由于 GROUP_CONCAT(),这只给了我一行。没有它,它给了我所有的结果,但只有逗号分隔列表中的第一个标签。我知道我已经很接近了,但我在这方面浪费了太多时间。任何帮助表示赞赏。
我知道我可以创建第三个表 left_right,其中包含行(id、left_id、right_id)以使其工作,但我想避免这种情况。
【问题讨论】:
tags
是整数字段? (你可以有多个带有不同标签的 Bob 行?)
你无法真正避免这种情况,将来它会变得更加混乱。如果您使用的是关系数据库,请尝试正确使用它们。您需要创建第三个表。
tags 是一个带有逗号分隔数字的 VARCHAR。只有一个“Bob”行包含他的所有标签。我完全同意你的观点,特别是如果我们想在将来从用户中删除标签时让它变得更简单,但是我正在与之一起编写数据库插入编码的另一个人对 mysql 不太好,所以我我试图让他变得简单。
哎哟。然后你想翻译标签从数字到文本?我会编辑我的答案。不过,它变得更加复杂。
是的,我知道,如果太多了,我会帮助我的队友并创建第三张桌子
【参考方案1】:
首先,bažmegakapa 说的是正确的,还有更多。如果我正确理解了您描述的设置,那么您已经在浪费大量空间(和性能)了。
你可以这样做:
CREATE TABLE pleft ( id integer, name varchar(20), tags integer );
CREATE TABLE pright ( id integer, tag varchar(20));
INSERT INTO pleft VALUES ( 1, 'Bob', 1 ), ( 9, 'Bob', 4 ), ( 15, 'Bob', 6 );
INSERT INTO pleft VALUES ( 2, 'Ann', 1 ), ( 3, 'Joe', 4 ), ( 4, 'Joe', 6 );
INSERT INTO pright VALUES ( 1, 'Sailing' ), ( 4, 'Snowboarding' ), ( 6, 'Skiing' );
SELECT pleft.name, GROUP_CONCAT(pright.tag)
FROM pleft JOIN pright ON ( pleft.tags = pright.id )
GROUP BY pleft.name ORDER BY pleft.name;
+------+-----------------------------+
| name | GROUP_CONCAT(pright.tag) |
+------+-----------------------------+
| Ann | Sailing |
| Bob | Sailing,Skiing,Snowboarding |
| Joe | Snowboarding,Skiing |
+------+-----------------------------+
...但请注意名称在pleft
表的每一行中是如何不必要地重复的。理想情况下,您将有一张表对人进行建模:(id=1, name="Bob"),一张表对标签进行建模 (id=6, value="Skiing") 和一张包含它们的关系的表。这将确保,例如,Bob 决定使用“Robert”,您不必对整个标签表进行 de-bob,而只需对涉及 Bob 的一行进行 de-bob。
更新
所以tags
是一个包含“1,4,6”的varchar 字段。同样的逻辑也适用,但现在我们必须在重新组合之前拆分字段。你不能使用像“1 in tags”这样的东西,因为“11”会返回true(“1”毕竟包含在“11”中)。 (这是被称为“乱穿马路”的 SQL 反模式:参见例如 https://groups.google.com/forum/?fromgroups=#!topic/django-users/5j4AmQE6nTk)
SELECT pleft.name, GROUP_CONCAT(pright.tag)
FROM pleft JOIN pright
ON ( CONCAT(',',pleft.tags,',') LIKE CONCAT('%,',pright.id,',%' ))
GROUP BY pleft.name ORDER BY pleft.name;
另一种方法是使用存储过程:参见http://www.marcogoncalves.com/2011/03/mysql-split-column-string-into-rows/。
CREATE TABLE pleft ( id integer, name varchar(20), tags varchar(20) );
INSERT INTO pleft VALUES ( 1, 'Bob', '1,4,6' ), ( 2, 'Jill', '4,1' );
SELECT pleft.name, GROUP_CONCAT(pright.tag)
FROM pleft JOIN pright
ON ( CONCAT(',',pleft.tags,',') LIKE CONCAT('%,',pright.id,',%' ))
GROUP BY pleft.name ORDER BY pleft.name;
+------+-----------------------------+
| name | GROUP_CONCAT(pright.tag) |
+------+-----------------------------+
| Bob | Sailing,Snowboarding,Skiing |
| Jill | Sailing,Snowboarding |
+------+-----------------------------+
【讨论】:
哇,感谢 Isemi 的提醒,我不知道 MySQL 对 Jaywalkimg 反模式采取了这种方式,这在开发后期会遇到一些麻烦,谢谢!然而,对于包含数十万行的表,使用 LIKE 的解决方法会很慢。如果没有更快的方法,我将创建我的第三个关系表【参考方案2】:尝试添加一个 GROUP BY 名称字段,您应该能够选择所有名称及其标签的列表。
SELECT `name`, GROUP_CONCAT(`right`.`tag`) AS tags
FROM (`left`)
LEFT OUTER JOIN `right` ON `right`.`id` IN(left.tags)
GROUP BY `name`
ORDER BY `name` ASC;
【讨论】:
不,不工作,无论如何使用 GROUP_BY 是没有意义的,GROUP_CONCAT 查询无论如何只返回一行。以上是关于Mysql JOIN with IN 和 RIGHT 表的多个结果的主要内容,如果未能解决你的问题,请参考以下文章
Oracle left outer join with is null in JOIN vs WHERE 条件(示例)
Mysql join with subquery join 使用相关名称
ClickHouse高级数据查询SQL: WITH/JOIN/IN/INTO OUTFILE/嵌套子查询/交并差计算等
Join EC2 into AD with SSM and remote powershell in AWS