【问题标题】:One row per group with multiple column sorting每组一行,多列排序
【发布时间】:2018-05-18 02:57:57
【问题描述】:

希望每组返回一行,其中一行由多个排序列选择。在 的土地上小心行事以避免重复的问题。

架构:

CREATE TABLE logs (
  id INT NOT NULL,
  ip_address INT NOT NULL,
  status INT NOT NULL,
  PRIMARY KEY id
);

数据:

INSERT INTO logs (id, ip_address, status) 
VALUES ('1', 19216800, 1),
       ('2', 19216801, 2),
       ('3', 19216800, 2),
       ('4', 19216803, 0),
       ('5', 19216804, 0),
       ('6', 19216803, 0),
       ('7', 19216804, 1);

当前查询:

SELECT *
  FROM logs
 ORDER BY ip_address, status=1 DESC, id DESC

注意:按status=1 排序有效地将状态列转换为布尔值。 status=1 之后的决胜局是 id。此查询当前首先为每个 ip_address 返回正确的行,然后返回一堆我不想要的 ip_address 的其他行。

当前输出:

1, 19216800, 1
3, 19216800, 2
2, 19216801, 2
6, 19216803, 0
4, 19216803, 0
7, 19216804, 1
5, 19216804, 0

想要的输出:

1, 19216800, 1
2, 19216801, 2
6, 19216803, 0
7, 19216804, 1

今天我的解决方法是使用if ($lastIP == $row['ip_address']) continue; 在 PHP 中进行过滤。但我想将此逻辑移至 MySQL。

【问题讨论】:

  • @Strawberry 好电话,谢谢。我已经添加了查询的当前输出和想要的输出。

标签: greatest-n-per-group mysql greatest-n-per-group limit-per-group


【解决方案1】:

试试这个 -

SELECT MIN(id), ip_address, status
FROM logs
GROUP BY ip_address, status

【讨论】:

  • 所以基本上这为每个 ip_address 获得了正确的 id status='completed' 的真值和假值。谢谢,这是非常接近的,因为我想将其减少到每个 ip_address 的一行。
【解决方案2】:

由于 MySQL 中已经有数百个解决方案可以解决每组最大 n 个问题,我将开始使用带有窗口函数的 CTE 语法来回答这些问题,因为现在 MySQL 8.0.3 中提供了这些问题。

WITH sorted AS (
    SELECT id, ip_address, status, 
      ROW_NUMBER() OVER (PARTITION BY ip_address ORDER BY status) AS rn
    FROM logs
)
SELECT * FROM sorted WHERE rn = 1;

【讨论】:

  • 谢谢,这是一个非常好的长期解决方案。我希望我们能尽快使用它!
  • 当 8.0 发布 GA 时,我将返回并编辑我最流行的 best-n-per-group 答案以添加 CTE 解决方案。
  • 明确地说,我还没有看到这个特定问题的解决方案。你知道没有 CTE 就可以工作的一种吗?询问是因为 CTE 尚未在 RHEL 中。
  • @FullDecent,只需关注greatest-n-per-group 标签,您就会找到很多解决方案。
  • 我很乐意将这个问题作为重复问题结束,但我找不到其他人正在寻找“具有多列排序的第一行”。我看到很多为每个用户寻找 MAX(post_id) 的问题,但这些问题要简单得多。
【解决方案3】:

这是思考问题的不同方式。您想为每个 id_address 找到“最佳”行。或者换句话说,您想选择不存在更好行的行。

此解决方案适用于 8.0 之前的 MySQL 版本。换句话说,它适用于您已经安装了 RHEL 7 的版本。您可以轻松地将此技术扩展到任意数量的排序列。

SELECT a.*
  FROM (SELECT * FROM logs) a
  LEFT JOIN (SELECT * FROM logs) b
    ON (b.ip_address = a.ip_address AND (b.stat=1) > (a.stat=1))
    OR (b.ip_address = a.ip_address AND (b.stat=1) = (a.stat=1) AND b.id > a.id)
 WHERE b.id IS NULL
 ORDER BY a.ip_address

如果您有更多列要排序,则继续添加OR 子句来处理平局,并为每个ip_address 选择“最佳”行。无论您的子查询多么复杂,或者您有多少个“SORT BY~”条件,您都只需要一个 LEFT JOIN 就可以了。

【讨论】:

    【解决方案4】:

    试试这个:

    SELECT 
    l.`ip_address` , l.`status`
    FROM
      `logs` l 
    GROUP BY l.`ip_address` 
    ORDER BY l.`status` = 1 DESC
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-16
    • 2021-06-10
    • 2012-03-11
    • 1970-01-01
    • 1970-01-01
    • 2016-04-02
    • 1970-01-01
    • 2015-03-22
    相关资源
    最近更新 更多