在有序查询中查找记录及其邻居

时间:2015-12-05 21:52:43

标签: sql postgresql sqlite

我有一个用户列表。每个用户都有一个分数。我想查询我的数据库以获取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

3 个答案:

答案 0 :(得分:0)

更新,完全找到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 |

答案 1 :(得分:0)

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 :(得分:0)

考虑这种通用的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]