【问题标题】:Get values from first sorted member of grouped(?) sql query从分组(?)sql查询的第一个排序成员中获取值
【发布时间】:2016-08-01 11:45:14
【问题描述】:

我觉得这很明显,但我很挣扎。一定是因为今天是星期一。

我在 MySQL 中有一个 licenses 表,其中包含字段 id (int)start_date (date)licensable_id (int)licensable_type (string)fixed_end_point (boolean)

我想获取开始日期等于或小于今天的所有许可证,将它们按licensable_id and licensable_type 分组,然后获取最近最近开始的一个,这样我就可以获得@987654328 @ 字段,以及 licensable_idlicensable_type

这就是我正在尝试的:

SELECT licensable_id, licensable_type, fixed_end_point
FROM licenses
WHERE start_date <= "2016-08-01"
GROUP BY licensable_id, licensable_type
ORDER BY start_date desc;

目前,ORDER BY 字段似乎被忽略了,它只是返回每个组的第一个许可证中的值,而不是最近的。谁能看到我做错了什么?我需要进行嵌套查询吗?

【问题讨论】:

  • 订购方式无效。当 GROUP BY(以及 SELECT DISTINCT 和 UNION 等)仅选择列表项时,可以在 ORDER BY 中指定。

标签: mysql sql group-by sql-order-by


【解决方案1】:

您不应该将此视为group by。考虑到问题中的限制,您希望为每个许可证选择最新的start_date。一种方法使用相关子查询:

select l.*
from licenses l
where l.start_date = (select max(l2.start_date)
                      from licenses l2
                      where l2.licensable_id = l.licensable_id and
                            l2.licensable_type = l.licensable_type and
                            l2.start_date <= '2016-08-01'
                     );

【讨论】:

  • 谢谢,这似乎有效!您的回答中有错字:l2.licensable_type = l2.licensable_type and 应该是 l2.licensable_type = l.licensable_type and
【解决方案2】:

你不使用聚合函数,所以你应该使用 distinct

SELECT  DISTINCT licensable_id, licensable_type, fixed_end_point
FROM licenses
WHERE date(start_date)  <= date(now())
ORDER BY start_date desc
limit 1;

【讨论】:

  • MySQL 是否执行该查询? (根据 ANSI SQL,您只能在 SELECT DISTINCT 时对选定的列进行 ORDER BY。)
  • 结果是有序的.. 我知道 ANSI 标准(我总是选择排序的列,但在这种情况下没有 OP)但这是 mysql 可能的行为
【解决方案3】:

这没有给你想要的结果的原因是 GROUP CONCAT 是如何工作的。

对于标准 SQL,SELECT 中的任何字段都必须在 GROUP BY 子句中也被提及,或者必须是聚合字段(与返回的字段 100% 相关的字段有一个例外,但许多 SQL 风格都可以不支持)。

MySQL 确实允许字段位于 SELECT 子句中,该字段不是聚合值,也没有在 GROUP BY 子句中提及,并且直到最近才允许这是默认值。但是,对于这些字段,GROUP BY 字段可能有多个值,在这种情况下,未定义选择哪一个。由于这是在处理 ORDER BY 语句之前解决的,因此 ORDER BY 子句对选择哪个语句没有影响。

有几种正常的方法可以做到这一点。您可以按照 Gordon 的建议使用 a ,或者类似地(并且可能更有效地取决于记录和索引),您可以使用子查询来获取每个重要行的最新行日期,然后将其连接回主表:-

SELECT l.licensable_id, 
        l.licensable_type, 
        l.fixed_end_point
FROM licenses l
INNER JOIN 
(
    SELECT licensable_id, 
            licensable_type, 
            MAX(l2.start_date) AS max_start_date
    FROM licenses 
    GROUP BY licensable_id, 
            licensable_type
) sub0
ON l.licensable_id = sub0.licensable_id
AND l.licensable_type = sub0.licensable_type
AND l.start_date = sub0.max_start_date

在某些情况下,另一种选择是(ab)使用 GROUP_CONCAT 和 SUBSTRING_INDEX 函数。这样,您可以按您想要的字段分组,但按日期的降序执行 GROUP_CONCAT 或其他字段。然后使用 SUBSTRING_INDEX 将所有内容添加到第一个逗号(GROUP_CONCAT 的默认分隔符):-

SELECT licensable_id, 
        licensable_type, 
        SUBSTRING_INDEX(GROUP_CONCAT(COALESCE(fixed_end_point, '') ORDER BY start_date DESC), ',', 1)
FROM licenses
WHERE start_date <= "2016-08-01"
GROUP BY licensable_id, licensable_type

如果最新行有一个空值,这显然会出现问题,因此我使用 COALESCE 来伪造非空值。此外,如果该字段包含逗号,您将需要使用替代分隔符。如果字段很大,那么您可能会遇到 GROUP_CONCAT 的最大字段长度问题(我认为默认为 1024)。

【讨论】:

  • 感谢您的解释!
猜你喜欢
  • 1970-01-01
  • 2021-04-01
  • 2016-03-14
  • 1970-01-01
  • 2021-10-07
  • 2018-10-20
  • 1970-01-01
  • 2018-04-22
  • 2022-11-21
相关资源
最近更新 更多