sql >> Database >  >> RDS >> Mysql

MySQL Aaneengesloten sequentiële rijenveld, zelfs bij verwijderen en invoegen

Ik weet dat er hier veel is. Ik heb geprobeerd het vrij goed te documenteren in de code en hier en daar. Het maakt gebruik van opgeslagen procedures. Je kunt de code natuurlijk eruit halen en die methode niet gebruiken. Het gebruikt een hoofdtabel die de volgende beschikbare incrementors bevat. Het maakt gebruik van veilige INNODB Intentie Locks voor gelijktijdigheid. Het heeft een hergebruiktabel en opgeslagen procedures om het te ondersteunen.

Het gebruikt op geen enkele manier de tabel myTable . Het wordt daar getoond voor uw eigen verbeelding op basis van opmerkingen onder uw vraag. De samenvatting hiervan is dat je weet dat je hiaten zult hebben bij DELETE . Je wilt een ordelijke mode om die slots, die volgnummers, opnieuw te gebruiken. Dus, wanneer u DELETE een rij, gebruik dan de opgeslagen procedures om dat nummer toe te voegen. Natuurlijk is er een opgeslagen procedure om het volgende volgnummer te krijgen voor hergebruik en andere dingen.

Voor testdoeleinden, uw sectionType ='apparaten'

En het beste van alles is dat het is getest!

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

Opgeslagen Proc: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'

Nadat u uspGetNextSequence() hebt aangeroepen, is het uw VERANTWOORDELIJKHEID om ervoor te zorgen dat die reeks #

wordt ofwel toegevoegd aan myTable (door het te bevestigen), of dat als het niet lukt, u het invoegt in

de hergebruiktabel met een aanroep van uspAddToReuseList(). Niet alle inserts slagen. Focus op dit deel.

Omdat je deze code met deze code niet terug kunt "zetten" in de sequences tafel vanwege

concurrency, andere gebruikers en het bereik dat al voorbij is. Dus, gewoon, als het invoegen mislukt,

zet het nummer in reuseMe via uspAddToReuseList()

...

Opgeslagen Proc: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

Opgeslagen Proc: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();

Opgeslagen Proc: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();

Opgeslagen Proc: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-ideeën:

Laat GNS bedoel een aanroep naar uspGetNextSequence() .

Laat RS bedoel Hergebruik reeks via een oproep naar uspGetOneToReuse()

Wanneer een nieuwe INSERT is gewenst, bel RS :

A. Als RS geeft -1 terug, dan hoeft er niets opnieuw te worden gebruikt, dus bel GNS die N retourneert. Als je met succes INSERT . kunt met myTable.seqNum=N met een bevestiging bent u klaar. Als het u niet lukt om INSERT roep dan uspAddToReuseList(N) . aan .

B. Als RS retourneert> 0, noteer in je hoofd dat slot reuseMe.reused=1 . heeft , een goede zaak om te onthouden. Er wordt dus aangenomen dat het met succes wordt hergebruikt. Laten we dat volgnummer N noemen. Als het je lukt INSERT met myTable.seqNum=N met een bevestiging bent u klaar. Als het u niet lukt om INSERT roep dan uspOoopsResetToAvail(N) . aan .

Wanneer u het veilig acht om uspCleanReuseList() . te bellen doen. Een DATETIME toevoegen naar de reuseMe tabel kan een goed idee zijn, om aan te geven wanneer een rij uit myTable was oorspronkelijk aan het verwijderen en veroorzaakte de reuseMe rij om de originele INSERT . te krijgen .




  1. Beveiligingsbenaderingen in gegevensmodellering. Deel 3

  2. eigenaar van postgresql-database heeft geen toegang tot database - Geen relaties gevonden.

  3. Hoe de dimensionaliteit van een ARRAY-kolom te krijgen?

  4. SSIS-verbinding met Oracle