这个查询不好吗? 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 索引优化与子查询与左连接

MySQL 基础 -- 多表关系(一对一1对多(多对一)多对多)多表查询(内连接外连接自连接子查询(嵌套查询)联合查询 union)笛卡儿积

mysql 一对多与纬度/经度

右连接与左连接

mysql左连接没有数据还会查出来吗

一对一与多对多与重复条目