Vím, že je toho tady hodně. Snažil jsem se to docela dobře zdokumentovat uvnitř kódu a sem tam. Používá uložené procedury. Kód můžete přirozeně vytáhnout a tuto metodu nepoužít. Používá hlavní tabulku, která obsahuje další dostupné inkrementory. Používá bezpečný INNODB
Zámky záměru pro souběžnost. Má tabulku opětovného použití a uložené procesy, které ji podporují.
V žádném případě nepoužívá tabulku myTable
. Je to tam zobrazeno pro vaši vlastní představivost na základě komentářů pod vaší otázkou. Souhrnem toho je, že víte, že po DELETE
budete mít mezery . Chcete nějaký uspořádaný způsob, jak znovu použít ty sloty, ta sekvenční čísla. Když tedy DELETE
řádek, použijte k přidání tohoto čísla uložené procesy. Přirozeně existuje uložený proces pro získání dalšího pořadového čísla pro opětovné použití a další věci.
Pro účely testování váš sectionType
='zařízení'
A nejlepší ze všeho je, že je testován!
Schéma:
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
Uložený 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'
Poté, co zavoláte uspGetNextSequence(), je vaší ODPOVĚDNOSTÍ zajistit, aby tato sekvence #
je buď přidán do myTable
(jeho potvrzením), nebo že pokud selže, vložíte jej do
tabulku opětovného použití s voláním uspAddToReuseList(). Ne všechny vložky jsou úspěšné. Zaměřte se na tuto část.
Protože s tímto kódem jej nemůžete "vložit" zpět do sequences
tabulka kvůli
souběžnost, ostatní uživatelé a rozsah, který již prošel. Takže jednoduše, pokud vložení selže,
vložte číslo do reuseMe
přes uspAddToReuseList()
...
Uložený 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
Uložený 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();
Uložený 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();
Uložený 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);
Nápady na pracovní postup:
Nechte GNS znamená volání uspGetNextSequence()
.
Nechat RS znamená Opětovné použití sekvence prostřednictvím volání uspGetOneToReuse()
Při novém INSERT
je žádoucí, zavolejte RS :
A. Pokud RS vrátí -1, pak se nic nemá znovu použít, takže zavolejte GNS což vrátí N. Pokud úspěšně INSERT
s myTable.seqNum=N
s potvrzením máte hotovo. Pokud nemůžete úspěšně INSERT
a poté zavolejte uspAddToReuseList(N)
.
B. Pokud RS vrací> 0, všimněte si, že slot má reuseMe.reused=1
, dobrá věc k zapamatování. Předpokládá se tedy, že je v procesu úspěšného opětovného použití. Říkejme tomu pořadové číslo N. Pokud úspěšně INSERT
s myTable.seqNum=N
s potvrzením máte hotovo. Pokud nemůžete úspěšně INSERT
a poté zavolejte uspOoopsResetToAvail(N)
.
Když považujete za bezpečné zavolat uspCleanReuseList()
Učiň tak. Přidání DATETIME
do reuseMe
tabulka může být dobrý nápad, označující, kdy řádek z myTable
původně smazal a způsobil reuseMe
řádek, abyste získali jeho původní INSERT
.