2009-05-25 10 views
6

Muszę napisać zapytanie, w którym muszę przydzielić ID (klucz unikalny) dla konkretnego rekordu, który nie jest używany/nie jest generowany/nie istnieje w bazie danych.Jak uzyskać pierwszy nieużywany identyfikator w tabeli?

Krótko mówiąc, muszę wygenerować id dla konkretnego rekordu i pokazać go na ekranie drukowania.

Np .:

 
ID Name 

1 abc 
2 def 
5 ghi 

Tak, chodzi o to, że powinien on powrócić ID=3 jako następny natychmiastowe, które nie jest jeszcze generowane, a po tej generacji id będę przechowywać te dane powrót do tabeli bazy danych.

To nie jest HW: robię projekt, i mam wymaganie, gdzie muszę napisać to zapytanie, więc potrzebuję pomocy, aby to osiągnąć.

Proszę, poprowadź mnie, jak zrobić to zapytanie lub jak to osiągnąć.

Dzięki.

nie jestem w stanie dodać komentarz ,, tak to dlaczego piszę moje komentarze tutaj .. używam MySQL jako bazy danych ..

moje kroki byłoby tak: -

1) Pobierz identyfikator z tabeli bazy danych, która nie jest używana.

2) Jak ich nie ma. użytkowników (projekt oparty na stronie internetowej), więc nie chcę, aby wystąpiła współbieżność, więc jeśli jeden identyfikator zostanie wygenerowany dla jednego użytkownika, powinien zablokować bazę danych, dopóki ten sam użytkownik nie otrzyma identyfikatora i nie zapisze rekordu dla tego identyfikatora. Następnie drugi użytkownik może pobrać identyfikator, który nie istnieje. (Wymaganie główne) ..

Jak mogę osiągnąć wszystkie te rzeczy w MySQL? Również przypuszczam, że odpowiedź Quassnoi będzie warta, ale jej nie praca w MySQL ... więc wyjaśnij trochę o zapytaniu, ponieważ jest dla mnie nowością .. i czy to zapytanie działa w MySQL ..

+0

Jakiego RDBMS używasz do swojego projektu? – Quassnoi

+4

Należy zachować ostrożność przy współbieżności, tutaj. Jeśli masz wielu użytkowników, luka czasowa między uruchomieniem kwerendy Quassnoi i przechowywaniem wyników w DB może spowodować duplikowanie identyfikatorów. Dlaczego po prostu niech RDBMS nie zarządza kolumnami ID? –

+2

Jak pisze DDaviesBrackett, jeśli to nie jest praca domowa, to cierpi na poważny problem z rzeczywistego świata: dwa procesy mogą uruchomić zapytanie i uzyskać odpowiedź, a następnie każda z nich próbuje wstawić duplikat rekordu. Jeśli jest to po prostu odpowiedź na pytanie: czy są jakieś luki? to jest inne. To zabawne, że ktokolwiek by się tym przejmował. – Yishai

Odpowiedz

6

Nazwałem Twój stół 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 

To zapytanie składa się z dwóch części.

Pierwsza część:

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

wybiera 1 to nie ma wpisu w tabeli z tym id.

Druga część:

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 

wybiera pierwszy id w tabeli, dla których nie ma następnego id.

Wynikowe zapytanie wybiera najmniejszą z tych dwóch wartości.

+1

nie znajdzie identyfikatorów ids mniejszych niż pierwszy istniejący identyfikator. To znaczy. jeśli tabela ma identyfikatory 3,4,6, znajdzie 5, ale nie 1 i 2.Możesz łączyć się z innym wyborem, który wyszukuje id większy niż 0 i mniejszy niż pierwszy identyfikator. –

+0

@Remus: ładny punkt, dodawanie, dzięki. – Quassnoi

+0

Moje kroki byłyby następujące: - 1) Pobierz identyfikator z tabeli bazy danych, która nie jest używana. 2) Jak ich nie ma. użytkowników (projekt oparty na stronie internetowej), więc nie chcę, aby wystąpiła współbieżność, więc jeśli jeden identyfikator zostanie wygenerowany dla jednego użytkownika, powinien zablokować bazę danych, dopóki ten sam użytkownik nie otrzyma identyfikatora i nie zapisze rekordu dla tego identyfikatora. Następnie inny użytkownik może pobrać identyfikator, który nie istnieje .. (Wymaganie główne) .. Jak mogę osiągnąć wszystkie te rzeczy w MySQL – AGeek

5

Zależy od tego, co masz na myśli przez "następny identyfikator" i jak jest generowany.

Jeśli używasz sekwencji lub tożsamości w bazie danych do wygenerowania identyfikatora, możliwe, że "następny identyfikator" nie jest równy 3 lub 4, ale 6 w przypadku, który przedstawiłeś. Nie masz możliwości sprawdzenia, czy istnieją wartości o identyfikatorze 3 lub 4, które zostały później usunięte. Sekwencje i tożsamości niekoniecznie starają się odzyskać luki; kiedy ich nie ma, nie wykorzystuj ich ponownie.

Dobrze jest utworzyć kolumnę sekwencji lub tożsamości w bazie danych, która jest automatycznie zwiększana podczas wykonywania INSERT, a następnie WYBRAĆ wygenerowaną wartość.

+0

To zrobi, ale ponieważ istnieją różni użytkownicy, którzy będą mieli dostęp do bazy danych, może się zdarzyć, że dwóch użytkowników otrzyma ten sam identyfikator, więc jak można uniknąć tej współbieżności, plz również podać przykład również .. Thanx .. – AGeek

+3

Jeśli używasz pola auto_increment w mysql, nie musisz martwić się o współbieżność. Po prostu spraw, aby użytkownik używał LAST_INSERT_ID() po, aby uzyskać identyfikator właśnie wstawionego wiersza. –

0

Czy możesz mieć tabelę narzędziową? Jeśli tak by utworzyć tabelę tak:

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

Wypełnij go ze wszystkimi pozytywnymi 32 bitowych liczb całkowitych (zakładając id trzeba wygenerować jest pozytywna 32 bitowa liczba całkowita)

Następnie można wybrać jak tak :

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 

Nie testowałem tego, ale powinno działać.

+0

oczywiście będzie to ssać wydajność, ale jest to jedyny stosunkowo łatwy sposób (mogę myśleć w tej chwili) o spełnieniu specyfikacji określonych w pytaniu, w przeciwieństwie do wyjaśnienia kolumn tożsamości. – Kris

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

Poprawnym sposobem jest użycie kolumny tożsamości dla klucza podstawowego. Nie próbuj patrzeć na już wstawione wiersze i wybierz nieużywaną wartość. Kolumna Id powinna zawierać numer wystarczająco duży, aby aplikacja nigdy nie zabrakło prawidłowych nowych (wyższych) wartości.

W swoim opisie, jeśli pomijasz wartości, których próbujesz użyć później, prawdopodobnie nadajesz jakieś znaczenie wartościom. Proszę rozważyć ponownie. Prawdopodobnie powinieneś używać tego pola tylko jako wartości wzorca (odniesienia) z innej tabeli.

Pozwól silnikowi bazy danych przypisać następną wyższą wartość dla twojego ID. Jeśli masz więcej niż jeden proces działający jednocześnie, musisz użyć funkcji LAST_INSERT_ID(), aby określić identyfikator wygenerowany przez bazę danych dla twojego wiersza. Możesz użyć funkcji LAST_INSERT_ID() w ramach tej samej transakcji przed zatwierdzeniem.

Drugą najlepszą (ale nie dobrą!) Jest użycie maksymalnej wartości pola indeksu plus jeden. Trzeba by było zrobić blokadę stołu, aby zarządzać kwestiami współbieżności.

+0

To prawda, i chociaż pytanie, które zadał, może nie być dla niego właściwym pytaniem, w rzeczywistości jest to przydatne pytanie, aby uzyskać odpowiedź dla niektórych z nas (np. Przydzielić niewykorzystany zasób z ograniczonej puli zasobów, zamiast przydzielania unikalnego numeru z nieskrępowana pula, jak ma to miejsce w przypadku kluczy podstawowych). – ijw

0

Powinien działać w 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 
Powiązane problemy