Mysql
 sql >> Baza danych >  >> RDS >> Mysql

procedura mysql do aktualizacji odniesienia numerycznego w poprzednich wierszach, gdy jeden z nich zostanie zaktualizowany

Myślę, że należy rozważyć dwa przypadki:

  1. Przesuń jeden wiersz, aby pojawił się wcześniej w kolejności.
  2. Przesuń jeden wiersz, aby pojawił się później w kolejności.

Tak czy inaczej, to nie jest trywialne. Nie jest jasne, czy istnieje jednoznaczne ograniczenie w kolumnie „porządek”; wynik końcowy z pewnością ma mieć unikalną kolejność.

Notacja:

  • „On” odnosi się do wiersza z wartością „order =n” w starych wartościach
  • „Nn” odnosi się do wiersza z „kolejność =n” w nowych wartościach

W przykładzie (ilustrującym przypadek 1):

  • O3 --> N1
  • O1 --> N2
  • O2 --> N3

Jako alternatywę rozważ przeniesienie id =2, aby miało porządek =4:

  • O2 --> N4
  • O3 --> N2
  • O4 --> N3

Zasadniczo dodajesz lub odejmujesz jeden z „innych” wierszy, gdzie są to wiersze w starej kolejności między starą pozycją przeniesionego wiersza a nową pozycją przeniesionego wiersza. W pseudokodzie, użycie $old i $new do zidentyfikowania pozycji przed i po przeniesionym wierszu oraz radzenie sobie z przypadkiem 1 ($stary> $nowy):

UPDATE AnonymousTable
   SET order = CASE
               WHEN order = $old THEN $new
               WHEN order >= $new AND order < $old THEN order + 1
               END CASE
 WHERE order BETWEEN $new AND $old;

Odpowiedni kod dla przypadku 2 ($stary <$nowy) to:

UPDATE AnonymousTable
   SET order = CASE
               WHEN order = $old THEN $new
               WHEN order > $new AND order <= $old THEN order - 1
               END CASE
 WHERE order BETWEEN $old AND $new;

Biorąc pod uwagę klauzulę WHERE w UPDATE jako całości, możesz usunąć drugie WHEN w CASE i zastąpić je prostym ELSE.

UPDATE AnonymousTable
   SET order = CASE
               WHEN order = $old THEN $new
               ELSE                   order + 1
               END CASE
 WHERE order BETWEEN $new AND $old;

UPDATE AnonymousTable
   SET order = CASE
               WHEN order = $old THEN $new
               ELSE                   order - 1
               END CASE
 WHERE order BETWEEN $old AND $new;

Myślę, że procedura składowana jest w porządku — wybór między dwoma instrukcjami na podstawie parametrów wejściowych $stary, $nowy. Możesz być w stanie zrobić coś za pomocą rozsądnej kombinacji wyrażeń, takich jak „($old - $new) / ABS($old - $new) ' i 'MIN($old, $new) ' i 'MAX($old, $new) ' gdzie MIN/MAX nie są agregacjami, ale funkcjami porównawczymi dla pary wartości (jak w Fortran, między innymi językami programowania).

Zauważ, że zakładam, że podczas wykonywania pojedynczej instrukcji SQL ograniczenie unikalności (jeśli istnieje) nie jest wymuszane, ponieważ każdy wiersz jest zmieniany — tylko po zakończeniu instrukcji. Jest to konieczne, ponieważ nie możesz kontrolować kolejności przetwarzania wierszy. Wiem o DBMS, gdzie mogłoby to spowodować kłopoty; Znam inne, w których by nie było.

Wszystko to można wykonać w jednej instrukcji SQL — ale potrzebujesz procedury składowanej, aby uporządkować parametry instrukcji. Używam IBM Informix Dynamic Server (11.50.FC6 na MacOS X 10.6.2) i jest to jeden z DBMS, który wymusza unikatowe ograniczenie w kolumnie „order” na końcu instrukcji. Zrobiłem rozwój SQL bez ograniczenia UNIQUE; to też oczywiście zadziałało. (I tak, IDS pozwala na wycofywanie instrukcji DDL, takich jak CREATE TABLE i CREATE PROCEDURE. Co powiedziałeś? Twój DBMS nie? Jakie to osobliwe!)

BEGIN WORK;
CREATE TABLE AnonymousTable
(
    id      INTEGER NOT NULL PRIMARY KEY,
    title   VARCHAR(10) NOT NULL,
    order   INTEGER NOT NULL UNIQUE
);
INSERT INTO AnonymousTable VALUES(1, 'test1', 1);
INSERT INTO AnonymousTable VALUES(2, 'test2', 2);
INSERT INTO AnonymousTable VALUES(3, 'test3', 3);
INSERT INTO AnonymousTable VALUES(4, 'test4', 4);

SELECT * FROM AnonymousTable ORDER BY order;

CREATE PROCEDURE move_old_to_new(old INTEGER, new INTEGER)
    DEFINE v_min, v_max, v_gap, v_inc INTEGER;
    IF old = new OR old IS NULL OR new IS NULL THEN
        RETURN;
    END IF;
    LET v_min = old;
    IF new < old THEN
        LET v_min = new;
    END IF;
    LET v_max = old;
    IF new > old THEN
        LET v_max = new;
    END IF;
    LET v_gap = v_max - v_min + 1;
    LET v_inc = (old - new) / (v_max - v_min);
    UPDATE AnonymousTable
       SET order = v_min + MOD(order - v_min + v_inc + v_gap, v_gap)
     WHERE order BETWEEN v_min AND v_max;
END PROCEDURE;

EXECUTE PROCEDURE move_old_to_new(3,1);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(1,3);
SELECT * FROM AnonymousTable ORDER BY order;

INSERT INTO AnonymousTable VALUES(5, 'test5', 5);
INSERT INTO AnonymousTable VALUES(6, 'test6', 6);
INSERT INTO AnonymousTable VALUES(7, 'test7', 7);
INSERT INTO AnonymousTable VALUES(8, 'test8', 8);

EXECUTE PROCEDURE move_old_to_new(3,6);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(6,3);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(7,2);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(2,7);
SELECT * FROM AnonymousTable ORDER BY order;

ROLLBACK WORK;

Pary wywołań procedury składowanej z odwróconymi numerami za każdym razem przywracały pierwotną kolejność. Oczywiście mógłbym przedefiniować v_inc zmienna tak, aby zamiast być tylko ±1, była 'LET v_inc = v_inc - v_min + v_gap; ' i wtedy wyrażenie MOD będzie po prostu 'MOD(order + v_inc, v_gap) „. Nie sprawdziłem, czy działa to z liczbami ujemnymi.

Adaptacja do MySQL lub innego DBMS jest pozostawiona jako ćwiczenie dla czytelnika.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak działa dla każdego wiersza w wyzwalaczach w mysql?

  2. Jak zbudować prosty system oceniania i oceny na 5 gwiazdek?

  3. Wygeneruj plik Excel w PHP po echo

  4. pobieranie wartości, które nie istnieją w tabeli mysql

  5. Czy klucz obcy może działać jako klucz podstawowy?