【问题标题】:Find a record and its neighbour in an ordered query在有序查询中查找记录及其邻居
【发布时间】:2015-12-05 21:52:42
【问题描述】:

我有一个用户列表。每个用户都有一个分数。我想查询我的数据库以获取 11 个用户的列表,按分数排序,并以特定记录为中心(我想要以前的 5 条记录、我查询的记录和后面的 5 条记录)。

所以例如,假设如下表:

Id  Name      Score 
1   Albert    12
2   Bernard   85
3   Christian 98
4   Danielle  5
5   Emilie    65
6   Fabrice   34
7   Gaston    123
8   Hasting   76
9   Isidor    90
10  Jacques   3
11  Kellam    32
12  Lily      13
13  Mael      4242

如果我使用 Emily 作为基准运行查询,我应该得到以下信息:

5   Danielle  --- -5
12  Albert    --- -4
13  Lily      --- -3
32  Kellam    --- -2
34  Fabrice   --- -1
65  Emilie    ---  0 (pivot)
76  Hasting   --- +1
85  Bernard   --- +2
90  Isidor    --- +3
98  Christian --- +4
123 Gaston    --- +5

(请注意 Jacques 和 Mael 没有返回,因为它们不在请求的范围内)

我怎样才能通过仅使用最少量的查询和/或仅使用简单的查询来实现此结果?

我正在使用 Postgres,但我想要一些也可以在 SQLite 上运行的东西

【问题讨论】:

  • 请告诉我们您使用的是什么 RDBMS,因为存在一些细微的语法差异。
  • @MikeNakis 我正在使用 Postgres,但我想要一些也可以在 SQLite 上运行的东西——我的测试使用它作为 postgres 的内存替代品。

标签: sql postgresql sqlite


【解决方案1】:

Sql Server/Postgresql/Oracle你可以使用窗口函数:

WITH cte(Score, Name, rn) AS
(
  SELECT Score, Name, 
         ROW_NUMBER() OVER (ORDER BY score) AS rn
  FROM tab
)
SELECT Score, Name, rn - (SELECT rn FROM cte WHERE Name = 'Emilie')
FROM cte
WHERE ABS(rn - (SELECT rn FROM cte WHERE Name = 'Emilie')) <=5
ORDER BY Score

LiveDemo

输出:

╔═══════╦═══════════╦══════╗
║ Score ║   Name    ║ Rank ║
╠═══════╬═══════════╬══════╣
║     5 ║ Danielle  ║   -5 ║
║    12 ║ Albert    ║   -4 ║
║    13 ║ Lily      ║   -3 ║
║    32 ║ Kellam    ║   -2 ║
║    34 ║ Fabrice   ║   -1 ║
║    65 ║ Emilie    ║    0 ║
║    76 ║ Hasting   ║    1 ║
║    85 ║ Bernard   ║    2 ║
║    90 ║ Isidor    ║    3 ║
║    98 ║ Christian ║    4 ║
║   123 ║ Gaston    ║    5 ║
╚═══════╩═══════════╩══════╝

我假设该名称是唯一的。如果不是,您可能需要在子查询中使用TOP 1ORDER BY。更好的方法是使用(SELECT rn FROM cte WHERE id = 5),因为Id 是独一无二的。

如果分数值可以相同,您可以将ROW_NUMBER() 更改为DENSE_RANK()

【讨论】:

    【解决方案2】:

    更新,准确找到 5 before5 after emilie

    MySQL 5.6 架构设置

    CREATE TABLE u
        (`Id` int, `Name` varchar(9), `Score` int)
    ;
    
    INSERT INTO u
        (`Id`, `Name`, `Score`)
    VALUES
        (1, 'Albert', 12),
        (2, 'Bernard', 85),
        (3, 'Christian', 98),
        (4, 'Danielle', 5),
        (5, 'Emilie', 65),
        (6, 'Fabrice', 34),
        (7, 'Gaston', 123),
        (8, 'Hasting', 76),
        (9, 'Isidor', 90),
        (10, 'Jacques', 3),
        (11, 'Kellam', 32),
        (12, 'Lily', 13),
        (13, 'Mael', 4242),
        (14, 'a', 65),
        (15, 'b', 65),
        (16, 'c', 65),
        (17, 'd', 65)
    ;
    

    查询 1

      SELECT *
      FROM (
        SELECT *
        FROM (
          SELECT *
          FROM u
          WHERE 
            Score >= (
              SELECT Score
              FROM u
              WHERE Name = 'Emilie'
            )
            AND Name != 'Emilie'
          ORDER BY Score ASC
          LIMIT 5
        ) a
        ORDER BY a.Score DESC
      ) z
    UNION ALL
      SELECT *
      FROM u
      WHERE Name = 'Emilie'
    UNION ALL
      SELECT *
      FROM (
        SELECT *
        FROM u
        WHERE 
          Score < (
            SELECT Score
            FROM u
            WHERE Name = 'Emilie'
          )
        ORDER BY Score DESC
        LIMIT 5
      ) w
    

    Results

    | Id |     Name | Score |
    |----|----------|-------|
    |  8 |  Hasting |    76 |
    | 16 |        c |    65 |
    | 15 |        b |    65 |
    | 17 |        d |    65 |
    | 14 |        a |    65 |
    |  5 |   Emilie |    65 |
    |  6 |  Fabrice |    34 |
    | 11 |   Kellam |    32 |
    | 12 |     Lily |    13 |
    |  1 |   Albert |    12 |
    |  4 | Danielle |     5 |
    

    【讨论】:

    • 简体:select * from ((select * from users where score &gt;= 65 limit 5) union all (select * from users where score &lt; 65 limit 4)) a order by score;
    • 嗯,我的回答比较简单,但事实是它在结构上与你的相同,所以我删除了它。
    • @MikeNakis 哦,谢谢,没想到会这样。所以是的,你使用固定值,但似乎 OP 想要“围绕用户”,我为它放置了子查询。重点是我的查询,如果我有与 Emilie 得分相同的人(因为他们同时超过和低于),我不会显示 12 结果。所以我会添加一个注释,因为我不知道 op 想要什么......
    • 不幸的是,当多个用户的分数相同时它不起作用:您可以看到,在这种情况下,结果不会以 Emilie 为中心(她将排在第三位)开始,而不是第六个)。这可能是一个相当大的问题,因为 Emilie 可能会被排除在最终结果之外(cf here
    • @MaëlNison 好的,看看,但这是一个使用许多临时表的大查询,所以我认为这不是一个好的解决方案......
    【解决方案3】:

    考虑这个没有窗口函数的通用 SQL 解决方案,它应该兼容大多数 RDMS。

    您将需要手动或动态传递枢轴的人。使用通用编码(VBA、PHP、Python、Java、C# 等)或存储过程的参数化查询可以处理:

    SELECT [Name], [Score], [rank]
    FROM (
         SELECT t1.[Name], t1.[Score],
               (SELECT Count(*) FROM [pivotTable] t2
                WHERE t2.[Score] >= (SELECT t3.[Score] FROM [pivotTable] t3 
                                     WHERE t3.[Name]='Emilie')) -
               (SELECT Count(*) FROM [pivotTable] t2
                WHERE t1.[Score] <= t2.[Score]) As rank
         FROM [pivotTable] t1
         ) AS derivedTbl
    WHERE [rank] >= -5 AND [rank] <= 5
    ORDER BY [Score]
    

    【讨论】:

    • 这是不是 ANSI SQL。窗口函数 ANSI SQL 标准的一部分(已有 10 多年的历史了)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-29
    • 1970-01-01
    • 2014-02-05
    • 2012-03-08
    • 1970-01-01
    • 2012-11-10
    • 2022-08-23
    相关资源
    最近更新 更多