Получить порядок столбцов запроса - PullRequest
2 голосов
/ 02 сентября 2011

У меня есть таблица с двумя столбцами [id, value], оба числовые.

В этом примере:

[ id, value ]
[ 1,  6     ]
[ 2,  4     ]
[ 3,  10    ]
[ 4,  2     ]
[ 5,  7     ]
[ 6,  3     ]

Для данного идентификатора я хотел бы получить 3 верхних идентификатора (с наибольшим значением), их верхнюю позицию и, если заданный идентификатор не находится в верхних 3, также получить его позицию, идентификатор и значение:

Пример 1: ask_id = 5 Возврат:

[ position, id, value ]
[ 1,        3,  10    ]
[ 2,        5,  7     ]
[ 3,        1,  6     ]

Пример 2: ask_id = 4. Возвращение:

[ position, id, value ]
[ 1,        3,  10    ]
[ 2,        5,  7     ]
[ 3,        1,  6     ]
[ 6,        4,  2     ]

Итак, важные моменты:

  • Как получить за столбец позиции?
  • Как получить дополнительную строку, если это возможно (во всяком случае, нет проблем, если мне нужно два запроса)?

Ответы [ 4 ]

2 голосов
/ 02 сентября 2011
select t2.pos, t1.id, t1.value 
from test as t1
inner join
(select id, value, @pos:=if(@pos is null, 0, @pos)+1 as pos 
 from test order by value desc) as t2
on t1.id=t2.id
where t2.pos<=3 or t2.id={$ask_id}
order by t2.pos;
1 голос
/ 02 сентября 2011

По сути, идея такова:

  1. Ранжирование строк по value.

  2. Извлечение строк, где хотя бы один изверно следующее:

    • position BETWEEN 1 AND 3

    • id = @given_id

В этих статьях приведены примеры того, как можно заменить функции ранжирования (по крайней мере, самые фундаментальные из них, ROW_NUMBER()) в MySQL:

Этот метод следует использовать состорожно, хотя, как объясняет эта статья .

Тем не менее, одна из возможных реализаций вышеуказанных шагов может выглядеть так:

SET @pos = 0;

SELECT
  position,
  id,
  value
FROM (
  SELECT
    id,
    value,
    @pos := @pos + 1 AS position
  FROM atable
  ORDER BY value DESC
) s
WHERE position BETWEEN 1 AND 3
   OR id = @given_id
ORDER BY position
1 голос
/ 02 сентября 2011

Протестировано в MySQL для получения 3 верхних идентификаторов (с наибольшим значением) с положением в порядке возрастания.

set @num = 0;
SELECT @num := @num + 1 as position_sequence,id,value FROM tablename
ORDER BY value desc
limit 3;
0 голосов
/ 03 сентября 2011

Я (пока) не тестировал выбранный ответ в 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.Возможно, есть более точный способ сделать это вычисление, но я не уверен, что это на данный момент.

Особые случаи

Однако код должен продемонстрировать, что он обрабатывает случаи, когда:

  1. Имеется 2 или более значений идентификатора с одинаковым верхним значением.
  2. Имеется 2 или более значений идентификатора с одинаковым вторым наивысшим рейтингом (но верхний является уникальным).
  3. Существует 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

Для меня все выглядит правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...