这个查询不好吗? MySql 一对多与左连接和嵌套查询
Posted
技术标签:
【中文标题】这个查询不好吗? MySql 一对多与左连接和嵌套查询【英文标题】:Isn't this query ok? MySql one to many with left join and nested query 【发布时间】:2021-01-14 08:22:56 【问题描述】:花了一段时间找到最佳的为什么做这项工作后,我以这个查询结束,我只是想知道它是否按我预期的那样工作?经验表明 mysql 总是给我一个惊喜,告诉我我的查询结构错误或没有达到预期的效率。
这是查询:
select
`users`.*,
`i`.`name` as `identity_name`,
`i`.`id` as `identity_id`,
`i`.`is_verified`
from
`users`
left join `identities` as `i` on `i`.`user_id` = `users`.`id`
and `i`.`id` = (
select
`i2`.`id`
from
`identities` as `i2`
where
`i2`.`user_id` = `i`.`user_id`
order by
`i2`.`is_verified` desc,
`i2`.`updated_at` desc
limit
1
)
用户表:
+---+-------------------+
| id| other columns ... |
+---+-------------------+
| 1 | user data ... |
| 2 | user data ... |
| 3 | user data ... |
| 4 | user data ... |
|...| user data ... |
+---+-------------------+
身份表:
+----+---------+-------------+-------------+-----------+
| id | user_id | is_verified | name | updated_at|
+----+---------+-------------+-------------+-----------+
| 1 | 1 | 0 | some name | 2001 |
| 2 | 1 | 1 | some name | 2000 |
| 3 | 2 | 0 | some name | 2000 |
| 4 | 1 | 0 | some name | 2000 |
| 5 | 3 | 1 | some name | 2003 |
|... | ... | ... | ......... | .... |
+----+---------+-------------+-------------+-----------+
预期输出:每个用户都应该有一个来自身份表的身份。 is_verified = 1 然后更新的 updated_at 的身份具有更高的优先级
很明显,id = 1 的用户是有问题的用户。
【问题讨论】:
您的代码不会严格返回 is_verified = 1 的行。还有为什么 id=1 的用户有问题? 因为 id=1 在 identities 表中有许多不同的行,并且应该只选择其中一个...我的主要目的是选择具有优先级的行,并且我希望每个用户都有身份(如果有的话)即使是强硬用户的身份也不是(is_verified = 1) 那我猜你的代码没问题,除非你的 MySql 版本是 8.0+,在这种情况下你可以使用窗口函数。 请在代码问题中给出minimal reproducible example--cut & paste & runnable code,包括最小的代表性示例输入作为代码;期望和实际输出(包括逐字错误消息);标签和版本;明确的规范和解释。给出尽可能少的代码,即您显示的代码可以通过您显示的代码扩展为不正常的代码。 (调试基础。)对于包含 DBMS 和 DDL(包括约束和索引)和输入为格式化为表的代码的 SQL。 How to Ask 暂停总体目标的工作,将代码砍到第一个表达式,没有给出你期望的内容,说出你期望的内容和原因。 @newmoon:您的代码很好——或者如果您运行的是 MySQL 8,您可以使用 forpas 演示的窗口函数(尽管它不一定会更快)。考虑在identities(user_id, is_verified desc, updated_at desc, id)
上建立索引以提高性能。
【参考方案1】:
如果你的 MySql/Mariadb 版本支持窗口函数,你可以使用ROW_NUMBER()
:
select u.*, i.name as identity_name, i.id as identity_id, i.is_verified
from users u
left join (
select *,
row_number() over (partition by user_id order by is_verified desc, updated_at desc) rn
from identities
) as i on i.user_id = u.id and i.rn = 1
请参阅demo。 结果:
> id | other columns | identity_name | identity_id | is_verified
> -: | :------------ | :------------ | ----------: | ----------:
> 1 | user data | some name | 2 | 1
> 2 | user data | some name | 3 | 0
> 3 | user data | some name | 5 | 1
> 4 | user data | null | null | null
【讨论】:
以上是关于这个查询不好吗? MySql 一对多与左连接和嵌套查询的主要内容,如果未能解决你的问题,请参考以下文章
MySQL 基础 -- 多表关系(一对一1对多(多对一)多对多)多表查询(内连接外连接自连接子查询(嵌套查询)联合查询 union)笛卡儿积