如何在按另一列排序时按一列过滤?

Posted

技术标签:

【中文标题】如何在按另一列排序时按一列过滤?【英文标题】:How can I filter by one column whilst sorting by another? 【发布时间】:2014-11-12 10:09:25 【问题描述】:

架构:

CREATE TABLE `Log` (
    `EntryId` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    `EntryTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(),
    `Severity` ENUM(
        'LOG_LEVEL_CRITICAL',
        'LOG_LEVEL_ERROR',
        'LOG_LEVEL_WARNING',
        'LOG_LEVEL_NOTICE',
        'LOG_LEVEL_INFO',
        'LOG_LEVEL_DEBUG'
    ) NOT NULL,

    `User` TEXT,
    `Text` TEXT NOT NULL,

    PRIMARY KEY(`EntryId`),
    KEY `EntryTime` (`EntryTime`)
) ENGINE=InnoDB COMMENT="Log of server activity";

查询:

SELECT 
   `EntryId`,
   UNIX_TIMESTAMP(`EntryTime`) AS `EntryTime_UnixTS`
   `Severity`,
   `User`,
   `Text`
FROM `Log` 
ORDER BY `EntryTime` DESC, `EntryId` DESC
LIMIT 0, 20

根据观察和执行计划(尽管这需要在小型数据集上使用FORCE INDEX!),索引正在按需要使用:

id  select_type  table  type  possible_keys  key        key_len  ref  rows  Extra
1   SIMPLE       Log    index \N             EntryTime  4        \N   20    

现在我想添加一个范围条件:

WHERE `Severity` <= 'LOG_LEVEL_WARNING'

但我不知道如何选择一个键,这样这个简单的查询仍然可以使用索引,同时对一个列进行排序并在另一列上进行过滤。我想得越多,我的弱 SQL 直觉就越表明它甚至不可能使用当前形式的查询,尤其是因为条件在 范围 上。

你会如何处理这个问题?

【问题讨论】:

Severity,EntryTime,EntryId 上创建一个新索引怎么样? sqlfiddle.com/#!2/0d3397/1 @juergend:WHERE 会很慢,不是吗?您的新严重性指数未被使用 @OscarPérez:嗯,这似乎行得通。我不知道怎么做,尤其是因为我在this answer! 【参考方案1】:

在我看来,您需要创建一个包含三个必填字段的索引:

CREATE INDEX test_idx
          ON Log (Severity,EntryTime,EntryId);

该服务器将能够使用此索引,因为它包含执行查询所需的所有内容。

【讨论】:

我在this answer 中被告知,索引上的尾随 PK 是错误的。为什么这里不一样?它似乎确实有效,但我无法概念化为什么会这样:(我的意思是,在范围条件下,无论如何它都必须进行一些排序,对吧?那么为什么结果如此之快:/跨度> 我从来没有读过...这是真的吗?你有没有找到任何官方的 mysql 页面说明这一点?据我所知,使用索引将帮助服务器以更快(对数?)的方式定位其范围内的所有记录...... 是的,我想即使查找不能保持不变,索引也会在很大程度上有所帮助;这将解释它。至于后面PK的事情,我不知道,这是我被告知的,并且该答案给出的解决方案有效。

以上是关于如何在按另一列排序时按一列过滤?的主要内容,如果未能解决你的问题,请参考以下文章

如何按一列的最大值获取SQL行,按另一列分组

如何按一列分组并对另一列的值进行排序?

pandas 按另一列的平均值对一列的值进行排序

SQL Server:按分组列求和并按另一列排序

SQL查询 - 按另一列排序一组列

按列分组,结果限制并按另一列轨道排序