【问题标题】:selecting rank from millions of records从数百万条记录中选择排名
【发布时间】:2017-03-30 08:08:05
【问题描述】:

我会向 SO 寻求帮助,因为我找不到类似的情况、问题/发布我的问题

假设我有数百万条记录,列是
user_id - 假设其记录从 1 到 1,000,000
名称 - 假设它在字母表中也记录了最多 20 个字符
分数 - 0 到 100 假设它也被记录了
日期 - 记录日期(时间戳)

user_id |   name   | score |        date       |
------------------------------------------------
23131   |   name1  |   15  | 2017-01-04 02:01:25
26824   |   name2  |   63  | 2017-01-04 02:41:33
19684   |   name3  |   28  | 2017-01-04 02:56:15
74937   |   name4  |   01  | 2017-01-04 04:07:55
27486   |   name5  |   75  | 2017-01-04 13:07:45
86476   |   name6  |   56  | 2017-01-04 14:21:47
36479   |   name7  |   19  | 2017-01-04 17:11:15
86752   |   name8  |   38  | 2017-01-04 18:22:23
11267   |   name9  |  100  | 2017-01-04 20:34:42
88763   |   name10 |   89  | 2017-01-04 22:45:43
  1. 如果我知道我的 user_id 是什么,我想知道自己的排名
  2. 我还想得到其他10条用户记录在我的排名上下,假设我的排名是100,我也想选择排名在90到99(高于我的排名)和101到110(低于我的排名)的用户).
    如果不同的用户按记录日期的得分顺序排名相同,则较早的记录具有较高的排名。

有可能吗?
假设所有记录都是唯一的并且没有设置索引。

我知道如何排序

SELECT * FROM record order by score

但这会让我选择所有记录,在不选择每条记录的情况下选择特定数据的实用方法是什么?

这是我想要实现的目标

user_id |   name   | score |        date          |     rank     |
------------------------------------------------------------------
12341   |   namep  |   90  | 2017-01-01 04:02:36  |      90      |
45341   |   nameo  |   88  | 2017-01-02 00:05:45  |      91      |
24341   |   namex  |   88  | 2017-01-03 00:11:15  |      92      |
26867   |   namec  |   83  | 2017-01-03 01:41:23  |      93      |
19156   |   nameb  |   81  | 2017-01-03 02:36:45  |      94      |
74973   |   namem  |   79  | 2017-01-03 04:07:55  |      95      |
23134   |   namek  |   78  | 2017-01-04 02:01:25  |      96      |
21424   |   namet  |   77  | 2017-01-04 02:41:33  |      97      |
19534   |   nameg  |   77  | 2017-01-04 02:56:15  |      98      |
74912   |   namez  |   75  | 2017-01-04 04:07:55  |      99      |

my_uid  |  my_name |   75  | 2017-01-04 13:07:45  |     100      |

86766   |   namen  |   75  | 2017-01-04 14:21:47  |     101      |
67976   |   namey  |   74  | 2017-01-04 16:22:23  |     102      |
34676   |   nameu  |   74  | 2017-01-04 17:33:32  |     103      |
86236   |   namei  |   73  | 2017-01-04 18:11:09  |     104      |
98636   |   nameo  |   73  | 2017-01-04 19:21:47  |     105      |
14326   |   namep  |   73  | 2017-01-04 20:33:22  |     106      |
45333   |   namet  |   72  | 2017-01-04 20:44:12  |     107      |
33323   |   namer  |   72  | 2017-01-04 21:34:26  |     108      |
11322   |   namee  |   71  | 2017-01-04 22:51:54  |     109      |
86633   |   namew  |   70  | 2017-01-04 22:55:33  |     110      |

好的,这就是我现在得到的,很抱歉我没有提到任何关于不使用 union 或 union all 的内容,我不能在我的项目中使用它。

但无论如何这是我的查询 我使用了“multi_query()”函数

$sql = "SELECT score, date FROM table_name WHERE user_id=your_user_id;" //assume you already know your user_id
$sql .= "SELECT name, score, date FROM table_name WHERE score >= your_score ORDER BY score, date LIMIT 10;"; //to get 10 rows that have greater or same score of your score order by date, earlier date is higher rank if score is the same with other user.
$sql .= "SELECT name, score, date table_name WHERE score <= your_score DESC, date ASC LIMIT 10"; //select score less than or equal to my score order by score and date

我得到这样的东西

my_uid  |  my_name |   75  | 2017-01-04 13:07:45  |     100      |

12341   |   namep  |   90  | 2017-01-01 04:02:36  |      90      |
45341   |   nameo  |   88  | 2017-01-02 00:05:45  |      91      |
24341   |   namex  |   88  | 2017-01-03 00:11:15  |      92      |
26867   |   namec  |   83  | 2017-01-03 01:41:23  |      93      |
19156   |   nameb  |   81  | 2017-01-03 02:36:45  |      94      |
74973   |   namem  |   79  | 2017-01-03 04:07:55  |      95      |
23134   |   namek  |   78  | 2017-01-04 02:01:25  |      96      |
21424   |   namet  |   77  | 2017-01-04 02:41:33  |      97      |
19534   |   nameg  |   77  | 2017-01-04 02:56:15  |      98      |
74912   |   namez  |   75  | 2017-01-04 04:07:55  |      99      |

74912   |   namez  |   75  | 2017-01-04 04:07:55  |      99      |
my_uid  |  my_name |   75  | 2017-01-04 13:07:45  |     100      |
86766   |   namen  |   75  | 2017-01-04 14:21:47  |     101      |
67976   |   namey  |   74  | 2017-01-04 16:22:23  |     102      |
34676   |   nameu  |   74  | 2017-01-04 17:33:32  |     103      |
86236   |   namei  |   73  | 2017-01-04 18:11:09  |     104      |
98636   |   nameo  |   73  | 2017-01-04 19:21:47  |     105      |
14326   |   namep  |   73  | 2017-01-04 20:33:22  |     106      |
45333   |   namet  |   72  | 2017-01-04 20:44:12  |     107      |
33323   |   namer  |   72  | 2017-01-04 21:34:26  |     108      |

我的问题是当使用多个查询时,由于我有 3 个查询,它仍然与执行 3 个不同的查询相同,我如何将它们组合为一个?不使用 union 或 union all?
在第三个查询中,如何从我的数据中设置起点?

【问题讨论】:

标签: mysql sql innodb


【解决方案1】:

在 MySQL 中,视图就像其他语言中的函数。

CREATE VIEW rankall AS SELECT * FROM record ORDER BY score;

SELECT * FROM rankall
    WHERE rank > (SELECT rank FROM rankall WHERE user_id = ID) - 11
    AND rank < (SELECT rank FROM rankall WHERE user_id = ID) + 11;

这是基本思想,但不能保证上面的代码可以工作:P

【讨论】:

  • 当然上面的代码是行不通的。排名本身不会突然出现在您的视野中。
【解决方案2】:

试试这个

SELECT user_id, name, score FROM record
WHERE score BETWEEN 
CONVERT((SELECT score FROM record WHERE user_id = (SELECT user_id FROM record WHERE score = '100') ), INTEGER) - 10
AND
CONVERT((SELECT score FROM record WHERE user_id = (SELECT user_id FROM record WHERE score = '100') ), INTEGER) + 10
ORDER BY score DESC, date DESC

【讨论】:

    【解决方案3】:

    可行的方法,但有些问题需要解决。这将获取所选用户两侧(得分明智)的记录,以及该用户的排名。然后按分数排序并计算排名。这确实可以正常工作,但如果所选用户是最高/最低得分手,则会搞砸。可以对此进行排序,但不确定是否可以使用真实数据。但会给你一些想法。

    这是使用用户 id 86474 作为您感兴趣的用户,并且两边各取 1 - 只是为了适合您提供的测试数据:-

    SELECT user_id,
            name, 
            score, 
            date,
            def_rank - (@ranking := @ranking -1) AS rank
    FROM
    (
        SELECT *
        FROM
        (
            (SELECT r1.user_id,
                    r1.name, 
                    r1.score, 
                    r1.date,
                    sub0.def_rank
            FROM record r1
            INNER JOIN record r2 ON r2.user_id = 86476
            CROSS JOIN 
            (
                SELECT COUNT(*) def_rank
                FROM record r1
                INNER JOIN record r2 ON r2.user_id = 86476
                WHERE r1.score >= r2.score
            ) sub0
            WHERE r1.score >= r2.score
            ORDER BY score ASC
            LIMIT 2) 
            UNION
            (SELECT r1.user_id,
                    r1.name, 
                    r1.score, 
                    r1.date,
                    sub0.def_rank
            FROM record r1
            INNER JOIN record r2 ON r2.user_id = 86476
            CROSS JOIN 
            (
                SELECT COUNT(*) def_rank
                FROM record r1
                INNER JOIN record r2 ON r2.user_id = 86476
                WHERE r1.score >= r2.score
            ) sub0
            WHERE r1.score <= r2.score
            ORDER BY score DESC
            LIMIT 2)
        ) sub97
        ORDER BY score
    ) sub1
    CROSS JOIN 
    (
        SELECT @ranking := 2
    ) sub2
    

    【讨论】:

    • 感谢您的回复!对于初学者来说,它看起来非常复杂的查询,我只知道基础知识,但我会看看它,谢谢你的想法!
    【解决方案4】:

    我终于得到了我想要得到的东西,所以我将回答我自己的问题

    SELECT score, date FROM rank WHERE uid=your_user_id; //your score and date recorded
    SELECT (count(*) + 1) AS rank FROM rank WHERE score > your_score OR (score = your_score AND date < date of your score recorded); //your rank
    SELECT * FROM rank WHERE score > your_score OR (score = your_score AND date < date of your score recorded) ORDER BY score ASC, date DESC LIMIT 10; //10 users above my rank, in your output you have to reverse the order
    SELECT * FROM rank WHERE score < your_score OR (score = your_score AND date > date of your score recorded) ORDER BY score DESC, date ASC LIMIT 10; //10 users below my rank
    

    感谢其他回复的用户:)

    【讨论】:

      猜你喜欢
      • 2016-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-27
      • 1970-01-01
      • 2019-02-28
      • 1970-01-01
      • 2019-04-29
      相关资源
      最近更新 更多