如何查询排名排行榜 table 以获取特定用户条目和条目子集 "around" 用户?
How to query a ranked leaderboard table for specific user entry and subset of entries "around" the user?
我有以下构成排名排行榜的表格:
CREATE TABLE IF NOT EXISTS "user" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"username" varchar(200) NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS "leaderboard" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" varchar(200) NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS "leaderboard_entry" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"score" integer unsigned NOT NULL CHECK ("score" >= 0),
"leaderboard_id" integer NOT NULL REFERENCES "leaderboard" ("id") DEFERRABLE INITIALLY DEFERRED,
"user_id" integer NOT NULL REFERENCES "user" ("id") DEFERRABLE INITIALLY DEFERRED
);
CREATE INDEX "score_idx" ON "leaderboard_entry" ("score" DESC);
CREATE UNIQUE INDEX "leaderboard_id_user_id_idx" ON "leaderboard_entry" ("leaderboard_id", "user_id");
CREATE INDEX "leaderboard_id_idx" ON "leaderboard_entry" ("leaderboard_id");
CREATE INDEX "user_id_idx" ON "leaderboard_entry" ("user_id");
-- Create a leaderboard
INSERT INTO "leaderboard" ("name") VALUES ('Global Leaderboard');
-- Create some users
INSERT INTO "user" ("username") VALUES ('Extreme Hawk');
INSERT INTO "user" ("username") VALUES ('Screaming Whistler');
INSERT INTO "user" ("username") VALUES ('Crashing Underdog');
INSERT INTO "user" ("username") VALUES ('Burly Creature');
INSERT INTO "user" ("username") VALUES ('Snarky Acrobat');
INSERT INTO "user" ("username") VALUES ('Deadly Striker');
INSERT INTO "user" ("username") VALUES ('Dark Zebra');
INSERT INTO "user" ("username") VALUES ('Eager Raptor');
INSERT INTO "user" ("username") VALUES ('Snarky Leader');
INSERT INTO "user" ("username") VALUES ('Keen Joker');
-- Add some leaderboard entries with random scores
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 1, 15);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 2, 80);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 3, 45);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 4, 55);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 5, 95);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 6, 90);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 7, 90);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 8, 25);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 9, 60);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 10, 55);
我可以使用此 SQL 查询对特定排行榜进行排名:
SELECT "leaderboard_entry"."id",
"leaderboard_entry"."leaderboard_id",
"leaderboard_entry"."user_id",
"leaderboard_entry"."score",
RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "rank",
PERCENT_RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "percentile_rank",
"leaderboard"."id",
"leaderboard"."name",
"user"."id",
"user"."username"
FROM "leaderboard_entry"
INNER JOIN "leaderboard"
ON ("leaderboard_entry"."leaderboard_id" = "leaderboard"."id")
INNER JOIN "user"
ON ("leaderboard_entry"."user_id" = "user"."id")
WHERE "leaderboard_entry"."leaderboard_id" = 1
ORDER BY "leaderboard_entry"."score" DESC
这会产生正确的结果,其中每个条目都被正确排名:
+----------------+--------------------+------+------------+-------+---------+--------------------+
| Leaderboard ID | Leaderboard Name | Rank | Percentile | Score | User ID | User Name |
+----------------+--------------------+------+------------+-------+---------+--------------------+
| 1 | Global Leaderboard | 1 | 0.000 | 95 | 5 | Snarky Acrobat |
| 1 | Global Leaderboard | 2 | 0.111 | 90 | 6 | Deadly Striker |
| 1 | Global Leaderboard | 2 | 0.111 | 90 | 7 | Dark Zebra |
| 1 | Global Leaderboard | 4 | 0.333 | 80 | 2 | Screaming Whistler |
| 1 | Global Leaderboard | 5 | 0.444 | 60 | 9 | Snarky Leader |
| 1 | Global Leaderboard | 6 | 0.556 | 55 | 4 | Burly Creature |
| 1 | Global Leaderboard | 6 | 0.556 | 55 | 10 | Keen Joker |
| 1 | Global Leaderboard | 8 | 0.778 | 45 | 3 | Crashing Underdog |
| 1 | Global Leaderboard | 9 | 0.889 | 25 | 8 | Eager Raptor |
| 1 | Global Leaderboard | 10 | 1.000 | 15 | 1 | Extreme Hawk |
+----------------+--------------------+------+------------+-------+---------+--------------------+
但是,我无法查询特定用户 ID 以了解他们在排行榜中的排名。它总是说他们排名第一。我认为这是因为在 RANK() window 函数之前应用了用户 ID 过滤器。我如何进行查询才能使它 return 是特定用户 ID 的正确排名?
这行不通:
SELECT "leaderboard_entry"."id",
"leaderboard_entry"."leaderboard_id",
"leaderboard_entry"."user_id",
"leaderboard_entry"."score",
RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "rank",
PERCENT_RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "percentile_rank",
"leaderboard"."id",
"leaderboard"."name",
"user"."id",
"user"."username"
FROM "leaderboard_entry"
INNER JOIN "leaderboard"
ON ("leaderboard_entry"."leaderboard_id" = "leaderboard"."id")
INNER JOIN "user"
ON ("leaderboard_entry"."user_id" = "user"."id")
WHERE ("leaderboard_entry"."user_id" = 3 AND "leaderboard_entry"."leaderboard_id" = 1)
ORDER BY "leaderboard_entry"."score" DESC
+----------------+--------------------+------+------------+-------+---------+-------------------+
| Leaderboard ID | Leaderboard Name | Rank | Percentile | Score | User ID | User Name |
+----------------+--------------------+------+------------+-------+---------+-------------------+
| 1 | Global Leaderboard | 1 | 0.000 | 45 | 3 | Crashing Underdog |
+----------------+--------------------+------+------------+-------+---------+-------------------+
用户 ID 3 的正确排名应该是 8,而不是 1。
此外,我希望能够过滤特定用户 ID 和 return 排行榜上该用户的条目 "around"。因此,如果一个用户排名第 5,我想显示他们周围的 4 个条目,我将查询用户 ID 以及 select 他们前面的 2 行和他们后面的 2 行。
感谢任何帮助。谢谢!
Window 函数对生成的数据集进行操作,after 使用 where
谓词进行过滤。所以过滤后数据集中只剩下一条记录,它永远排在第一位。
您需要将现有查询转换为子查询,然后 对给定用户进行过滤:
SELECT *
FROM (
SELECT "leaderboard_entry"."id",
"leaderboard_entry"."leaderboard_id",
"leaderboard_entry"."user_id",
"leaderboard_entry"."score",
RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "rank",
PERCENT_RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "percentile_rank",
"leaderboard"."id",
"leaderboard"."name",
"user"."id",
"user"."username"
FROM "leaderboard_entry"
INNER JOIN "leaderboard"
ON ("leaderboard_entry"."leaderboard_id" = "leaderboard"."id")
INNER JOIN "user"
ON ("leaderboard_entry"."user_id" = "user"."id")
WHERE "leaderboard_entry"."leaderboard_id" = 1
) t
WHERE "user_id" = 3
请注意,不再需要 ORDER BY
子句,因为查询 returns 只是一行。否则,您也需要将其移动到外部查询。
您可以使用带有 window 函数的子查询,然后在外部查询中过滤用户:
select . . .
from (. . .
where "leaderboard_entry"."leaderboard_id" = 1
)
where "user_id" = 3
我有以下构成排名排行榜的表格:
CREATE TABLE IF NOT EXISTS "user" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"username" varchar(200) NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS "leaderboard" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" varchar(200) NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS "leaderboard_entry" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"score" integer unsigned NOT NULL CHECK ("score" >= 0),
"leaderboard_id" integer NOT NULL REFERENCES "leaderboard" ("id") DEFERRABLE INITIALLY DEFERRED,
"user_id" integer NOT NULL REFERENCES "user" ("id") DEFERRABLE INITIALLY DEFERRED
);
CREATE INDEX "score_idx" ON "leaderboard_entry" ("score" DESC);
CREATE UNIQUE INDEX "leaderboard_id_user_id_idx" ON "leaderboard_entry" ("leaderboard_id", "user_id");
CREATE INDEX "leaderboard_id_idx" ON "leaderboard_entry" ("leaderboard_id");
CREATE INDEX "user_id_idx" ON "leaderboard_entry" ("user_id");
-- Create a leaderboard
INSERT INTO "leaderboard" ("name") VALUES ('Global Leaderboard');
-- Create some users
INSERT INTO "user" ("username") VALUES ('Extreme Hawk');
INSERT INTO "user" ("username") VALUES ('Screaming Whistler');
INSERT INTO "user" ("username") VALUES ('Crashing Underdog');
INSERT INTO "user" ("username") VALUES ('Burly Creature');
INSERT INTO "user" ("username") VALUES ('Snarky Acrobat');
INSERT INTO "user" ("username") VALUES ('Deadly Striker');
INSERT INTO "user" ("username") VALUES ('Dark Zebra');
INSERT INTO "user" ("username") VALUES ('Eager Raptor');
INSERT INTO "user" ("username") VALUES ('Snarky Leader');
INSERT INTO "user" ("username") VALUES ('Keen Joker');
-- Add some leaderboard entries with random scores
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 1, 15);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 2, 80);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 3, 45);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 4, 55);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 5, 95);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 6, 90);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 7, 90);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 8, 25);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 9, 60);
INSERT INTO "leaderboard_entry" ("leaderboard_id", "user_id", "score") VALUES (1, 10, 55);
我可以使用此 SQL 查询对特定排行榜进行排名:
SELECT "leaderboard_entry"."id",
"leaderboard_entry"."leaderboard_id",
"leaderboard_entry"."user_id",
"leaderboard_entry"."score",
RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "rank",
PERCENT_RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "percentile_rank",
"leaderboard"."id",
"leaderboard"."name",
"user"."id",
"user"."username"
FROM "leaderboard_entry"
INNER JOIN "leaderboard"
ON ("leaderboard_entry"."leaderboard_id" = "leaderboard"."id")
INNER JOIN "user"
ON ("leaderboard_entry"."user_id" = "user"."id")
WHERE "leaderboard_entry"."leaderboard_id" = 1
ORDER BY "leaderboard_entry"."score" DESC
这会产生正确的结果,其中每个条目都被正确排名:
+----------------+--------------------+------+------------+-------+---------+--------------------+
| Leaderboard ID | Leaderboard Name | Rank | Percentile | Score | User ID | User Name |
+----------------+--------------------+------+------------+-------+---------+--------------------+
| 1 | Global Leaderboard | 1 | 0.000 | 95 | 5 | Snarky Acrobat |
| 1 | Global Leaderboard | 2 | 0.111 | 90 | 6 | Deadly Striker |
| 1 | Global Leaderboard | 2 | 0.111 | 90 | 7 | Dark Zebra |
| 1 | Global Leaderboard | 4 | 0.333 | 80 | 2 | Screaming Whistler |
| 1 | Global Leaderboard | 5 | 0.444 | 60 | 9 | Snarky Leader |
| 1 | Global Leaderboard | 6 | 0.556 | 55 | 4 | Burly Creature |
| 1 | Global Leaderboard | 6 | 0.556 | 55 | 10 | Keen Joker |
| 1 | Global Leaderboard | 8 | 0.778 | 45 | 3 | Crashing Underdog |
| 1 | Global Leaderboard | 9 | 0.889 | 25 | 8 | Eager Raptor |
| 1 | Global Leaderboard | 10 | 1.000 | 15 | 1 | Extreme Hawk |
+----------------+--------------------+------+------------+-------+---------+--------------------+
但是,我无法查询特定用户 ID 以了解他们在排行榜中的排名。它总是说他们排名第一。我认为这是因为在 RANK() window 函数之前应用了用户 ID 过滤器。我如何进行查询才能使它 return 是特定用户 ID 的正确排名?
这行不通:
SELECT "leaderboard_entry"."id",
"leaderboard_entry"."leaderboard_id",
"leaderboard_entry"."user_id",
"leaderboard_entry"."score",
RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "rank",
PERCENT_RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "percentile_rank",
"leaderboard"."id",
"leaderboard"."name",
"user"."id",
"user"."username"
FROM "leaderboard_entry"
INNER JOIN "leaderboard"
ON ("leaderboard_entry"."leaderboard_id" = "leaderboard"."id")
INNER JOIN "user"
ON ("leaderboard_entry"."user_id" = "user"."id")
WHERE ("leaderboard_entry"."user_id" = 3 AND "leaderboard_entry"."leaderboard_id" = 1)
ORDER BY "leaderboard_entry"."score" DESC
+----------------+--------------------+------+------------+-------+---------+-------------------+
| Leaderboard ID | Leaderboard Name | Rank | Percentile | Score | User ID | User Name |
+----------------+--------------------+------+------------+-------+---------+-------------------+
| 1 | Global Leaderboard | 1 | 0.000 | 45 | 3 | Crashing Underdog |
+----------------+--------------------+------+------------+-------+---------+-------------------+
用户 ID 3 的正确排名应该是 8,而不是 1。
此外,我希望能够过滤特定用户 ID 和 return 排行榜上该用户的条目 "around"。因此,如果一个用户排名第 5,我想显示他们周围的 4 个条目,我将查询用户 ID 以及 select 他们前面的 2 行和他们后面的 2 行。
感谢任何帮助。谢谢!
Window 函数对生成的数据集进行操作,after 使用 where
谓词进行过滤。所以过滤后数据集中只剩下一条记录,它永远排在第一位。
您需要将现有查询转换为子查询,然后 对给定用户进行过滤:
SELECT *
FROM (
SELECT "leaderboard_entry"."id",
"leaderboard_entry"."leaderboard_id",
"leaderboard_entry"."user_id",
"leaderboard_entry"."score",
RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "rank",
PERCENT_RANK() OVER (PARTITION BY "leaderboard_entry"."leaderboard_id" ORDER BY "leaderboard_entry"."score" DESC) AS "percentile_rank",
"leaderboard"."id",
"leaderboard"."name",
"user"."id",
"user"."username"
FROM "leaderboard_entry"
INNER JOIN "leaderboard"
ON ("leaderboard_entry"."leaderboard_id" = "leaderboard"."id")
INNER JOIN "user"
ON ("leaderboard_entry"."user_id" = "user"."id")
WHERE "leaderboard_entry"."leaderboard_id" = 1
) t
WHERE "user_id" = 3
请注意,不再需要 ORDER BY
子句,因为查询 returns 只是一行。否则,您也需要将其移动到外部查询。
您可以使用带有 window 函数的子查询,然后在外部查询中过滤用户:
select . . .
from (. . .
where "leaderboard_entry"."leaderboard_id" = 1
)
where "user_id" = 3