即使在删除和插入

时间:2016-09-09 23:31:54

标签: mysql sequential

我需要一个表中行的序列号序列,我需要确保它始终是顺序的,插入时没有间隙,当删除时我可以留下行间隙,但是在插入时我必须填补空白新行。原因是不同的系统必须与行记录一对一排列。然而,db可以在sql端和应用程序中被其他人操纵;我认为触发器将允许我完成更改的部分 - 但是如何确定我是否有间隙并执行此序列号的插入 - 即使我必须在单独的表中维护已删除的序列并且管理很好 - 无论桌子如何被操纵,我都需要与其他系统一对一排列。

自动增量字段将不起作用,因为行被删除,下一个插入将是最后一个自动增量值。我需要插入...或者可能保留行并添加一个字段IsDeleted并强制将表作为只读或不再插入/删除..但是如何做到这一点? 也许当插入行时,如果找到则可以在间隙处设置序列号,否则设置在最后。

有人有经验做这种事吗?

1 个答案:

答案 0 :(得分:2)

我知道这里有很多。我试着在代码中以及在这里和那里记录它。它使用存储过程。您可以自然地拉出代码而不使用该方法。它使用一个主表,容纳下一个可用的增量器。它使用安全的INNODB Intention Locks来实现并发。它有一个重用表和存储过程来支持它。

它无论如何都不使用表myTable。根据您提出的问题,根据您的想法显示出您自己的想象力。总结一下,您知道DELETE会有差距。你想要一些有序的方式重用那些插槽,那些序列号。因此,当您DELETE行时,相应地使用存储过程来添加该数字。当然,存在一个存储过程来获取下一个序列号以供重用和其他事情。

出于测试目的,您的sectionType ='设备'

最重要的是它经过测试!

架构:

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

存储过程: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'

在您调用uspGetNextSequence()之后,您有责任确保该序列#

要么添加到myTable(通过确认),要么如果失败,则将其插入

重用表,调用uspAddToReuseList()。并非所有插入都成功。关注这一部分。

因为使用此代码,由于

,您无法将其“放回”sequences表中

并发,其他用户以及已经传递的范围。所以,简单地说,如果插入失败,

通过uspAddToReuseList()

将号码放入reuseMe

。 。

存储过程: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

存储过程: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();

存储过程: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();

存储过程: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);

工作流程提示:

GNS 表示对uspGetNextSequence()的调用。

通过调用uspGetOneToReuse()

RS 表示重用序列

如果需要新的INSERT,请拨打 RS

一个。如果 RS 返回-1,则不会重复使用任何内容,因此请调用返回N的 GNS 。如果您可以INSERTmyTable.seqNum=N成功确认,你完成了。如果您无法成功INSERT,请拨打uspAddToReuseList(N)

B中。如果 RS 返回> 0,在你的脑海中注意插槽有reuseMe.reused=1,这是一个值得记住的好事。因此,假设它正处于成功重用的过程中。让我们称之为序列号为N.如果您可以INSERTmyTable.seqNum=N成功进行确认,那么您就完成了。如果您无法成功INSERT,请拨打uspOoopsResetToAvail(N)

当您认为可以安全地致电uspCleanReuseList()时。向DATETIME表添加reuseMe可能是一个好主意,表示来自myTable的行是否正在删除并导致reuseMe行获取其原始{{1} }}