2009-05-25 76 views
6

我必须写一个查询,其中我需要为特定记录分配一个ID(唯一键),这个记录没有被使用/没有被生成/在数据库中不存在。如何获得表中第一个未使用的ID?

总之,我需要为特定记录生成一个id并在打印屏幕上显示它。

E.摹:

 
ID Name 

1 abc 
2 def 
5 ghi 

那么,事情是,它应该返回ID=3作为未还产生下一个即时的,而这一代id后,我会存储这些数据回到数据库表。

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

所以请指导我如何使这个查询,或如何实现这一点。

谢谢。

我不能够添加注释,,所以这就是为什么我在这里写我的意见.. 我使用MySQL作为数据库..

我的步骤是这样的: -

1)从数据库表中检索未被使用的ID。

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

我如何在MySQL中完成所有这些事情,我想也是Quassnoi的答案值得,但它不是在MySQL中工作..所以PLZ解释一下关于查询,因为它是我的新...并将这个查询工作在MySQL ..

+0

您在项目中使用了哪些关系数据库? – Quassnoi 2009-05-25 16:23:51

+4

这里要小心并发性。如果您有多个用户,运行Quassnoi查询和将结果存储在数据库中的时间间隔可能会导致重复的ID。为什么不让RDBMS管理你的ID列? – 2009-05-25 16:43:09

+2

正如DDaviesBrackett写道,如果这不是家庭作业,那么它会遭受严重的现实世界问题:两个进程可能会运行查询,并获得他们的答案,然后每个尝试插入重复记录。如果这只是为了回答这个问题:有没有差距?那是不同的。那么任何人都会在乎的就很有趣。 – Yishai 2009-05-25 16:54:45

回答

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

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

5

取决于“next id”的含义及其生成方式。

如果您在数据库中使用序列或标识来生成标识,那么在您提交的情况下,“下一个标识”可能不是3或4,而是6。您无法知道是否存在随后被删除的ID为3或4的值。序列和身份不一定试图填补空白;一旦他们走了,你不会重复使用它们。

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

0

是否允许您拥有实用工具表?如果是的话我会创造像这样的表:

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

与所有正的32个整数填充它(假设你需要生成是一个正的32位整数ID)

然后你就可以像这样选择:

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 

还没有实际测试过这个,但它应该工作。

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 
1

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

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

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

第二好(但不好!)是使用索引字段的最大值加1。你将不得不做一个表锁来管理并发问题。

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 
相关问题