Специальный заказ по запросу SQL - PullRequest
3 голосов
/ 10 февраля 2009

Мне нужно отобразить список записей из таблицы базы данных, упорядоченный по некоторому числовому столбцу. Таблица выглядит так:

CREATE TABLE  items (
  position int NOT NULL,
  name varchar(100) NOT NULL,
);
INSERT INTO items (position, name) VALUE
(1, 'first'),
(5, 'second'),
(8, 'third'),
(9, 'fourth'),
(15, 'fifth'),
(20, 'sixth');

Теперь порядок списка должен меняться в зависимости от параметра, предоставленного пользователем. Этот параметр указывает, какая запись идет первой, например:

position = 0
order should be = 1, 5, 8, 9, 15, 20

position = 1
order should be = 20, 1, 5, 8, 9, 15

position = 2
order should be = 15, 20, 1, 5, 8, 9

Другими словами, последняя запись становится первой и так далее. Можете ли вы придумать способ сделать это в SQL?

Я использую MySQL, но пример для любой базы данных SQL подойдет.

Спасибо

Ответы [ 7 ]

2 голосов
/ 10 февраля 2009

Посмотрите, как это работает для вас. Использует универсальный SQL, поэтому он также должен быть действительным для MySql (не проверено).

DECLARE @user_sort INTEGER
SET @user_sort = 0

SELECT position, name FROM 
(
  SELECT I1.position, I1.name, COUNT(*) AS rownumber, (SELECT COUNT(*) FROM items) AS maxrows
  FROM items I1, items I2
  WHERE I2.position <= I1.position
  GROUP BY I1.position, I1.name
) Q1
ORDER BY 
  CASE WHEN maxrows - rownumber < (@user_sort % maxrows) THEN 1 ELSE 2 END, position

Примечание: * Если предоставленный пользователем индекс сортировки превышает число строк, значение будет перенесено в допустимый диапазон. Чтобы удалить эту функцию, удалите «% maxrows» из ORDER BY.

Результаты:

SET @user_sort = 0

position    name
1   first
5   second
8   third
9   fourth
15  fifth
20  sixth

SET @user_sort = 1

position    name
20  sixth
1   first
5   second
8   third
9   fourth
15  fifth

SET @user_sort = 2

position    name
15  fifth
20  sixth
1   first
5   second
8   third
9   fourth

SET @user_sort = 9

9   fourth
15  fifth
20  sixth
1   first
5   second
8   third
2 голосов
/ 10 февраля 2009

Вы уверены, что хотите сделать это в SQL?

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

Возможно использование связанного списка.

1 голос
/ 11 февраля 2009

Здесь я остановлюсь на решении для пляжа, но исключаю самостоятельное объединение и только дважды выбираю из таблицы items (и использую синтаксис Oracle):

select 
    i.position
  , i.name
from(
  select 
      items.*
    , ( SELECT COUNT(*) FROM items ) AS maxrows
  from items
  order by position
) i
order by 
  case 
    when rownum > maxrows - 2 -- NOTE: change "2" to your "position" variable
      then 1 - 1 / rownum -- pseudo-rownum < 1, still ascending
    else
      rownum
  end
;
1 голос
/ 10 февраля 2009

ORDER BY (ПОЛЕ (позиция, 1, 5, 8, 9, 15, 20) + параметр)% 7

Редактировать: чтобы сделать галерею арахиса счастливой, общее решение:

ORDER BY (SELECT ix + параметр - 1 FROM (SELECT i.position, @ix: = @ix + 1 AS ix FROM (SELECT @ix: = 0) AS n, элементы AS i ORDER BY position) AS s ГДЕ s.position = items.position)% (ВЫБЕРИТЕ СЧЕТ (*) ИЗ элементов)

0 голосов
/ 10 февраля 2009

Согласно комментарию Джона, но вместо этого используется синтаксис LIMIT (ROW_NUMBER / OVER не работает в MySQL и, кроме того, LIMIT намного проще для чтения):

(
    SELECT position, name FROM items
    ORDER BY position
    LIMIT @offset, @bignum
) UNION ALL (
    SELECT position, name FROM items
    ORDER BY position
    LIMIT @offset
)

Где @bignum - произвольное число, превышающее любое количество результатов, которое вы можете получить.

Я до сих пор не совсем уверен, что на практике это будет быстрее, чем реорганизация списка на веб-сервере ... это будет зависеть от того, как вы справляетесь с набором результатов и насколько он большой, я полагаю. Но, по крайней мере, он избегает самоперекрестного соединения, задействованного в умном подходе пляжа.

0 голосов
/ 10 февраля 2009

Это действительно не идеальная вещь для SQL.

У меня есть решение, но с большими таблицами оно будет медленным.

DECLARE @iOrder INT
SET @iOrder = 4

SELECT abc.position,abc.name FROM
(
SELECT position,name,ROW_NUMBER() OVER (ORDER BY position) AS rownum
FROM items
) abc
WHERE abc.rownum >= @iOrder
UNION ALL
SELECT def.position, def.name FROM
(
SELECT position,name,ROW_NUMBER() OVER (ORDER BY position) AS rownum
FROM items
) def
WHERE def.rownum < @iOrder

Обратите внимание, что использование UNION (без всех) приведет к изменению порядка результатов при поиске дубликатов

0 голосов
/ 10 февраля 2009

Если это заданный список, вы знаете количество элементов, которые вы можете сделать, например:

SELECT *
FROM Items
ORDER BY CASE WHEN Position >= Position THEN POSITION ELSE Position+1000 END

Но это действительно ужасно.

...