Mysql
 sql >> Datenbank >  >> RDS >> Mysql

MySQL Contiguous Sequential Rows Field auch beim Löschen und Einfügen

Ich weiß, hier gibt es viel. Ich habe versucht, es ziemlich gut im Code und hier und da zu dokumentieren. Es verwendet gespeicherte Prozeduren. Sie können den Code natürlich herausziehen und diese Methode nicht verwenden. Es verwendet eine Haupttabelle, die die nächsten verfügbaren Inkrementoren enthält. Es verwendet sicheres INNODB Absichtssperren für Parallelität. Es hat eine Wiederverwendungstabelle und gespeicherte Prozeduren, um es zu unterstützen.

Es verwendet in keiner Weise die Tabelle myTable . Es wird dort für Ihre eigene Vorstellung basierend auf Kommentaren unter Ihrer Frage angezeigt. Die Zusammenfassung davon ist, dass Sie wissen, dass Sie bei DELETE Lücken haben werden . Sie wollen eine geordnete Art und Weise, diese Slots, diese Sequenznummern wiederzuverwenden. Also, wenn Sie DELETE einer Reihe, verwenden Sie die gespeicherten Prozeduren entsprechend, um diese Nummer hinzuzufügen. Natürlich gibt es eine gespeicherte Prozedur, um die nächste Sequenznummer für die Wiederverwendung und andere Dinge zu erhalten.

Zu Testzwecken Ihr sectionType ='Geräte'

Und das Beste:es wird getestet!

Schema:

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

Gespeicherte Prozedur: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'

Nachdem Sie uspGetNextSequence() aufgerufen haben, liegt es in Ihrer VERANTWORTUNG sicherzustellen, dass diese Sequenz #

entweder zu myTable hinzugefügt wird (indem Sie es bestätigen) oder dass Sie es, falls es fehlschlägt, in

einfügen

die Wiederverwendungstabelle mit einem Aufruf von uspAddToReuseList(). Nicht alle Einfügungen sind erfolgreich. Konzentrieren Sie sich auf diesen Teil.

Denn mit diesem Code kann man ihn nicht wieder in die sequences „einstecken“. Tabelle wegen

Nebenläufigkeit, andere Benutzer und der bereits vergangene Bereich. Also einfach, wenn die Einfügung fehlschlägt,

Geben Sie die Nummer in reuseMe ein über uspAddToReuseList()

...

Gespeicherte Prozedur: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

Gespeicherter Prozess: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();

Gespeicherter Prozess: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();

Gespeicherter Prozess: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);

Workflow-Ideen:

Lassen Sie GNS bedeuten einen Aufruf von uspGetNextSequence() .

Lassen Sie RS bedeutet Sequenz wiederverwenden über einen Aufruf von uspGetOneToReuse()

Wenn ein neuer INSERT gewünscht ist, rufen Sie RS an :

A. Wenn RS gibt -1 zurück, dann soll nichts wiederverwendet werden, also rufen Sie GNS auf was N zurückgibt. Wenn Sie erfolgreich INSERT können mit myTable.seqNum=N Mit einer Bestätigung sind Sie fertig. Wenn Sie INSERT nicht erfolgreich ausführen können Rufen Sie dann uspAddToReuseList(N) auf .

B. Wenn RS gibt> 0 zurück, beachten Sie in Ihrem Kopf, dass Slot reuseMe.reused=1 hat , eine gute Sache, an die man sich erinnert. Es wird also davon ausgegangen, dass es erfolgreich wiederverwendet wird. Nennen wir diese Sequenznummer N. Wenn Sie erfolgreich INSERT können mit myTable.seqNum=N Mit einer Bestätigung sind Sie fertig. Wenn Sie INSERT nicht erfolgreich ausführen können Rufen Sie dann uspOoopsResetToAvail(N) auf .

Wenn Sie es für sicher halten, uspCleanReuseList() aufzurufen tun Sie dies. Hinzufügen eines DATETIME zu reuseMe table könnte eine gute Idee sein, wenn es sich um eine Zeile aus myTable handelt wurde ursprünglich gelöscht und verursachte den reuseMe Zeile, um ihr ursprüngliches INSERT zu erhalten .