如何在表格中获得第一个未使用的ID?

时间:2009-05-25 16:16:09

标签: sql mysql

我必须编写一个查询,其中我需要为未使用/未生成/在数据库中不存在的特定记录分配ID(唯一键)。

简而言之,我需要为特定记录生成id并在打印屏幕上显示。

电子。 G:

ID  Name

1   abc
2   def
5   ghi

所以,问题是它应该返回ID=3作为尚未生成的下一个立即数,并且在这一代id之后,我将把这些数据存储回数据库表。

这不是一个硬件:我正在做一个项目,我有一个要求,我需要编写这个查询,所以我需要一些帮助来实现这一点。

因此,请指导我如何进行此查询,或如何实现此目标。

感谢。

我无法添加评论,所以这就是我在这里写评论的原因.. 我使用MySQL作为数据库..

我的步骤是这样的: -

1)从数据库表中检索未使用的id ..

2)因为他们不是。用户(基于网站的项目),所以我不想发生并发,所以如果一个用户生成一个ID,那么它应该锁定数据库,直到同一个用户收到id并存储该id的记录。之后,其他用户可以检索不存在的ID。(主要要求)..

我怎样才能在MySQL中实现所有这些东西,而且我认为Quassnoi的答案是值得的,但它不适用于MySQL ..所以plz解释了有关查询的一点,因为它对我来说是新的..并且将会这个查询在MySQL中工作..

6 个答案:

答案 0 :(得分:6)

我将您的表命名为unused

SELECT  id
FROM    (
        SELECT  1 AS id
        ) q1
WHERE   NOT EXISTS
        (
        SELECT  1
        FROM    unused
        WHERE   id = 1
        )
UNION ALL
SELECT  *
FROM    (
        SELECT  id + 1
        FROM    unused t
        WHERE   NOT EXISTS
                (
                SELECT  1
                FROM    unused ti
                WHERE   ti.id = t.id + 1
                )
        ORDER BY
                id
        LIMIT 1
        ) q2
ORDER BY
        id
LIMIT 1

此查询由两部分组成。

第一部分:

SELECT  *
FROM    (
        SELECT  1 AS id
        ) q
WHERE   NOT EXISTS
        (
        SELECT  1
        FROM    unused
        WHERE   id = 1
        )

选择1表格中没有此id条目。

第二部分:

SELECT  *
FROM    (
        SELECT  id + 1
        FROM    unused t
        WHERE   NOT EXISTS
                (
                SELECT  1
                FROM    unused ti
                WHERE   ti.id = t.id + 1
                )
        ORDER BY
                id
        LIMIT 1
        ) q2

选择表格中没有下一个id的第一个id

生成的查询选择这两个值中的最小值。

答案 1 :(得分:5)

取决于您对“下一个ID”的含义以及它是如何生成的。

如果您在数据库中使用序列或标识来生成id,那么“next id”可能不是3或4,而是在您呈现的情况下为6。您无法知道是否存在随后删除的id为3或4的值。序列和身份不一定试图回收差距;一旦他们离开,你就不要重复使用它们。

因此,正确的做法是在数据库中创建一个序列或标识列,当您执行INSERT时会自动递增,然后选择生成的值。

答案 2 :(得分:1)

/*
This is a query script I wrote to illustrate my method, and it was created to solve a Real World problem where we have multiple machines at multiple stores creating transfer transactions in their own databases,
that are then synced to other databases on the store (this happens often, so getting the Nth free entry for the Nth machine should work) where the transferid is the PK and then those are synced daily to a MainFrame where the maximum size of the key (which is the TransactionID and StoreID) is limited.  
*/

--- table variable declarations
/* list of used transaction ids (this is just for testing, it will be the view or table you are reading the transaction ids from when implemented)*/

DECLARE @SampleTransferIDSourceTable TABLE(TransferID INT)     

/* Here we insert the used transaction numbers*/

DECLARE @WorkTable TABLE (WorkTableID INT IDENTITY (1,1), TransferID INT) 

/*this is the same table as above with an extra column to help us identify the blocks of unused row numbers (modifying a table variable is not a good idea)*/

DECLARE @WorkTable2 TABLE (WorkTableID INT , TransferID INT, diff int)  

--- Machine ID declared

DECLARE @MachineID INT

-- MachineID set

SET @MachineID = 5

-- put in some rows with different sized blocks of missing rows.
-- comment out the inserts after two to the bottom to see how it handles no gaps or make 
-- the @MachineID very large to do the same.
-- comment out early rows to test how it handles starting gaps.

INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 1 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 2 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 4 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 5 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 6 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 9 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 10 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 20 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 21 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 24 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 25 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 30 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 31 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 33 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 39 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 40 )
INSERT @SampleTransferIDSourceTable ( TransferID ) VALUES ( 50 )

-- copy the transaction ids into a table with an identiy item.
-- When implemented add where clause before the order by to limit to the local StoreID
-- Zero row added so that it will find gaps before the lowest used row.

INSERT @WorkTable (TransferID)

SELECT 0

INSERT @WorkTable (TransferID)

SELECT TransferID FROM @SampleTransferIDSourceTable ORDER BY TransferID   

-- copy that table to the new table with the diff column 

INSERT @WorkTable2

SELECT WorkTableID,TransferID,TransferID - WorkTableID 

  FROM @WorkTable              

--- gives us the (MachineID)th unused ID or the (MachineID)th id beyond the highest id used.

IF EXISTS (

SELECT Top 1

       GapStart.TransferID  + @MachineID - (GapStart.diff + 1)

  FROM @WorkTable2 GapStart

 INNER JOIN @WorkTable2 GapEnd

    ON GapStart.WorkTableID = GapEnd.WorkTableID - 1

   AND GapStart.diff < GapEnd.diff

   AND gapEnd.diff >= (@MachineID - 1)

 ORDER BY GapStart.TransferID

 )

SELECT Top 1

       GapStart.TransferID  + @MachineID - (GapStart.diff + 1)

  FROM @WorkTable2 GapStart

 INNER JOIN @WorkTable2 GapEnd

    ON GapStart.WorkTableID = GapEnd.WorkTableID - 1

   AND GapStart.diff < GapEnd.diff

   AND gapEnd.diff >= (@MachineID - 1)

 ORDER BY GapStart.TransferID

ELSE 

SELECT MAX(TransferID) + @MachineID FROM @SampleTransferIDSourceTable

答案 3 :(得分:1)

正确的方法是使用主键的标识列。不要试图查看已插入的行,并选择一个未使用的值。 Id列应该包含一个足够大的数字,以使您的应用程序永远不会耗尽有效的新(更高)值。

在您的描述中,如果您要跳过以后尝试使用的值,那么您可能会给这些值赋予一些含义。请重新考虑。您可能只应将此字段用作另一个表中的查找(引用)值。

让数据库引擎为您的ID分配下一个更高的值。如果同时运行多个进程,则需要使用LAST_INSERT_ID()函数来确定数据库为您的行生成的ID。在提交之前,您可以在同一事务中使用LAST_INSERT_ID()函数。

第二好(但不好!)是使用索引字段的最大值加1。您必须执行表锁来管理并发问题。

答案 4 :(得分:0)

你可以准备一张实用工具桌吗?如果是这样我会创建一个像这样的表:

CREATE TABLE number_helper (
    n INT NOT NULL
   ,PRIMARY KEY(n)
);

用所有正32位整数填充(假设你需要生成的id为正32位整数)

然后您可以这样选择:

SELECT MIN(h.n) as nextID
FROM my_table t
LEFT JOIN number_helper h ON h.n = t.ID
WHERE t.ID IS NULL

实际上没有对此进行测试,但它应该有效。

答案 5 :(得分:0)

应该在MySql下工作。

SELECT TOP 100
    T1.ID + 1 AS FREE_ID 
FROM TABLE1 T1
LEFT JOIN TABLE2 T2 ON T2.ID = T1.ID + 1
WHERE T2.ID IS NULL