Обновление порядка отображения нескольких строк MySQL в одном или нескольких запросах - PullRequest
16 голосов
/ 14 апреля 2009

У меня есть таблица с 20 строками, каждая с номером для порядка отображения (1-20).

SELECT * FROM `mytable` ORDER BY `display_order` DESC;

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

Конечно, не следует зацикливаться на запросе ОБНОВЛЕНИЯ для каждой строки, что является альтернативой в одном или нескольких запросах, подходящих для обновления одной ячейки в 20 или более строк, 50-200+?


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

Одна строка массива : Я мог бы иметь порядок в строке, содержащей уникальные идентификаторы строк в нужном мне порядке - например, строки 1,9,2,6,23. Когда заказ обновляется, скрытое поле обновляется с помощью JavaScript и добавляет его в базу данных или текстовый файл по завершении:

UPDATE `my_dispaly_order_table` SET `display_order`='1,9,2,6,23';

Обновлять каждую строку по отдельности : это то, чего я пытался избежать, но это можно было изменить очень редко, поэтому 20-30 вызовов в одном обращении один раз в неделю или месяц не могли бы быть проблемой, так просто я обычно делаю вызов UPDATE для каждой строки:

UPDATE `mytable` SET `display_order`='1' WHERE `rowId` = 1;
UPDATE `mytable` SET `display_order`='2' WHERE `rowId` = 9;
UPDATE `mytable` SET `display_order`='3' WHERE `rowId` = 2;
UPDATE `mytable` SET `display_order`='4' WHERE `rowId` = 6;
UPDATE `mytable` SET `display_order`='5' WHERE `rowId` = 23;

Ответы [ 11 ]

13 голосов
/ 23 апреля 2011

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

UPDATE mytable SET display_order =
  CASE id
    WHEN 10 THEN 1
    WHEN 23 THEN 2
    WHEN 4 THEN 3
  END CASE
WHERE id IN (10, 23, 4)

Мне нужно это в моем текущем приложении. В PHP я получаю сериализованный (и упорядоченный) набор идентификаторов из встроенной функции сортировки в jQuery UI. Итак, массив выглядит так:

$new_order = array(4, 2, 99, 15, 32); // etc

Чтобы сгенерировать одиночный запрос на обновление MySQL, я делаю это:

$query = "UPDATE mytable SET display_order = (CASE id ";
foreach($new_order as $sort => $id) {
  $query .= " WHEN {$id} THEN {$sort}";
}
$query .= " END CASE) WHERE id IN (" . implode(",", $new_order) . ")";

В конце "implode" дает мне мой список идентификаторов для IN (). Это прекрасно работает для меня.

10 голосов
/ 14 апреля 2009

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

-- Move #10 down (i.e. swap #10 and #11)
UPDATE mytable SET display_order =
  CASE display_order
    WHEN 10 THEN 11
    WHEN 11 THEN 10
  END CASE
WHERE display_order BETWEEN 10 AND 11;

-- Move #4 to #10
UPDATE mytable SET display_order
  CASE display_order
    WHEN 4 THEN 10
    ELSE display_order - 1
  END CASE
WHERE display_order BETWEEN 4 AND 10;

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

-- Swap in two steps will not work as demostrated here:

UPDATE mytable SET display_order = 10 WHERE display_order = 11;
-- Now you have two entries with display_order = 10

UPDATE mytable SET display_order = 11 WHERE display_order = 10;
-- Now you have two entries with display_order = 11 (both have been changed)

А вот ссылка на оператор CASE mysql .

4 голосов
/ 15 мая 2012

Немного поздно, но это может пригодиться кому-то еще:

UPDATE mytable SET display_order = FIND_IN_SET(rowId, '1,9,2,6,23') WHERE rowId in (1,9,2,6,23)
1 голос
/ 12 октября 2011

Это отличный способ сортировки элементов в базе данных. Именно то, что я искал;)

Вот улучшенная версия версии Jamon Holmgren. Эта функция полностью динамическая:

function reOrderRows($tablename, $ordercol, $idsarray){

    $query = "UPDATE $tablename SET $ordercol = (CASE $ordercol ";
    foreach($idsarray as $prev => $new) {
      $query .= " WHEN $prev THEN $new\n";
    }
    $query .= " END) WHERE $ordercol IN (" . implode(",", array_keys($idsarray)) . ")";

    mysql_query($query);
}

Предполагается, что у вас есть столбец $ ordercol на вашей таблице $ tablename только для целей заказа.

$ idsarray - это массив в массиве форматов ("current_order_num" => "Updated_order_num").

Так что, если вы просто хотите поменять две строки в своей таблице (представьте, что у вас есть, например, значки «двигаться вверх» и «двигаться вниз» на вашей веб-странице), вы можете использовать эту функцию поверх предыдущей:

function swapRows($tablename, $ordercol, $firstid, $secondid){
    $swaparray = array("$firstid" => "$secondid", "$secondid" => "$firstid");
    reOrderRows($tablename, $ordercol, $swaparray); 
}
1 голос
/ 07 мая 2009

Если вам нужно перетащить строки, это хорошая реализация для linked list.

Упорядочение строк с linked list означает, что вы будете обновлять не более 3 строк за раз - даже если вы переместите весь блок (пока он смежный).

Создайте таблицу из ваших строк следующим образом:

CREATE TABLE t_list (
        id INT NOT NULL PRIMARY KEY,
        parent INT NOT NULL,
        value VARCHAR(50) NOT NULL,
        /* Don't forget to create an index on PARENT */
        KEY ix_list_parent ON (parent)
)

id   parent  value

1    0       Value1
2    3       Value2
3    4       Value3
4    1       Value4

и используйте этот запрос MySQL для выбора строк по порядку:

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

При этом вы пройдете по связанному списку и вернете заказанные товары:

id   parent  value

1    0       Value1
4    1       Value4
3    4       Value3
2    3       Value2

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

См. Эту серию статей в моем блоге о том, как сделать это эффективно в MySQL:

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

1 голос
/ 07 мая 2009

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

Order    Item
-----    ----
1        Original Item 1
2        Original Item 2
3        Original Item 3
4        Original Item 4
5        Original Item 5

Если вы измените пункт 4 на 2-ю позицию, вы получите:

Order    Item
-----    ----
1        Original Item 1
1.5      Original Item 4
2        Original Item 2
3        Original Item 3
5        Original Item 5

Если вы измените пункт 3 на 3-ю позицию, вы получите:

Order    Item
-----    ----
1        Original Item 1
1.5      Original Item 4
1.75     Original Item 3
2        Original Item 2
5        Original Item 5

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

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

1 голос
/ 14 апреля 2009

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

UPDATE table SET col=col+1 WHERE col > 10
UPDATE table SET col=10 WHERE id = X
... 

Но на самом деле сложно использовать всю необходимую логику. Поскольку некоторые записи могут нуждаться в декременте и т. Д. Вы хотите избежать дубликатов и т. Д.

Подумайте об этом с точки зрения времени разработки и выигрыша.

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

0 голосов
/ 14 апреля 2009

Соберите новый заказ во временной переменной и поместите кнопку «сохранить этот заказ» в административную область. Затем сохраните порядок для строк с одним раундом.

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

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

0 голосов
/ 14 апреля 2009

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

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

Пример, позиция 10 сдвинута на 2-12

UPDATE table SET display_order = display_order -1 WHERE display_order BETWEEN 10 AND 12
UPDATE table SET display_order = 12 WHERE row_id = [id of what was row 10]
0 голосов
/ 14 апреля 2009

Добавьте идентификатор (или другой ключ) в таблицу и обновите, где идентификатор (или ключ) = идентификатор (или ключ) измененной строки.

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

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