每组一行,具有多列排序

时间:2017-12-04 15:37:23

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

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

SCHEMA:

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上。

4 个答案:

答案 0 :(得分:1)

试试这个 -

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

答案 1 :(得分:0)

由于MySQL已经有数百种针对每组最大问题的解决方案,我将开始用带窗函数的CTE语法回答这些问题,因为现在可以在MySQL 8.0.3中使用。 / p>

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;

答案 2 :(得分:0)

这里是思考问题的另一种方式。您想为每个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

答案 3 :(得分:-2)

试试这个:

public class NavigationModel
{
    public List<NavigationNode> NavigationNodes { get; set; }
    public NavigationModel()
    {

    }

    private class NavigationNode
    {

    }
}