我需要一个表中行的序列号序列,我需要确保它始终是顺序的,插入时没有间隙,当删除时我可以留下行间隙,但是在插入时我必须填补空白新行。原因是不同的系统必须与行记录一对一排列。然而,db可以在sql端和应用程序中被其他人操纵;我认为触发器将允许我完成更改的部分 - 但是如何确定我是否有间隙并执行此序列号的插入 - 即使我必须在单独的表中维护已删除的序列并且管理很好 - 无论桌子如何被操纵,我都需要与其他系统一对一排列。
自动增量字段将不起作用,因为行被删除,下一个插入将是最后一个自动增量值。我需要插入...或者可能保留行并添加一个字段IsDeleted并强制将表作为只读或不再插入/删除..但是如何做到这一点? 也许当插入行时,如果找到则可以在间隙处设置序列号,否则设置在最后。
有人有经验做这种事吗?
答案 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()
如果需要新的INSERT
,请拨打 RS :
一个。如果 RS 返回-1,则不会重复使用任何内容,因此请调用返回N的 GNS 。如果您可以INSERT
与myTable.seqNum=N
成功确认,你完成了。如果您无法成功INSERT
,请拨打uspAddToReuseList(N)
。
B中。如果 RS 返回> 0,在你的脑海中注意插槽有reuseMe.reused=1
,这是一个值得记住的好事。因此,假设它正处于成功重用的过程中。让我们称之为序列号为N.如果您可以INSERT
与myTable.seqNum=N
成功进行确认,那么您就完成了。如果您无法成功INSERT
,请拨打uspOoopsResetToAvail(N)
。
当您认为可以安全地致电uspCleanReuseList()
时。向DATETIME
表添加reuseMe
可能是一个好主意,表示来自myTable
的行是否正在删除并导致reuseMe
行获取其原始{{1} }}