【问题标题】:Why can't select records for each code having the maximum date with having date=max(date)?为什么不能为具有 date=max(date) 的最大日期的每个代码选择记录?
【发布时间】:2021-03-16 03:56:34
【问题描述】:

显示创建表结构;

CREATE TABLE `quote` (
  `id` int(8) unsigned NOT NULL AUTO_INCREMENT,
  `code` text COLLATE utf8mb4_unicode_ci,
  `date` date DEFAULT NULL,
  `open` double DEFAULT NULL,
  `high` double DEFAULT NULL,
  `low` double DEFAULT NULL,
  `close` double DEFAULT NULL,
  `volume` bigint(15) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=17449887 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci 

查找具有最大日期的每个代码的记录,例如:

SELECT q1.*
FROM quote q1
INNER JOIN
(
    SELECT code, MAX(date) AS max_date
    FROM quote
    GROUP BY code
) q2
    ON q2.code = q1.code AND
       q2.max_date = q1.date;

我想知道为什么子查询不能像上面那样得到想要的结果:

select * from quote group by code having date=max(date);

请详细说明原因。

【问题讨论】:

    标签: mysql subquery groupwise-maximum


    【解决方案1】:

    @scrapy 我相信您的问题的答案与子查询与其他查询的结构之间的区别有关。子查询的工作方式是 MySQL 在运行外部查询之前从查询中创建一个派生表,然后在 MySQL 执行外部查询时使用派生表 (here's the documentation on derived tables for you to refer to)。

    您的子查询有效,因为您只选择 1 列 (code),然后您通过使用 MAX(date) 作为第二列获得聚合值,最后您按 code 分组子查询的最后一行。

    在您的第二个查询中,您使用SELECT *,然后在您尝试在HAVING 子句中使用MAX(date) 之前仅按code 进行分组。此查询不起作用,因为您正在使用 SELECT * 选择表中的每一列,但您仅在 GROUP BY 子句中按 code 分组。从 MySQL v5.7 及更高版本开始,有一个名为 only_full_group_by 的东西不允许您使用 GROUP BY 运行查询,除非您在 GROUP BY 中指定 SELECT 语句中的每一列,IE:获取您的要运行第二个查询,您必须将表中的每一列都列在 GROUP BY 子句中,因为您使用的是 SELECT * 选择语句 (here is the documentation that talks about only_full_group_by)。

    最后,为了获得您正在寻找的结果集,您必须按正确的列进行分组,就像您在子查询中所做的那样。如果您在查询中使用code 以外的任何内容,并尝试获取每个code 的最大日期,则结果集将不一样,因为您必须按额外的列进行分组,这会抛弃您的结果集.

    【讨论】:

      【解决方案2】:

      我想知道为什么子查询不能像上面那样得到想要的结果:

      select * from quote group by code having date=max(date);
      

      开始:

      select * from quote group by code
      

      从 SQL 标准的角度来看,此查询本身不是有效的。

      如果所有其他列在功能上都依赖于code,则可能是这样,而基于表定义的情况并非如此(代码不是唯一的,也不是主键)。相关阅读:Group by clause in mySQL and postgreSQL, why the error in postgreSQL?

      查询的行为类似于ANY_VALUE

      select code, ANY_VALUE(id), ANY_VALUE(`date`), ANY_VALUE(`open`)...
      from quote
      group by code
      

      关于第二部分:

      having date=max(date);
      --
      having any_value(date) = max(date) -- sidenote: it will work for single row per `code`
      

      这里 HAVING 中的条件在聚合后应用,这意味着比较是在每个代码的 MAX(date) 与“未指定”日期之间进行比较。


      举例说明(此代码仅在only_full_group_by 关闭时才有效):

      CREATE TABLE `quote` (
        `id` int(8) unsigned NOT NULL AUTO_INCREMENT,
        `code` text COLLATE utf8mb4_unicode_ci,
        `date` date DEFAULT NULL,
        `open` double DEFAULT NULL,
        PRIMARY KEY (`id`)
      ) ;
      
      INSERT INTO quote(`code`, `date`, `open`)
      VALUES ('a', '2020-01-01',10),
             ('a', '2021-01-01',20),
             ('a', '2022-01-01',30);
      

      还有查询:

      SELECT * FROM quote;
      +-----+-------+-------------+------+
      | id  | code  |    date     | open |
      +-----+-------+-------------+------+
      |  1  | a     | 2020-01-01  |   10 |
      |  2  | a     | 2021-01-01  |   20 |
      |  3  | a     | 2022-01-01  |   30 |
      +-----+-------+-------------+------+
      
      select * from quote group by code;
      
      -- this part is unspecified, id/date/open are arbitrary
      +-----+-------+-------------+------+
      | id  | code  |    date     | open |
      +-----+-------+-------------+------+
      |  1  | a     | 2020-01-01  |    1 |
      +-----+-------+-------------+------+
      
      select *, MAX(date) from quote group by code;
      
      -- MAX(date) is stable, date is arbitrary, comparison does not make sense at this point
      +-----+-------+-------------+-------+------------+
      | id  | code  |    date     | open  | MAX(date)  |
      +-----+-------+-------------+-------+------------+
      |  1  | a     | 2020-01-01  |   10  | 2022-01-01 |  
      +-----+-------+-------------+-------+------------+
      
      select * from quote group by code having date=max(date);
      
      -- empty
      +-----+-------+-------+------+
      | id  | code  | date  | open |
      +-----+-------+-------+------+
      

      db<>fiddle demo


      这么说,为了得到所有列 ranking functions MySQL 8.0+ 可以使用:

      本节描述非聚合窗口函数,对于查询中的每一行,使用与该行相关的行执行计算

      SELECT *
      FROM (SELECT *, ROW_NUMBER() OVER(PARTITION BY `code` ORDER BY `date` DESC) AS rn
            FROM `quote`) s   --RANK() if `date` is not unique per code
      WHERE rn = 1;
      

      db<>fiddle demo 2

      【讨论】:

        【解决方案3】:

        这个

        select * from quote group by code having date=max(date);
        

        具有max(date),这在GROUP BY code 的上下文中是有意义的。但是date 没有。问题是应该比较行的date?简单来说,很可能是“无效”的 SQL。

        另见“only_full_group_by”的讨论。 (较新版本的 MySQL 会将您的查询标记为无效。该标记是一种关闭它以获取旧的错误评估的方法。)

        这会导致子查询,例如您拥有的子查询。还有其他几个。这是我的最佳 groupwise-max 方法目录:http://mysql.rjweb.org/doc.php/groupwise_max

        也有很多讨论;查看我添加的标签[groupwise-maximum]

        其他问题:code 是股票代码吗?如果是这样,它不需要是TEXT。通过更改为 VARCHAR(15),您可以获得很多性能:

        去掉id,改成PRIMARY KEY(code, date)。这将显着扩展该子查询,并且可能会改进其他一些查询。

        【讨论】:

          猜你喜欢
          • 2013-06-25
          • 1970-01-01
          • 1970-01-01
          • 2021-08-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多