Использование столбца порядка сортировки в таблице базы данных - PullRequest
31 голосов
/ 22 декабря 2011

Допустим, у меня есть таблица Product в базе данных торгового сайта для хранения описания, цены и т. Д. Продуктов магазина. Каков наиболее эффективный способ сделать так, чтобы мой клиент мог повторно заказать эти продукты?

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

Id    Order
5     3
8     1
26    2
32    5
120   4

Теперь, что я могу сделать, чтобы изменить порядок записи с ID=26 на 3?

Я создал процедуру, которая проверяет, есть ли запись в целевом порядке (3) и обновляет порядок строки (ID = 26), если нет. Если в целевом порядке есть запись, процедура выполняется сама, отправляя идентификатор этой строки с target order + 1 в качестве параметров.

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

Id    Order
5     4
8     1
26    3
32    6
120   5

Так что бы сделал умный человек?

  • Я использую SQL Server 2008 R2.

Изменить:

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

В дополнение ко всему, мне интересно, могу ли я реализовать что-то вроде связанного списка: столбец «Далее» вместо столбца «Заказ» для сохранения идентификатора следующего элемента. Но я понятия не имею, как написать запрос, который получает записи в правильном порядке. Если у кого-то есть идеи по поводу этого подхода, пожалуйста, поделитесь.

Ответы [ 6 ]

27 голосов
/ 22 декабря 2011
Update product set order = order+1 where order >= @value changed

Хотя со временем вы получите все больше и больше "пробелов" в вашем заказе, но он все равно будет "сортировать"

Это добавит 1 к изменяемому значению и каждому значению после него в одном утверждении, но приведенное выше утверждение все еще верно. все больше и больше «пробелов» будут формироваться в вашем заказе, возможно, достигая точки превышения значения INT.

Альтернативное решение, если нет пробелов:

Представьте себе процедуру для: UpdateSortOrder с параметрами @NewOrderVal, @ IDToChange, @ OriginalOrderVal

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

If @NewOrderVal < @OriginalOrderVal --Moving down chain 

--Create space for the movement; no point in changing the original 
    Update product set order = order+1 
    where order BETWEEN @NewOrderVal and @OriginalOrderVal-1;

end if

If @NewOrderVal > @OriginalOrderVal --Moving up chain

--Create space  for the momvement; no point in changing the original  
  Update product set order = order-1 
  where order between @OriginalOrderVal+1 and @NewOrderVal
end if

--Finally update the one we moved to correct value

    update product set order = @newOrderVal where ID=@IDToChange;

Относительно наилучшей практики; В большинстве сред, в которых я находился, обычно требуется что-то сгруппированное по категориям и отсортированное в алфавитном порядке или на основе «популярности в продаже», что исключает необходимость предоставления пользовательской сортировки.

5 голосов
/ 22 декабря 2011

Используйте старый трюк, который использовали BASIC-программы (среди прочих мест): скачайте числа в столбце порядка на 10 или другое удобное приращение. Затем вы можете вставить одну строку (на самом деле, до 9 строк, если вам повезет) между двумя существующими числами (которые разделены на 10). Или вы можете переместить строку 370 на 565, не меняя ни одну из строк с 570 и выше.

5 голосов
/ 22 декабря 2011

Одним из решений, которое я использовал в прошлом, с некоторым успехом, является использование «веса» вместо «заказа».Вес очевиден: чем тяжелее предмет (то есть: чем меньше число) опускается на дно, тем легче (выше число) поднимается наверх.

Если у меня несколько предметов с одинаковымвес, я предполагаю, что они имеют одинаковое значение, и я упорядочиваю их в алфавитном порядке.

Это означает, что ваш SQL будет выглядеть примерно так:

ORDER BY 'weight', 'itemName'

надеюсь, что это поможет.

3 голосов
/ 12 ноября 2014

Вот альтернативный подход, использующий общее табличное выражение (CTE).

Этот подход учитывает уникальный индекс для столбца SortOrder и закрывает все пробелы в последовательности порядка сортировки, которые могли остаться после более ранних операций DELETE.

/* For example, move Product with id = 26 into position 3 */
DECLARE @id int = 26
DECLARE @sortOrder int = 3


;WITH Sorted AS (
    SELECT  Id,
            ROW_NUMBER() OVER (ORDER BY SortOrder) AS RowNumber
    FROM    Product
    WHERE   Id <> @id
)

UPDATE  p
SET     p.SortOrder = 
        (CASE 
            WHEN p.Id = @id THEN @sortOrder
            WHEN s.RowNumber >= @sortOrder THEN s.RowNumber + 1
            ELSE s.RowNumber
        END)
FROM    Product p
        LEFT JOIN Sorted s ON p.Id = s.Id 
3 голосов
/ 14 октября 2014

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

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

https://docs.google.com/document/d/14WuVyGk6ffYyrTzuypY38aIXZIs8H-HbA81st-syFFI/edit?usp=sharing

1 голос
/ 21 августа 2014

(простите за английский. Я учусь)

Очень просто. Вам нужна "кардинальная дыра"

структура, вам нужно иметь 2 столбца

1) pk = 32 бита, int

2) order = 64bit bigint (БОЛЬШОЙ, НЕ ДВОЙНОЙ !!!)

вставка / обновление

1) при вставке первой новой записи вы должны установить order = round (max_bigint / 2).

2) если вы вставляете в начало таблицы, вы должны установить order = round («порядок первой записи» / 2)

3) если вы вставляете в конец таблицы, вы должны установить order = round ("max_bigint - порядок последней записи" / 2)

4) если вы вставляете в середину, вы должны установить order = round («порядок записи до - порядок записи после» / 2)

этот метод имеет очень большую сердечность. если у вас есть ошибка ограничения или если вы думаете, что у вас небольшая мощность, вы можете перестроить столбец заказа (нормализовать).

в ситуации максимальности с нормализацией (с этой структурой) вы можете иметь «дыру в кардинальности» в 32 битах.

очень просто и быстро!

помните, НЕТ ДВОЙНОЙ !!! Только INT - порядок является значением точности!

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