Я (пока) не тестировал выбранный ответ в MySQL в интересных случаях, когда есть связи в верхних трех местах, но я тестировал этот код в Informix для этих случаев, и он выдает ответ, который, я думаю, должен
Предполагая, что таблица называется leader_board
:
CREATE TABLE leader_board(id INTEGER NOT NULL PRIMARY KEY, value INTEGER NOT NULL);
INSERT INTO leader_board(id, value) VALUES(1, 6);
INSERT INTO leader_board(id, value) VALUES(2, 4);
INSERT INTO leader_board(id, value) VALUES(3, 10);
INSERT INTO leader_board(id, value) VALUES(4, 2);
INSERT INTO leader_board(id, value) VALUES(5, 7);
INSERT INTO leader_board(id, value) VALUES(6, 3);
Этот запрос работает с указанными данными, предполагая, что специальный идентификатор равен 4:
SELECT b.position - c.tied + 1 AS standing, a.id, a.value
FROM leader_board AS a
JOIN (SELECT COUNT(*) AS position, d.id
FROM leader_board AS d
JOIN leader_board AS e ON (d.value <= e.value)
GROUP BY d.id
) AS b
ON a.id = b.id
JOIN (SELECT COUNT(*) AS tied, f.id
FROM leader_board AS f
JOIN leader_board AS g ON (f.value = g.value)
GROUP BY f.id
) AS c
ON a.id = c.id
WHERE (a.id = 4 OR (b.position - c.tied + 1) <= 3) -- Special ID = 4; Top N = 3
ORDER BY position, a.id;
Вывод исходных данных:
standing id value
1 3 10
2 5 7
3 1 6
6 4 2
Пояснение
Два подзапроса тесно связаны, но дают разные ответы.Одно время я использовал две временные таблицы для хранения этих результатов.В частности, первый подзапрос (AS b
) создает позицию, но при наличии связей позиция является самой низкой, а не самой высокой из связанных позиций.То есть, учитывая:
ID Value
1 10
2 7
3 7
4 7
Выходные данные будут:
Position ID
1 1
4 2
4 3
4 4
Однако мы хотели бы считать их как:
Position ID
1 1
2 2
2 3
2 4
Итак,исправленная позиция - это исходная позиция за вычетом количества связанных значений (3 для ID ∈ {2, 3, 4}, 1 для ID 1) плюс 1. Второй подзапрос возвращает количество связанных значений для каждого ID.Возможно, есть более точный способ сделать это вычисление, но я не уверен, что это на данный момент.
Особые случаи
Однако код должен продемонстрировать, что он обрабатывает случаи, когда:
- Имеется 2 или более значений идентификатора с одинаковым верхним значением.
- Имеется 2 или более значений идентификатора с одинаковым вторым наивысшим рейтингом (но верхний является уникальным).
- Существует 2 или более значений идентификатора с одинаковым третьим наивысшим рейтингом (но два верхних являются уникальными).
Чтобы сохранить каждый раз переписывающий запрос, я преобразовалэто в хранимую процедуру в стиле Informix, которая принимает значения специального идентификатора и значения Top N (по умолчанию 3), которые должны отображаться, и превращает их в параметры процедуры.(Да, запись в предложении RETURNING странная.)
CREATE PROCEDURE leader_board_standings(extra_id INTEGER, top_n INTEGER DEFAULT 3)
RETURNING INTEGER AS standing, INTEGER AS id, INTEGER AS value;
DEFINE standing, id, value INTEGER;
FOREACH SELECT b.position - c.tied + 1 AS standing, a.id, a.value
INTO standing, id, value
FROM leader_board AS a
JOIN (SELECT COUNT(*) AS position, d.id
FROM leader_board AS d
JOIN leader_board AS e ON (d.value <= e.value)
GROUP BY d.id
) AS b
ON a.id = b.id
JOIN (SELECT COUNT(*) AS tied, f.id
FROM leader_board AS f
JOIN leader_board AS g ON (f.value = g.value)
GROUP BY f.id
) AS c
ON a.id = c.id
WHERE (a.id = extra_id OR (b.position - c.tied + 1) <= top_n)
ORDER BY position, a.id
RETURN standing, id, value WITH RESUME;
END FOREACH;
END PROCEDURE;
Это может быть вызвано для получения того же результата, что и раньше:
EXECUTE PROCEDURE leader_board_standings(4);
Чтобы проиллюстрировать различные случаи, описанные выше, добавьте и удалите лишние строки:
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
3 1 6
6 4 2
INSERT INTO leader_board(id, value) VALUES(10, 10);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
1 10 10
3 5 7
7 4 2
INSERT INTO leader_board(id, value) VALUES(11, 10);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
1 10 10
1 11 10
8 4 2
INSERT INTO leader_board(id, value) VALUES(12, 10);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
1 10 10
1 11 10
1 12 10
9 4 2
DELETE FROM leader_board WHERE id IN (10, 11, 12);
EXECUTE PROCEDURE leader_board_standings(6, 4); -- Special ID 6; Top 4
1 3 10
2 5 7
3 1 6
4 2 4
5 6 3
INSERT INTO leader_board(id, value) VALUES(7, 7);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
2 7 7
7 4 2
INSERT INTO leader_board(id, value) VALUES(13, 7);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
2 7 7
2 13 7
8 4 2
INSERT INTO leader_board(id, value) VALUES(14, 7);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
2 7 7
2 13 7
2 14 7
9 4 2
DELETE FROM leader_board WHERE id IN(7, 13, 14);
INSERT INTO leader_board(id, value) VALUES(8, 6);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
3 1 6
3 8 6
7 4 2
INSERT INTO leader_board(id, value) VALUES(9, 6);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
3 1 6
3 8 6
3 9 6
8 4 2
INSERT INTO leader_board(id, value) VALUES(15, 6);
EXECUTE PROCEDURE leader_board_standings(4);
1 3 10
2 5 7
3 1 6
3 8 6
3 9 6
3 15 6
9 4 2
EXECUTE PROCEDURE leader_board_standings(3); -- Special ID 3 appears in top 3
1 3 10
2 5 7
3 1 6
Для меня все выглядит правильно.