Как получить первый неиспользованный идентификатор в таблице? - PullRequest
6 голосов
/ 25 мая 2009

Мне нужно написать запрос, в котором мне нужно выделить ID (уникальный ключ) для конкретной записи, которая не используется / не генерируется / не существует в базе данных.

Короче говоря, мне нужно сгенерировать id для конкретной записи и показать ее на экране печати. ​​

E. g.:

ID  Name

1   abc
2   def
5   ghi

Итак, дело в том, что он должен возвращать ID=3 в качестве следующего непосредственного объекта, который еще не генерируется, и после этого поколения id я сохраню эти данные обратно в таблицу базы данных.

Это не HW: я делаю проект, и у меня есть требование, где мне нужно написать этот запрос, поэтому мне нужна некоторая помощь для достижения этого.

Поэтому, пожалуйста, сообщите мне, как сделать этот запрос или как этого добиться.

Спасибо.

Я не могу добавлять комментарии, вот почему я пишу свои комментарии здесь .. Я использую MySQL в качестве базы данных ..

Мои шаги будут выглядеть так: -

1) Получить идентификатор из таблицы базы данных, которая не используется ..

2) Как их нет. пользователей (проект на основе веб-сайта), поэтому я не хочу, чтобы происходил параллелизм, поэтому, если один идентификатор генерируется для одного пользователя, он должен блокировать базу данных, пока тот же пользователь не получит идентификатор и не сохранит запись для этого идентификатора. После этого другой пользователь может получить идентификатор, который не существует .. (Основное требование) ..

Как я могу достичь всех этих вещей в MySQL? Кроме того, я полагаю, что ответ Quassnoi будет стоить, но он не работает в MySQL .. поэтому, пожалуйста, объясните немного о запросе, поскольку он новый для меня ... и сделаю этот запрос работает в MySQL ..

Ответы [ 6 ]

6 голосов
/ 25 мая 2009

Я назвал вашу таблицу 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 голосов
/ 25 мая 2009

Зависит от того, что вы подразумеваете под «следующим идентификатором» и как он генерируется.

Если вы используете последовательность или идентификатор в базе данных для генерации идентификатора, возможно, что «следующий идентификатор» будет не 3 или 4, а 6 в представленном вами случае. У вас нет возможности узнать, были ли значения с идентификатором 3 или 4, которые впоследствии были удалены. Последовательности и идентичности не обязательно пытаются исправить пробелы; как только они уйдут, вы не сможете их повторно использовать.

Поэтому правильное решение - создать в вашей базе данных столбец последовательности или идентификатора, который автоматически увеличивается при вводе, а затем ВЫБРАТЬ сгенерированное значение.

1 голос
/ 14 января 2011

Правильный способ - использовать столбец идентификаторов для первичного ключа. Не пытайтесь смотреть на уже вставленные строки и выбирайте неиспользуемое значение. Столбец Id должен содержать достаточно большое число, чтобы в вашем приложении никогда не заканчивались действительные новые (более высокие) значения.

В вашем описании, если вы пропускаете значения, которые пытаетесь использовать позже, вы, вероятно, придаете значениям значения. Пожалуйста, пересмотрите. Возможно, вам следует использовать это поле только как справочное значение из другой таблицы.

Позвольте механизму базы данных назначить следующее более высокое значение для вашего идентификатора. Если у вас одновременно запущено несколько процессов, вам нужно использовать функцию LAST_INSERT_ID (), чтобы определить идентификатор, который база данных сгенерировала для вашей строки. Вы можете использовать функцию LAST_INSERT_ID () в той же транзакции, прежде чем совершить фиксацию.

Вторым лучшим (но не хорошим!) Является использование максимального значения поля индекса плюс один. Вам нужно было бы заблокировать таблицу, чтобы справиться с проблемами параллелизма.

1 голос
/ 14 января 2011
/*
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
0 голосов
/ 28 января 2015

Должно работать под 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
0 голосов
/ 25 мая 2009

Вам разрешено иметь служебную таблицу? если это так, я бы создал таблицу следующим образом:

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

Заполните его всеми положительными 32-разрядными целыми числами (при условии, что идентификатор, который вам нужно сгенерировать, является положительным 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

На самом деле это не проверялось, но оно должно работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...