Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

prosedur mysql untuk memperbarui referensi numerik di baris sebelumnya ketika ada yang diperbarui

Ada dua kasus yang perlu dipertimbangkan, menurut saya:

  1. Pindahkan satu baris agar muncul lebih awal dalam pengurutan.
  2. Pindahkan satu baris sehingga muncul kemudian dalam urutan.

Ini tidak sepele. Tidak jelas apakah ada batasan unik pada kolom 'order'; hasil akhirnya pasti memiliki urutan yang unik.

Notasi:

  • 'Aktif' mengacu pada baris dengan nilai 'order =n' di nilai lama
  • 'Nn' mengacu pada baris dengan 'order =n' dalam nilai baru

Dalam contoh (ilustratif kasus 1):

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

Sebagai alternatif, pertimbangkan untuk memindahkan id =2 sehingga memiliki orde =4:

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

Anda pada dasarnya menambahkan atau mengurangi satu dari baris 'lainnya', di mana itu adalah baris dalam urutan lama antara posisi lama dari baris yang dipindahkan dan posisi baru dari baris yang dipindahkan. Dalam kode semu, menggunakan $old dan $new untuk mengidentifikasi posisi sebelum dan sesudah dari baris yang dipindahkan, dan menangani kasus 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;

Kode yang sesuai untuk kasus 2 ($old <$new) adalah:

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;

Mengingat klausa WHERE pada UPDATE secara keseluruhan, Anda mungkin dapat menghapus WHEN kedua dalam CASE dan menggantinya dengan ELSE sederhana.

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;

Saya pikir prosedur tersimpan sudah beres - memilih di antara dua pernyataan berdasarkan parameter input $old, $new. Anda mungkin dapat melakukan sesuatu dengan campuran ekspresi yang bijaksana seperti '($old - $new) / ABS($old - $new) ' dan 'MIN($old, $new) ' dan 'MAX($old, $new) ' di mana MIN/MAX bukan agregat tetapi fungsi pembanding untuk sepasang nilai (seperti yang ditemukan di Fortran, di antara bahasa pemrograman lainnya).

Perhatikan bahwa saya berasumsi bahwa ketika satu pernyataan SQL dijalankan, batasan keunikan (jika ada) tidak diterapkan karena setiap baris diubah - hanya ketika pernyataan selesai. Ini diperlukan karena Anda sebenarnya tidak dapat mengontrol urutan pemrosesan baris. Saya tahu DBMS di mana ini akan menyebabkan masalah; Saya tahu orang lain di mana itu tidak.

Itu semua dapat dilakukan dalam satu pernyataan SQL - tetapi Anda ingin prosedur tersimpan untuk memilah parameter ke pernyataan. Saya menggunakan IBM Informix Dynamic Server (11.50.FC6 pada MacOS X 10.6.2), dan itu adalah salah satu DBMS yang memberlakukan batasan unik pada kolom 'pesanan' di akhir pernyataan. Saya melakukan pengembangan SQL tanpa batasan UNIK; yang bekerja juga, tentu saja. (Dan ya, IDS memungkinkan Anda untuk memutar kembali pernyataan DDL seperti CREATE TABLE dan CREATE PROCEDURE. Apa yang Anda katakan? DBMS Anda tidak? Sungguh aneh!)

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;

Pasangan pemanggilan dari stored procedure dengan angka dibalik mengembalikan urutan aslinya setiap kali. Jelas, saya dapat mendefinisikan ulang v_inc variabel sehingga bukannya hanya ±1, itu adalah 'LET v_inc = v_inc - v_min + v_gap; ' dan kemudian ekspresi MOD akan menjadi 'MOD(order + v_inc, v_gap) '. Saya belum memeriksa apakah ini berfungsi dengan angka negatif.

Adaptasi ke MySQL atau DBMS lainnya dibiarkan sebagai latihan untuk pembaca.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Perbaiki semua tabel sekaligus

  2. Bagaimana tabel innodb dikunci saat pemicu ON INSERT diproses?

  3. Mysql untuk memilih catatan per bulan meskipun data tidak ada

  4. MySQL> Tabel tidak ada. Tapi memang (atau seharusnya)

  5. MySQL 8 membuat pengguna baru dengan kata sandi tidak berfungsi