Лучший способ сохранить упорядоченный список в базе данных при сохранении порядка - PullRequest
66 голосов
/ 01 декабря 2008

Мне было интересно, есть ли у кого-нибудь хорошее решение проблемы, с которой я сталкивался много раз за последние годы.

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

Очевидным способом было бы просто вставить некоторый OrderField, где я бы назначил число 0 для N, и отсортировать его таким образом.

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

(я использую C # 3,5 с NHibernate и SQL Server 2005)

Спасибо

Ответы [ 12 ]

47 голосов
/ 27 апреля 2012

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

Использование числового (целочисленного) столбца в таблице, поддерживаемого запросами SQL

CREATE TABLE myitems (Myitem TEXT, id INTEGER PRIMARY KEY, orderindex NUMERIC);

Чтобы удалить элемент с индексом заказа 6:

DELETE FROM myitems WHERE orderindex=6;    
UPDATE myitems SET orderindex = (orderindex - 1) WHERE orderindex > 6;

Чтобы поменять местами два элемента (4 и 7):

UPDATE myitems SET orderindex = 0 WHERE orderindex = 4;
UPDATE myitems SET orderindex = 4 WHERE orderindex = 7;
UPDATE myitems SET orderindex = 7 WHERE orderindex = 0;

т.е. 0 не используется, поэтому используйте его как пустышку, чтобы избежать неоднозначного элемента.

Вставить в 3:

 UPDATE myitems SET orderindex = (orderindex + 1) WHERE orderindex > 2;
 INSERT INTO myitems (Myitem,orderindex) values ("MytxtitemHere",3)
31 голосов
/ 31 марта 2011

Лучшим решением является Двусвязный список . O (1) для всех операций, кроме индексации. Ничто не может быстро проиндексировать SQL, за исключением предложения where для нужного элемента.

0,10,20 типов терпят неудачу. Последовательности столбцов не работают. Столбец последовательности с плавающей точкой не выполняется при групповых перемещениях.

Двусвязный список - это те же операции, что и при добавлении, удалении, удалении группы, добавлении группы, перемещении группы. Единый связанный список тоже работает нормально. По моему мнению, двойная связь лучше с SQL. Единый связанный список требует, чтобы у вас был полный список.

24 голосов
/ 01 декабря 2008

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

10 голосов
/ 19 февраля 2010

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

5 голосов
/ 01 декабря 2008

К сожалению, для этого нет волшебной пули. Вы не можете гарантировать порядок любого заявления SELECT БЕЗ заказа по пункту. Вам нужно добавить столбец и запрограммировать его.

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

Извините, я не могу предложить ничего лучше, надеюсь, это помогло.

4 голосов
/ 02 декабря 2008

Я бы вообще не рекомендовал подходы A, AA, B, BA, BB. Для определения иерархии требуется много дополнительной обработки, и вставка записей между ними совсем неинтересна.

Просто добавьте OrderField, целое число. Не используйте пробелы, потому что тогда вам придется либо работать с нестандартным «шагом» на следующей средней вставке, либо вам придется сначала повторно синхронизировать список, а затем добавить новую запись.

Наличие 0 ... N легко переупорядочить, и если вы можете использовать методы Array или List вне SQL, чтобы переупорядочить коллекцию в целом, то обновите каждую запись, или вы можете выяснить, где вы находитесь вставка в, и +1 или -1 каждая запись после или перед ней соответственно.

Как только у вас будет написана небольшая библиотека, это будет кусок пирога.

1 голос
/ 21 июля 2015

Я решил это прагматично вот так:

  1. Порядок определяется в пользовательском интерфейсе.

  2. Бэкэнд получает запрос POST, который содержит идентификаторы и соответствующую позицию каждого элемента в списке.

  3. Я запускаю транзакцию и обновляю позицию для каждого идентификатора.

Готово.

Так что заказ стоит дорого, но чтение упорядоченного списка супер дешево.

1 голос
/ 03 августа 2009

На уровне абстракции над корзиной Items, скажем, CartOrder (который имеет 1-n с CartItem), вы можете поддерживать поле с именем itemOrder, которое может быть просто разделенным запятыми списком идентификаторов (PK) соответствующих записей CartItem. Это будет на прикладном уровне, который вам потребуется проанализировать и соответствующим образом упорядочить ваши модели элементов. Большим плюсом для этого подхода будет случай перестановок порядка, возможно, не будет изменений в отдельных объектах, но так как порядок сохраняется как поле индекса в строках таблицы элементов порядка, вам нужно будет выполнить команду обновления для каждого из строки обновляют свое индексное поле. Пожалуйста, дайте мне знать вашу критику в отношении этого подхода, мне любопытно узнать, каким образом это может потерпеть неудачу.

1 голос
/ 01 декабря 2008

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

Если в дальнейшем вы обнаружите это ограничение из-за низкой производительности при вставках и обновлениях, тогда можно использовать поле varchar вместо целого числа. Это обеспечивает довольно высокий уровень точности при вставке. Например, для вставки между элементами «A» и «B» вы можете вставить элемент, обозначенный как «AA». Это почти наверняка излишне для корзины покупок.

0 голосов
/ 25 декабря 2014

Когда я использую Hibernate, и мне нужно сохранить порядок @OneToMany, я использую Map, а не List.

@OneToMany(fetch = FetchType.EAGER, mappedBy = "rule", cascade = CascadeType.ALL)
@MapKey(name = "position")
@OrderBy("position")
private Map<Integer, RuleAction>    actions             = LazyMap.decorate(new LinkedHashMap<>(), FactoryUtils.instantiateFactory(RuleAction.class, new Class[] { Rule.class }, new Object[] { this }));

В этом примере Java position является свойством Integer RuleAction, поэтому порядок сохраняется таким образом. Я думаю, что в C # это будет выглядеть довольно похоже.

...