2009-05-25 30 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 
相關問題