在 WHERE IN 子句 SQL 中显示每条记录的 MAX 日期

Posted

技术标签:

【中文标题】在 WHERE IN 子句 SQL 中显示每条记录的 MAX 日期【英文标题】:Display the MAX date for each record in a WHERE IN clause SQL 【发布时间】:2019-11-09 12:33:00 【问题描述】:

使用 mysql,我有一个名为subscriptions 的表,如下所示

name,   renewal_date,    national_id
---------------------------------------
ted,     1/2/2010,        A1111R
ben,     1/3/2010,        A145E1
bob,      8/4/2009,       A11C11
kate,     2/2/2010,       A111E1
ted,     12/2/2011,       A1111R
bob,     12/2/2014,       A11C11
ben,     12/2/2016,       A145E1

etc..

该表有大约 150000 条记录。每个人可以有多个订阅。我想为给我查询的列表中的每条记录显示最大/最新renewal_date。这是我的sql语句:

select d.name, d.renewal_date, d.national_id
from subscriptions d
where renewal_date= (select max(d1.renewal_date) 
                     from subscriptions d1 
                     where d1.national_id = d.national_id IN ('A1111R','A11C11', 'A145E1' ....));

当我在 phpmyadmin 中运行查询时,它似乎并没有结束执行,即使 IN 子句中的记录很少。

最好的方法是什么?我还应该说我还不是 SQL 专家 :-) 在此先感谢

【问题讨论】:

对于 MySQL,最好知道您正在使用的版本。最近发生了重要的变化。在 MySQL 8 中,有些事情比以前的版本更容易实现。 表中的同名可以有两个不同的national_id吗?如果是这样,您是想要姓名一个结果行还是每个姓名和national_id 一个结果行? 谢谢@ThorstenKettner。 MySQL 版本是 5.7.25。不,表中的同名不能有两个不同的national_id。 那么是时候修复数据库设计了。一张供有国民身份证的人使用,一张供订阅者使用。 【参考方案1】:

假设给定人的姓名和国家 ID 不变,您可以只使用聚合:

select d.name, max(d.renewal_date), d.national_id
from subscriptions d
where d.national_id in ( . . . )
group by d.name, d.national_id;

如果您想要行上的所有列,最有效的方法通常是具有正确索引的相关子查询:

select s.*
from subscriptions s
where s.national_id in ( . . . ) and
      s.renewal_date = (select max(s2.renewal_date)
                        from subscriptions s2
                        where s2.national_id = s.nation_id
                       );

正确的索引是(national_id, renewal_date)。实际上,在许多情况下,这可能比 group by 查询更快。

【讨论】:

非常感谢@gordon-linoff 的出色回答和快速响应。第二个例子对我有用。最后,如果我想删除我显示的那些续订日期,我将如何从select 转换为deletedelete s.renewal_date from... 之类的东西? @adamvanbart:要删除这些行,只需将 select s.* 更改为 delete。还是您想更新行并只删除(即设为 null)它们的日期? 谢谢@gordonlinoff。我不想删除整行,只删除renewal_date 然后把select s.* from subscriptions s改成update subscriptions s set renewal_date = null 谢谢@ThorstenKettner。但是 MySQL 会抛出一个错误:#1093 - You can't specify target table 's' for update in FROM clause【参考方案2】:

您需要每个名称的最大续订日期。如果是这样,那么您需要做的就是聚合:

select name, max(renewal_date)
from subscriptions
group by name
order by name;

我不知道,national_id 是如何进入 Play 的。一个人的national_id 真的可以从一个订阅更改为另一个订阅吗?还是您的数据库设计存在缺陷?

你可能想要这个:

select name, national_id, max(renewal_date)
from subscriptions
group by name, national_id
order by name, national_id;

或者这个:

select name, max(renewal_date), any_value(national_id)
from subscriptions
group by name
order by name;

或者这个:

select *
from subscriptions
where (name, renewal_date) in
(
  select name, max(renewal_date)
  from subscriptions
  group by name
)
order by name;

【讨论】:

以上是关于在 WHERE IN 子句 SQL 中显示每条记录的 MAX 日期的主要内容,如果未能解决你的问题,请参考以下文章

将 IN() 用于数组的 PL/SQL where 子句

是否可以使用“WHERE”子句来选择 SQL 语句中的所有记录?

SQL 优化记录

SQL 在 WHERE IN 子句中使用 CASE 语句

PL/SQL - 在 Where In 子句中使用“列表”变量

如何在子查询的 WHERE 子句中使用来自 UNNEST 的多个值?