Как я могу хранить заказы? - PullRequest
12 голосов
/ 13 февраля 2009

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

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

  • Имя (номер заказа)

-

  • 1Пример (1)
  • 2Пример (2)
  • 3Пример (3)
  • 4Пример (4)
  • 5Пример (5)

-

  • 2Пример (1) *
  • 3Пример (2) *
  • 4Пример (3) *
  • 5Пример (4) *
  • 1Пример (5) *

* необходимо изменить в базе данных

также некоторые задачи могут быть удалены из-за их выполнения

Ответы [ 6 ]

14 голосов
/ 13 февраля 2009

Вы можете хранить ордера как литералы и использовать лексическую сортировку:

1. A
2. Z

Добавить задачу:

1. A
3. L
2. Z

Добавить больше:

1. A
4. B
3. L
2. Z

Перемещение 2 между 1 и 4:

1. A
2. AL
4. B
3. L

и т.д.

Вы обновляете только одну запись за раз: просто возьмите среднюю букву между первыми, которые отличаются: если вы поставите между A и C, вы получите B, если вы поставите между ALGJ и ALILFG, вы берете ALH.

Буква рядом с существующим считается как существующая объединенная с буквой рядом с Z. И. е. если вам нужно поставить между ABHDFG и ACSD F, вы посчитаете его между ABH и AB(Z+) и напишите AB(letter 35/2), то есть ABP.

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

Обновление:

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

См. Статью в моем блоге о том, как это сделать, в MySQL:

В двух словах:

/* This just returns all records in no particular order */

SELECT  *
FROM    t_list

id      parent
------- --------
1       0
2       3
3       4
4       1

/* This returns all records in intended order */

SELECT  @r AS _current,
        @r := (
        SELECT  id
        FROM    t_list
        WHERE   parent = _current
        )
FROM    (
        SELECT  @r := 0
        ) vars,
        t_list

_current id
-------  --------
0        1
1        4
4        3
3        2

При перемещении элементов вам необходимо обновить не более 4 строк.

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

9 голосов
/ 13 февраля 2009

Обычно я добавляю столбец типа int или smallint с именем что-то вроде 'Ordinal' или 'PositionOrdinal', как вы предлагаете, и с точным предупреждением, которое вы упомянули & mdash; необходимость обновления потенциально значительного числа записей каждый раз, когда одна запись переупорядочивается.

Преимущество состоит в том, что при наличии ключа для конкретной задачи и новой позиции для этой задачи код для перемещения элемента представляет собой всего два оператора:

UPDATE `Tasks` SET Ordinal= Ordinal+1 WHERE Ordinal>=@NewPosition
UPDATE `Tasks` SET Ordinal= @NewPosition WHERE TaskID= @TaskID

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

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

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

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

Из ваших ответов я придумал смесь, которая выглядит следующим образом:

Скажем, у нас есть:

  • 1Пример (1)
  • 2Пример (2)
  • 3Пример (3)
  • 4Пример (4)
  • 5Пример (5)

Теперь, если я отсортирую что-то между 4 и 5, это будет выглядеть так:

  • 2Пример (2)
  • 3Пример (3)
  • 4Пример (4)
  • 1Пример (4,5)
  • 5Пример (5)

теперь опять что-то между 1 и 5

  • 3Пример (3)
  • 4Пример (4)
  • 1Пример (4,5)
  • 2Пример (4,75)
  • 5Пример (5)

всегда будет половина разницы между числами

Я надеюсь, что это работает, пожалуйста, поправьте меня;)

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

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

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

Вы можете поручить эту работу клиентской стороне. Попросите клиента поддерживать старый порядок сортировки и новый порядок сортировки и определить, какие строки [порядок сортировки] должны быть обновлены, а затем передать эти кортежи в интерфейс PHP-mySQL.

Вы можете усовершенствовать этот метод следующим образом ( не требует плавающих чисел):

  1. Если все сортируемые элементы в списке инициализированы в порядке сортировки в соответствии с их положением в списке, установите порядок сортировки каждого элемента в нечто вроде строки [sort-order] = row [sort- order * K] где K - это некоторое число> среднее число раз, когда вы ожидаете, что список будет переупорядочен. O (N), N = количество элементов, но увеличивает емкость вставки как минимум на N * K, по крайней мере, на K открытых слотов между каждой выходящей парой элементов.

  2. Тогда, если вы хотите вставить элемент между двумя другими, это так же просто, как изменить порядок сортировки, чтобы он был> нижним элементом и <верхним. Если между элементами нет «места», вы можете просто применить алгоритм «распространения» (1), представленный в предыдущем абзаце. Чем больше К, тем реже оно применяется. </p>

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

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

Мы делаем это с помощью столбца Sequence в базе данных.

Мы используем разреженную нумерацию (например, 10, 20, 30, ...), чтобы мы могли «вставить» одну из существующих значений. Если соседние строки имеют последовательные номера, мы нумеруем минимальное количество строк, которое можем.

Возможно, вы могли бы использовать десятичные числа - взять среднее значение порядковых номеров для строк, смежных с тем местом, куда вы вставляете, тогда вам нужно только обновить строку, которую "перемещают"

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

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

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

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