У меня есть следующие таблицы, которые составляют рейтинговую таблицу лидеров:
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);
Я могу присвоить специфическую c таблицу лидеров этим запросом 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 |
+----------------+--------------------+------+------------+-------+---------+--------------------+
Однако я не могу запросить указанный c идентификатор пользователя, чтобы узнать его ранг в таблице лидеров. Всегда говорится, что они ранжируются 1. Я предполагаю, что это потому, что фильтр для идентификатора пользователя применяется перед оконной функцией RANK (). Как выполнить запрос, чтобы он возвращал правильный ранг для указанного c идентификатора пользователя?
Это не работает:
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 |
+----------------+--------------------+------+------------+-------+---------+-------------------+
Правильный ранг для Идентификатор пользователя 3 должен быть 8, а не 1.
Кроме того, я хотел бы иметь возможность отфильтровывать указанный идентификатор пользователя c и возвращать записи «вокруг» этого пользователя в таблице лидеров. Поэтому, если пользователь занимает 5 место, и я хочу показать 4 записи вокруг них, я бы запросил идентификатор пользователя, а также выделил 2 строки перед ними и 2 строки за ними.
Любой помощь приветствуется. Спасибо!