Как сделать способ заказывать записи в БД?

Мне нужно сделать возможность переупорядочивать записи, хранить в БД (я использую MS SQL, но, похоже, это неважно). Я вижу возможные 2 решения:

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

{"Нарушение ограничения UNIQUE KEY" IX_EscortItems ". Невозможно вставить   дублировать ключ в объекте 'dbo.EscortItems'. Значение повторяющегося ключа   (2, 20). \ R \ nПриложение завершено. "}

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

Каков правильный подход к решению этой проблемы?

1
добавлено автор Schroeder, источник
Какова цель заказа и какое переупорядочение вам требуется? Пожалуйста, опишите ваш фактический прецедент. Из вашего вопроса неясно, происходит ли переупорядочение для всех записей сразу или с помощью свопов или с помощью вставок или why , так важно, чтобы ваша программа имела уникальное ограничение на (и «Я узнал это из своего учебника» - это не достаточная причина!).
добавлено автор Peter LeFanu Lumsdaine, источник
-1 и голосом, чтобы закрыть, как неясно, у вас было достаточно времени, чтобы ответить на мои вопросы.
добавлено автор Peter LeFanu Lumsdaine, источник

8 ответы

Существует несколько подходов, которые вы можете использовать:

  1. The easiest one is to forgo the UNIQUE constraint on the column that specifies the ordering (either as linked list or as sequence number). It is then up to the software to ensure that eventually the database is consistent again.
  2. Use sequence numbers with gaps.
    If you initially start with sequence numbers like 10, 20, 30, etc., then you can re-order them by allocating sequence numbers in-between the ones already given out.

    For example, to put the fourth element between the first and second, the sequence numbers would become 10, 15, 20, 30, 50, etc.

    To avoid running out of gaps to place elements, you can either periodically normalize the sequence numbers, or you can use floating point sequence numbers.

4
добавлено
@ user285336: Если вы используете пробелы (и убедитесь, что всегда остается пробел), тогда никогда не нужно указывать два элемента одинакового номера для заказа. Даже не временно, поэтому вы можете сохранить УНИКАЛЬНЫЕ ограничения.
добавлено автор Dee, источник
Я понимаю о пробелах и использую этот шаблон (с шагом 10). Но наибольшей проблемой является УНИКАЛЬНОЕ ограничение. Конечно, я могу забыть об этом, но это неправильно. БД должна иметь все необходимые ограничения
добавлено автор CarLaTeX, источник

Четыре варианта для вас. Мне нравится вариант 4, но первый из них проще всего.

Вариант 1. Просто запустите один выше

Значения в столбце Order не имеют значения, только их относительные значения. Будете ли вы указывать десять строк от 1 до 10, от 11 до 20 или от 57 до 66 - все одинаковы.

Таким образом, очень простое решение состоит в том, чтобы запустить один из них выше, чем ваш самый высокий Order : если ваши строки имеют значения Order от 1 до 10, и вам нужно их отменить, присвойте им значения от 20 до 11.

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

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

Вариант 2. Включить номер версии сортировки в ограничение уникальности

Вместо добавления одного столбца Order добавьте два, SortOrder и SortVersion . Примените ограничение уникальности к комбинации этих двух столбцов.

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

Вариант 3. Удалите строки и снова вставьте их

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

Это может оказаться невозможным, если вы не используете EF или если есть отношения FK, которые приведут к каскадному удалению.

Вариант 4. Сохраните столбец «упорядочить по» в отдельной таблице

Технически это самый нормализованный вариант, поскольку порядок сортировки, вероятно, не является атрибутом объекта, а является атрибутом отношения между сущностью и другим объектом. Таким образом, Order фактически нарушает 3NF, верьте или нет - значения столбцов должны зависеть от ключа (1NF), всего ключа (2NF) и ничего, кроме первичного ключа (3NF), и в этом случае столбец Order зависит от отношения объекта в более широком контексте. Чтобы нормализовать это, вы должны иметь таблицу соединений.

Итак, допустим, у вас есть таблица счетов и таблица line_item. Добавьте таблицу третьей таблицы, invoice_line_item, которая содержит InvoiceID , LineItemID и SortOrder . Если вы хотите изменить порядок строк, удалите их все, а затем снова вставьте их. Это позволяет избежать любых проблем с FK или связанными объектами, поскольку эти FK не будут в этой третьей таблице, а одна из других таблиц. Это сводится к изменению отношения между позициями и счетами, поэтому имеет смысл удалить и заменить их. Естественно, вы захотите обернуть все это в транзакцию.

4
добавлено

Единственным ограничением должно быть сочетание столбца Order и внешнего ключа с родительской таблицей. В качестве примера я использую «корзину покупок».

Table: ShoppingCartItems
- ShoppingCartId (FK to ShoppingCarts table)
- DisplayNumber (int)

В этом случае единственное ограничение обеспечит комбинацию ShoppingCartItems.ShoppingCartId и ShoppingCartItems.DisplayNumber уникально в таблице - так, чтобы в корзине покупок не было более одного элемента с тем же порядком отображения.

3
добавлено
@BartvanIngenSchenau: Исправил мой ответ. Я написал «первичный ключ» и думал «внешний ключ». Sheesh. Это был один из тех дней.
добавлено автор DenNukem, источник
Если Items.Id - первичный ключ, то комбинация этого и любого другого столбца тривиально уникальна, поскольку только первичный ключ уже уникален.
добавлено автор Dee, источник

Я использую отрицательный для temp

declare int @rowA = 7 
declare int @rowB = 22

update table set order = [email protected] where order =  @rowA; 
update table set order =  @rowA where order =  @rowB; 
update table set order =  @rowB where order = [email protected];

Вы можете обернуть его в транзакцию, но, скорее всего, вы не столкнетесь с конфликтами

3
добавлено
Я не был включен, но я должен был заключить транзакцию, чтобы другой клиент не видел временную строку.
добавлено автор svec, источник
Приходите на голосование, в чем проблема?
добавлено автор Flamewires, источник

Часто при использовании столбца заказа важно, чтобы сам заказ, а не конкретные значения. Поэтому, если вы используете столбец заказов, вам не нужно заботиться о том, какие числа в нем есть, если они отсортированы правильно относительно друг друга.

Поэтому, если вы хотите переместить запись в порядке, вы можете:

  • увеличивать все значения порядка из точки вставки и выше для создания открытого слота
  • обновить значение порядка записи в этом слоте.

Нет конфликтов

Далее проиллюстрировано. Если у вас есть значения для заказа:

1,2,3,4,5,6,7,8

и вы хотите переместить элемент на # 7 в # 3, вы сначала запускаете обновление, например

update foo set order = order +1 where container = X and order >=3

Это дает вам

1,2,4,5,6,7,8,9

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

update foo set order = 3 where id = 102334

И вы можете

1,2,3,4,5,6,7,9

Вы должны следовать аналогичной схеме для других типов переустановки.

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

2
добавлено

Здесь вы должны углубиться в требования и не слишком беспокоиться о реализации. Он заботится о себе. Нет лучшего способа сделать это.

Кто-нибудь действительно заботится, есть ли у вас уникальные номера для сортировки? Что делать, если пользователь (при условии, что я делаю это вручную), мне все равно, все ли они равны нулю? Если вы думаете, что они непреднамеренно будут включать в себя дубликат, и они предполагают, что заказ будет соответствовать тому, что они видят в своем интерфейсе, вы всегда можете опубликовать предупреждение. Опять же, существует ли потребность в этом уникальном и отсортированном номере, например, в системе выставления счетов (я помню, как работала над системой для фирмы в Германии, которая не допускает пробелов в этих числах)?

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

Я работаю с системой, которая позволяет пользователю настраивать сортировку по выпадающим спискам. Администратор назначает номера, и вы просто заставляете его работать. Ловушка для дубликатов просто не стоит.

2
добавлено

Часть основной идеи реляционных баз данных в целом состоит в том, что записи не имеют «неотъемлемого» порядка в самой базе данных 1 .

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

Например, мы могли бы иметь возможность создавать отчеты о заказах по дате/времени каждого заказа (т. Е. Отображать их в том порядке, в котором они были размещены):

select ID, value, date /* , ... */ from Orders
order by ship_date

... или дата/время отправки заказа,

select ID, value, date /* , ... */ from Orders
order by ship_date

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

select ID, value, date /* , ... */ from Orders
order by value desc

Вы можете, конечно, добавить столбец order_by (по другому имени, если хотите), когда/если вы действительно хотите, чтобы заказы отображались в порядке, который является полностью произвольным (или, по крайней мере, на основе критериев, которые не хранятся в базе данных). В этом случае, когда вы хотите изменить порядок, у вас есть несколько вариантов. Один заключается в том, чтобы просто не сообщить базе данных, что этот столбец содержит уникальные значения. Другой - просто избегать дублирования. Для вашего примера вы начали с записей с номерами 1 и 2. Чтобы отменить их, вы можете изменить числа на 4 и 3 соответственно.

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


1. Хотя MS SQL имеет концепцию «кластерного индекса», которая напрямую связана с порядком хранения записей - это в основном оптимизация. 2. Обычно вы индексируете этот столбец, поэтому эту операцию мы можем ожидать, что эта операция будет довольно быстрой.

2
добавлено
@JohnKugelman: Основываясь на содержании вопроса, я не вижу никаких оснований считать ненужным распространять основную информацию. Скорее наоборот, перечитывая его сейчас, я еще больше убежден, что основная информация была тем, что, вероятно, отсутствовало, и, вероятно, будет иметь наибольшую ценность. Если ваш пример перенумерации 1 и 2, когда он содержит от 1 до 10, является точным, тогда ваша реакция должна быть опротестована на вопрос, потому что он дает no намек на любое такое требование (а вторая половина мой ответ действительно охватывает эту возможность в любом случае).
добавлено автор achrn, источник

Три предложения:

  1. Всегда делайте свои обновления в последовательности, которая не вызывает дубликатов. Например, чтобы поменять местами 2 и 3, сначала обновить 2 с нулевым значением, затем вставить 2 для замены 3, а затем вставить 3.
  2. Вероятно, вам понадобится запрос для вставки записи в середине отсортированного списка. Если вы последовательно записываете свои записи, вам нужно будет перенумеровать многие записи, чтобы вставить их. Если вы используете связанный список, вам нужно обновить только два. По этой причине, я надеюсь, вы найдете связанный список менее дорогостоящим во время выполнения и, вероятно, проще закодировать.
  3. Я считаю условным назвать ваш столбец sort_key , порядковый или, возможно, position .

Внедрение с открытым исходным кодом для Active Record в ruby может вдохновить вас. Прочтите источник act_as_list gem и сравнить его с этими идеями.

2
добавлено
1. Затем нужно разрешить хранить NULL. Но моя бизнес-логика не позволяет этого, и я не хочу этого делать
добавлено автор CarLaTeX, источник
ОК. Тогда ответы других могут работать лучше для вас, например, с фиктивным значением или временно перемещать одну запись до конца или главы последовательности.
добавлено автор jroith, источник
SqlCom.ru - Стиль жизни SQL
SqlCom.ru - Стиль жизни SQL
908 участник(ов)

Правила чата - https://t.me/sqlcom/88269 @sqlcom - основной канал (только MS SQL) @sql_ninja - второй канал (SQL вопросы начального уровня и свободное общение) @Gopnegbot - Викторина по SQL Server (наберите в привате /quiz). Предложения в @sql_ninja

SQL_Ninja
SQL_Ninja
340 участник(ов)

Правила чата - https://t.me/sqlcom/88269 @sqlcom - основной канал (только SQL) @sql_ninja - второй канал (SQL вопросы начального уровня и свободное общение) @Gopnegbot - Викторина по SQL Server (наберите в привате /quiz)