如何在 MySQL 排序中控制 nulls-first 或 nulls-last?

Posted

技术标签:

【中文标题】如何在 MySQL 排序中控制 nulls-first 或 nulls-last?【英文标题】:How to control nulls-first or nulls-last in MySQL sorting? 【发布时间】:2017-11-02 09:32:24 【问题描述】:

我的数据如下所示:

App ID  Ref Id  App Type  Reg Date
1       1       Main      2017-05-13
2       2       Sub       2017-05-14
3       1       Sub       2017-05-16
4       2       Main      2017-05-15
5       3       Main      2017-05-14
6       1       sub       2017-05-17
7      null     Main      2017-05-20

我想按如下所示更改此表。

App ID  Ref Id  App Type Reg Date
7      null     Main     2017-05-20
4       2       Main     2017-05-15
2       2       Sub      2017-05-14
5       3       Main     2017-05-14
1       1       Main     2017-05-13
6       1       sub      2017-05-17
3       1       Sub      2017-05-16

显示具有相同 ref ID 的内容,具有 Main 的内容位于顶部。最近注册的内容必须在顶部。也就是说,我想创建一个层次结构。

我配置了如下所示的查询。

SELECT * 
FROM t 
JOIN (SELECT `Ref Id`, MAX(`Reg Date`) AS maxdate FROM t WHERE `App Type` = 'Main' GROUP BY 1) md USING(`Ref Id`) 
ORDER BY maxdate DESC, `Ref Id`, (`App Type` = 'Main') DESC;

但是,结果如下。即使 Ref Id 为空,我也会按 Reg Date 排序。总之,要配置为第二张表,数据7应该在最上面。

App ID  Ref Id  App Type  Reg Date    maxdate
4       2       Main     2017-05-15  2017-05-15
2       2       Sub      2017-05-14  2017-05-15
5       3       Main     2017-05-14  2017-05-14
1       1       Main     2017-05-13  2017-05-13
6       1       sub      2017-05-17  2017-05-13
3       1       Sub      2017-05-16  2017-05-13
7      null     Main     2017-05-20    null

【问题讨论】:

请使用引号使您的信息可读。另外,如果你能以表格形式发布结果,那就更好了。 问题是由于空Ref Id 导致子查询的连接失败。也许你可以用ORDER BY COALESCE(maxdate,'Reg Date') 代替ORDER BY maxdate 最后一条评论应该在Reg Date周围加上反引号而不是单引号,但我不知道如何防止对格式做奇怪的事情。 @SteveLovell 我已经通过添加该部分解决了这个问题。ORDER BY COALESCE(maxdate,'Reg Date') 谢谢。 【参考方案1】:

你快到了,使用left join获取所有记录,尝试以下操作:

SELECT t.* 
FROM t 
LEFT JOIN (
    SELECT `Ref Id`, MAX(`Reg Date`) AS maxdate
    FROM t
    WHERE `App Type` = 'Main'
    GROUP BY 1
) md USING(`Ref Id`) 
ORDER BY t.`Ref Id` is not null, maxdate DESC, (`App Type` = 'Main') DESC, t.`Reg Date` DESC;

【讨论】:

【参考方案2】:

重写我之前的评论作为答案,并提供更多背景信息和建议。我觉得代码应该大致如下:

SELECT
    t.*,
    md.MaxDate
FROM
    t 
LEFT JOIN
    (
     SELECT
        `Ref Id` as RefId,
        MAX(`Reg Date`) AS MaxDate
     FROM t
     WHERE
        `App Type` = 'Main'
     GROUP BY
         RefId
    ) md ON md.RefId = t.`Ref Id`
ORDER BY
    COALESCE(md.MaxDate, t.`Reg Date`) DESC,
    t.`Ref Id`,
    (t.`App Type` = 'Main') DESC;

关于连接的思考

您会注意到我已将您的“自然连接”替换为标准(左外)连接。我非常喜欢 Natural Joins 的优雅,但似乎有充分的理由不使用它们(请参阅this SO 问题和各种答案)。

我猜您实际上已经将其作为外部联接,否则我希望您的代码根本不会返回有问题的行(因为 `Ref Id` is NULL 这就是您要加入的)...和 AFAIK,mysql 中没有 ANSI_NULLS OFF 选项。

对此的思考

现在,我将评论转换为答案的主要原因是指出此解决方案可能存在的问题。很大程度上取决于记录具有`Ref Id` = NULL 的原因,以及在这种情况下还能预期什么。如果您有NULLs,其中既有'Main'`App Type`'Sub'`App Type`,那么由于`Reg Date` 不同,它们可能会相互分离。其他相关记录显示在连续行中,因为它们按共享的MaxDate 排序,由子查询检索,但在这种情况下,子查询失败并使用`Reg Date` 作为代理。

如果额外的'Sub' 类型记录仅在分配`Ref Id` 之后出现,这不会有问题。

如果发生这种情况,那么您需要另一种方法在子查询中将这些行绑定在一起。不知道数据,不知道能不能用。


附:天哪,我真的不喜欢所有这些反引号。如果列名中没有空格,则不需要它们。如果您问我,这是避免在开头放置这些空格的绝佳理由(如果这是您的决定)。

【讨论】:

以上是关于如何在 MySQL 排序中控制 nulls-first 或 nulls-last?的主要内容,如果未能解决你的问题,请参考以下文章

mysql 如何根据 like 、not like 排序

如何在mysql查询中获得排序结果?

mysql 中英文如何排序

如何按照给定的方式在mysql中排序数据

Mysql数据查询in的时候如何排序

如何在Mysql查询中通过PHP对加密数据进行排序