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

MySQL Contiguous Sequential Rows Field nawet przy usuwaniu i wstawianiu

Wiem, że jest tu dużo. Starałem się dobrze udokumentować to w kodzie i tu i tam. Wykorzystuje procedury składowane. Możesz naturalnie wyciągnąć kod i nie używać tej metody. Wykorzystuje główną tabelę, w której znajdują się kolejne dostępne inkrementatory. Używa bezpiecznego INNODB Blokady intencji dla współbieżności. Ma tabelę ponownego wykorzystania i przechowywane procedury, które ją obsługują.

W każdym razie nie używa tabeli myTable . Jest tam pokazywany dla własnej wyobraźni na podstawie komentarzy pod twoim pytaniem. Podsumowując, wiesz, że będziesz mieć luki po DELETE . Potrzebujesz uporządkowanej mody, aby ponownie wykorzystać te szczeliny, te numery sekwencji. Tak więc, gdy DELETE wiersz, użyj odpowiednio zapisanych procesów, aby dodać tę liczbę. Oczywiście istnieje przechowywany proces, który pozwala uzyskać następny numer sekwencji do ponownego użycia i innych rzeczy.

Na potrzeby testowania Twój sectionType ='urządzenia'

A co najważniejsze, jest przetestowany!

Schemat:

create table myTable
(   -- your main table, the one you cherish
    `id` int auto_increment primary key, -- ignore this
    `seqNum` int not null, -- FOCUS ON THIS
    `others` varchar(100) not null
) ENGINE=InnoDB;

create table reuseMe
(   -- table for sequence numbers to reuse
    `seqNum` int not null primary key, -- FOCUS ON THIS
    `reused` int not null -- 0 upon entry, 1 when used up (reused)
    -- the primary key enforces uniqueness
) ENGINE=InnoDB;;

CREATE TABLE `sequences` (
    -- table of sequence numbers system-wide
    -- this is the table that allocates the incrementors to you
    `id` int NOT NULL AUTO_INCREMENT,
    `sectionType` varchar(200) NOT NULL,
    `nextSequence` int NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `sectionType` (`sectionType`)
) ENGINE=InnoDB;
INSERT sequences(sectionType,nextSequence) values ('devices',1); -- this is the focus
INSERT sequences(sectionType,nextSequence) values ('plutoSerialNum',1); -- not this
INSERT sequences(sectionType,nextSequence) values ('nextOtherThing',1); -- not this
-- the other ones are conceptuals for multi-use of a sequence table

Zapisany proces:uspGetNextSequence

DROP PROCEDURE IF EXISTS uspGetNextSequence;
DELIMITER $$
CREATE PROCEDURE uspGetNextSequence(p_sectionType varchar(200))
BEGIN
    -- a stored proc to manage next sequence numbers handed to you.
    -- driven by the simple concept of a name. So we call it a section type.
    -- uses SAFE INNODB Intention Locks to support concurrency
    DECLARE valToUse INT;

    START TRANSACTION;
    SELECT nextSequence into valToUse from sequences where sectionType=p_sectionType FOR UPDATE;
    IF valToUse is null THEN
        SET valToUse=-1;
    END IF;
    UPDATE sequences set nextSequence=nextSequence+1 where sectionType=p_sectionType;
    COMMIT; -- get it and release INTENTION LOCK ASAP
    SELECT valToUse as yourSeqNum; -- return as a 1 column, 1 row resultset
END$$
DELIMITER ;
-- ****************************************************************************************
-- test:
call uspGetNextSequence('devices'); -- your section is 'devices'

Po wywołaniu uspGetNextSequence() Twoim obowiązkiem jest upewnienie się, że ta sekwencja #

jest dodany do myTable (potwierdzając to) lub że jeśli się nie powiedzie, wstawiasz go do

tabela ponownego wykorzystania z wywołaniem uspAddToReuseList(). Nie wszystkie wstawki się sprawdzają. Skoncentruj się na tej części.

Ponieważ z tym kodem nie można go "wstawić" z powrotem do sequences tabela z powodu

współbieżność, inni użytkownicy i zakres, który już przeszedł. Więc po prostu, jeśli wstawka się nie powiedzie,

umieść numer w reuseMe przez uspAddToReuseList()

...

Zapisany proces:uspAddToReuseList:

DROP PROCEDURE IF EXISTS uspAddToReuseList;
DELIMITER $$
CREATE PROCEDURE uspAddToReuseList(p_reuseNum INT)
BEGIN
    -- a stored proc to insert a sequence num into the reuse list
    -- marks it available for reuse (a status column called `reused`)
    INSERT reuseMe(seqNum,reused) SELECT p_reuseNum,0; -- 0 means it is avail, 1 not
END$$
DELIMITER ;
-- ****************************************************************************************
-- test:
call uspAddToReuseList(701); -- 701 needs to be reused

Zapisany proces:uspGetOneToReuse:

DROP PROCEDURE IF EXISTS uspGetOneToReuse;
DELIMITER $$
CREATE PROCEDURE uspGetOneToReuse()
BEGIN
    -- a stored proc to get an available sequence num for reuse
    -- a return of -1 means there aren't any
    -- the slot will be marked as reused, the row will remain
    DECLARE retNum int; -- the seq number to return, to reuse, -1 means there isn't one

    START TRANSACTION;

    -- it is important that 0 or 1 rows hit the following condition
    -- also note that FOR UPDATE is the innodb Intention Lock
    -- The lock is for concurrency (multiple users at once)
    SELECT seqNum INTO retNum 
    FROM reuseMe WHERE reused=0 ORDER BY seqNum LIMIT 1 FOR UPDATE;

    IF retNum is null THEN
        SET retNum=-1;
    ELSE 
        UPDATE reuseMe SET reused=1 WHERE seqNum=retNum; -- slot used
    END IF;
    COMMIT; -- release INTENTION LOCK ASAP

    SELECT retNum as yoursToReuse; -- >0 or -1 means there is none
END$$
DELIMITER ;
-- ****************************************************************************************
-- test:
call uspGetOneToReuse();

Zapisany proces:uspCleanReuseList:

DROP PROCEDURE IF EXISTS uspCleanReuseList;
DELIMITER $$
CREATE PROCEDURE uspCleanReuseList()
BEGIN
    -- a stored proc to remove rows that have been successfully reused
    DELETE FROM reuseMe where reused=1;
END$$
DELIMITER ;
-- ****************************************************************************************
-- test:
call uspCleanReuseList();

Zapisany proces:uspOoopsResetToAvail:

DROP PROCEDURE IF EXISTS uspOoopsResetToAvail;
DELIMITER $$
CREATE PROCEDURE uspOoopsResetToAvail(p_reuseNum INT)
BEGIN
    -- a stored proc to deal with a reuse attempt (sent back to you)
    -- that you need to reset the number as still available, 
    -- perhaps because of a failed INSERT when trying to reuse it
    UPDATE reuseMe SET reused=0 WHERE seqNum=p_reuseNum;
END$$
DELIMITER ;
-- ****************************************************************************************
-- test:
call uspOoopsResetToAvail(701);

Pomysły dotyczące przepływu pracy:

Niech GNS oznacza wywołanie uspGetNextSequence() .

Niech RS oznacza sekwencję ponownego użycia przez wywołanie uspGetOneToReuse()

Kiedy nowy INSERT jest pożądane, zadzwoń do RS :

A. Jeśli RS zwraca -1, to nic nie może być ponownie użyte, więc wywołaj GNS co zwraca N. Jeśli możesz pomyślnie INSERT z myTable.seqNum=N z potwierdzeniem, gotowe. Jeśli nie możesz pomyślnie INSERT to, a następnie wywołaj uspAddToReuseList(N) .

B. Jeśli RS zwraca> 0, pamiętaj, że slot ma reuseMe.reused=1 , warto pamiętać. Zakłada się więc, że jest w trakcie pomyślnego ponownego wykorzystania. Nazwijmy ten numer sekwencyjny N. Jeśli możesz pomyślnie INSERT z myTable.seqNum=N z potwierdzeniem, gotowe. Jeśli nie możesz pomyślnie INSERT to, a następnie wywołaj uspOoopsResetToAvail(N) .

Kiedy uważasz, że bezpieczne jest wywołanie uspCleanReuseList() Zrób tak. Dodawanie DATETIME do reuseMe tabela może być dobrym pomysłem, oznaczając, kiedy wiersz z myTable pierwotnie usuwał i powodował reuseMe wiersz, aby uzyskać oryginalny INSERT .




  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 przekonwertować podzapytanie skalarne SQL na wyrażenie SQLAlchemy?

  2. Używanie MySQLi z innej klasy w PHP

  3. Przekształć daty w zakres dat w MYSQL---jak radzić sobie z lukami w datach

  4. SQL Wybierz tylko wiersze, w których istnieje dokładnie wiele relacji

  5. Błąd #1442 MySQL - uruchamianie wyzwalacza