sql >> Database teknologi >  >> RDS >> Mysql

MySQL Contiguous Sequential Rows Field selv ved sletning og indsættelse

Jeg ved, at der er meget her. Jeg forsøgte at dokumentere det ret godt inde i koden og her og der. Den bruger lagrede procedurer. Du kan naturligvis trække koden ud og ikke bruge den metode. Den bruger en hovedtabel, der rummer de næste tilgængelige inkrementorer. Den bruger sikker INNODB Hensigtslåse for samtidighed. Det har en genbrugstabel og lagrede procs til at understøtte det.

Den bruger på ingen måde tabellen myTable . Det vises der for din egen fantasi baseret på kommentarer under dit spørgsmål. Sammenfatningen af ​​det er, at du ved, at du vil have huller ved DELETE . Du vil have en ordentlig måde at genbruge de slots, de sekvensnumre. Så når du DELETE en række, skal du bruge de lagrede processer i overensstemmelse hermed for at tilføje dette nummer. Naturligvis er der en lagret proc for at få det næste sekvensnummer til genbrug og andre ting.

Med henblik på test skal din sectionType ='enheder'

Og bedst af alt er det testet!

Skema:

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

Lagret 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'

Efter du har kaldt uspGetNextSequence() er det dit ANSVAR at sikre, at den sekvens #

er enten tilføjet til myTable (ved at bekræfte det), eller at hvis det mislykkes, indsætter du det i

genbrugstabellen med et kald til uspAddToReuseList(). Ikke alle indsættelser lykkes. Fokuser på denne del.

For med denne kode kan du ikke "sætte" den tilbage i sequences tabel på grund af

samtidighed, andre brugere og rækkevidden, der allerede er passeret. Så simpelthen, hvis indsættelsen mislykkes,

sæt nummeret i reuseMe via uspAddToReuseList()

...

Lagret 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

Lagret 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();

Lagret 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();

Lagret 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-idéer:

Lad GNS betyder et kald til uspGetNextSequence() .

Lad RS betyder Genbrugssekvens via et opkald til uspGetOneToReuse()

Når en ny INSERT er ønsket, ring RS :

A. Hvis RS returnerer -1, så skal intet genbruges, så kald GNS som returnerer N. Hvis du kan INSERT med myTable.seqNum=N med en bekræftelse er du færdig. Hvis du ikke kan INSERT det, og kald derefter uspAddToReuseList(N) .

B. Hvis RS returnerer> 0, bemærk i dit hoved, at slot har reuseMe.reused=1 , en god ting at huske. Så det antages at være i færd med at blive genbrugt med succes. Lad os kalde det sekvensnummer N. Hvis du kan INSERT med myTable.seqNum=N med en bekræftelse er du færdig. Hvis du ikke kan INSERT det, kald derefter uspOoopsResetToAvail(N) .

Når du anser det for sikkert at kalde uspCleanReuseList() gør det. Tilføjelse af en DATETIME til reuseMe tabel kan være en god idé, der angiver, når en række fra myTable oprindeligt slettede og forårsagede reuseMe række for at få dens originale INSERT .




  1. værdien er for lang til type karakter varierende (100) ------for nylig skiftet database, gjorde ikke noget i db

  2. PostgreSQL 9.6:Parallel sekventiel scanning

  3. hvad er forskellen mellem '!=' og '<>' i mysql

  4. Sådan aktiveres en fremmednøglebegrænsning i SQL Server (T-SQL-eksempler)