Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Campo de linhas sequenciais contíguas do MySQL mesmo ao excluir e inserir


Eu sei que tem muito aqui. Tentei documentá-lo bastante bem dentro do código e aqui e ali. Ele usa procedimentos armazenados. Você pode extrair o código naturalmente e não usar esse método. Ele usa uma tabela principal que abriga os próximos incrementadores disponíveis. Ele usa INNODB seguro Bloqueios de intenção para simultaneidade. Ele tem uma tabela de reutilização e procs armazenados para apoiá-lo.

Ele não usa de forma alguma a tabela myTable . Ele é mostrado lá para sua própria imaginação com base nos comentários da sua pergunta. O resumo disso é que você sabe que terá lacunas ao DELETE . Você quer alguma forma ordenada para reutilizar esses slots, esses números de sequência. Então, quando você DELETE uma linha, use os procs armazenados de acordo para adicionar esse número. Naturalmente, há um proc armazenado para obter o próximo número de sequência para reutilização e outras coisas.

Para fins de teste, seu sectionType ='dispositivos'

E o melhor de tudo é testado!

Esquema:
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

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

Depois de chamar uspGetNextSequence(), é sua RESPONSABILIDADE garantir que essa sequência #

é adicionado em myTable (confirmando-o), ou que, se falhar, você o insere no

a tabela de reutilização com uma chamada para uspAddToReuseList(). Nem todas as inserções são bem-sucedidas. Concentre-se nesta parte.

Porque com este código você não pode "colocá-lo" de volta nas sequences mesa por causa

simultaneidade, outros usuários e o intervalo já passado. Então, simplesmente, se a inserção falhar,

coloque o número em reuseMe via uspAddToReuseList()

...

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

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

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

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

Ideias de fluxo de trabalho:

Deixe o GNS significa uma chamada para uspGetNextSequence() .

Deixe RS significa Sequência de reutilização por meio de uma chamada para uspGetOneToReuse()

Quando um novo INSERT desejado, ligue para RS :

A. Se RS retorna -1 então nada deve ser reutilizado então chame GNS que retorna N. Se você conseguir INSERT com sucesso com myTable.seqNum=N com uma confirmação, você está feito. Se você não conseguir INSERT e chame uspAddToReuseList(N) .

B. Se RS retorna> 0, observe em sua cabeça que o slot tem reuseMe.reused=1 , uma boa coisa a lembrar. Portanto, supõe-se que esteja em processo de reutilização com sucesso. Vamos chamar esse número de sequência de N. Se você conseguir INSERT com myTable.seqNum=N com uma confirmação, você está feito. Se você não conseguir INSERT e chame uspOoopsResetToAvail(N) .

Quando você considera seguro chamar uspCleanReuseList() faça isso. Adicionando um DATETIME para o reuseMe table pode ser uma boa ideia, denotando quando uma linha de myTable estava excluindo originalmente e causando o reuseMe linha para obter seu INSERT original .