sql >> Databáze >  >> RDS >> Mysql

procedura mysql pro aktualizaci číselného odkazu v předchozích řádcích, když je jeden aktualizován

Myslím, že je třeba zvážit dva případy:

  1. Přesuňte jeden řádek tak, aby se v pořadí objevil dříve.
  2. Přesuňte jeden řádek, aby se v pořadí objevil později.

V obou případech je to netriviální. Není jasné, zda existuje jedinečné omezení ve sloupci „objednávka“; konečný výsledek by měl mít určitě jedinečné uspořádání.

Zápis:

  • „Zapnuto“ odkazuje na řádek s hodnotou „order =n“ ve starých hodnotách
  • „Nn“ odkazuje na řádek s „order =n“ v nových hodnotách

V příkladu (ilustrativní případ 1):

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

Jako alternativu zvažte přesunutí id =2 tak, aby mělo pořadí =4:

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

V podstatě přidáváte nebo odečítáte jeden z „ostatních“ řádků, kde jsou řádky ve starém pořadí mezi starou pozicí přesunutého řádku a novou pozicí přesunutého řádku. V pseudokódu pomocí $old a $new k identifikaci pozic před a za přesunutým řádkem a řešení případu 1 ($old> $new):

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;

Odpovídající kód pro případ 2 ($old <$new) je:

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;

Vzhledem k klauzuli WHERE v UPDATE jako celku možná budete moci odstranit druhou WHEN v CASE a nahradit ji jednoduchým 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;

Myslím, že uložená procedura je v pořádku - výběr mezi dvěma příkazy na základě vstupních parametrů $old, $new. Možná budete schopni něco udělat s rozumnou kombinací výrazů, jako je '($old - $new) / ABS($old - $new) ' a 'MIN($old, $new) ' a 'MAX($old, $new) ' kde MIN/MAX nejsou agregáty, ale komparační funkce pro pár hodnot (jak je lze nalézt ve Fortranu, mezi jinými programovacími jazyky).

Všimněte si, že předpokládám, že když se provádí jeden příkaz SQL, omezení jedinečnosti (pokud existuje) není vynuceno, protože se mění každý řádek - pouze po dokončení příkazu. To je nezbytné, protože ve skutečnosti nemůžete řídit pořadí, ve kterém jsou řádky zpracovávány. Vím o DBMS, kde by to způsobilo potíže; Vím o jiných, kde by tomu tak nebylo.

To vše lze provést v jediném příkazu SQL - ale chcete, aby uložená procedura třídila parametry příkazu. Používám IBM Informix Dynamic Server (11.50.FC6 na MacOS X 10.6.2), a to je jeden z DBMS, který vynucuje jedinečné omezení ve sloupci 'objednávka' na konci příkazu. Vytvořil jsem vývoj SQL bez omezení UNIQUE; to taky fungovalo, samozřejmě. (A ano, IDS vám umožňuje vrátit zpět příkazy DDL jako CREATE TABLE a CREATE PROCEDURE. Co jste říkal? Váš DBMS ne? Jak kuriózní!)

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;

Dvojice vyvolání uložené procedury s obrácenými čísly pokaždé obnovily původní pořadí. Je jasné, že bych mohl předefinovat v_inc proměnná, takže místo pouhé ±1 to bylo 'LET v_inc = v_inc - v_min + v_gap; ' a potom by výraz MOD byl jen 'MOD(order + v_inc, v_gap) '. Nezkontroloval jsem, zda to funguje se zápornými čísly.

Adaptace na MySQL nebo jiný DBMS je ponechána jako cvičení pro čtenáře.



  1. nastavení mamp portů na 80 a 3306

  2. Jak DEGREES() funguje v MariaDB

  3. MySQL – MariaDB – Psaní úplně první uložené procedury

  4. Prostorová vzdálenost Mysql pomocí POINT - Nefunguje