2009-03-26 9 views
34

SQL Serverの列で使用されていない最小の番号はどうやって見つかりますか?SQL Serverで使用されていない最小の番号の検索

手動で記録された多数のレコードをExcelからSQL Serverテーブルにインポートしようとしています。彼らはすべて数字のID(文書番号と呼ばれます)を持っていますが、私のWebサイトが新しいレコードを記録した時点から、適用可能な最小の文書番号を割り当てる必要がありますゼロより大きい)である。

これをプレーンSQLで行う方法はありますか、これはTSQL /コードの問題ですか?

ありがとうございます!同時実行の問題を惹起するためのWW

EDIT

感謝します。これはウェブアプリケーションであることから、定義上マルチスレッドであり、この同じ問題に直面した人は、競合を防ぐためにコードレベルまたはDBレベルのロックを考慮する必要があります。 FYI

LINQ

- これは、次のコードでLINQを介して達成することができる。

var nums = new [] { 1,2,3,4,6,7,9,10}; 

int nextNewNum = (
    from n in nums 
    where !nums.Select(nu => nu).Contains(n + 1) 
    orderby n 
    select n + 1 
).First(); 

nextNewNumの== 5

答えて

48

編集ID + 1

SELECT TOP 1 t1.Id+1 
FROM table t1 
WHERE NOT EXISTS(SELECT * FROM table t2 WHERE t2.Id = t1.Id + 1) 
ORDER BY t1.Id 

持つ行が存在しない最初の行を検索:

最低の既存のIDはここで、1ない特殊なケースを扱うためには、醜いです溶液:

SELECT TOP 1 * FROM (
    SELECT t1.Id+1 AS Id 
    FROM table t1 
    WHERE NOT EXISTS(SELECT * FROM table t2 WHERE t2.Id = t1.Id + 1) 
    UNION 
    SELECT 1 AS Id 
    WHERE NOT EXISTS (SELECT * FROM table t3 WHERE t3.Id = 1)) ot 
ORDER BY 1 
+0

これは、範囲の先頭から始まるすべての連続したIDブロックを欠落します。たとえば、 'table'にids(5,6,8,9,10)がある場合、これは1から4のいずれかではなく7を返します。 – joshperry

+0

@joshperryそうです。私は0より大きい全てのIDを満たすことを望んでいるというコメントを見逃しました。私は醜い修正を加えました。多分誰かが改善を提案するでしょう。 –

+0

+1とても助かりました、ありがとう!他の答えのほとんどは、 "あなたはそれをする必要はありません、システムがキーをインクリメントさせる"ですが、私の場合はプライマリキーではなく、空のスロットが問題になる別のユニークな数値フィールドです削除の結果としてではなく、発生します。 –

2

は、それが最小でなければならない理由があります可能な数?なぜあなたは穴を埋める必要がありますか?

を編集すると、それはビジネスルールなので、答えをアドバタイズしてください。

DECLARE @counter int 
DECLARE @max 
SET @counter = 0 
SET @max = SELECT MAX(Id) FROM YourTable 
WHILE @counter <= @max 
BEGIN 
    SET @counter = @counter + 1 
    IF NOT EXISTS (SELECT Id FROM YourTable WHERE Id = @counter) 
     BREAK 
    END 
END 

(私は便利デシベルを持っていないので、これは100%正確ではないかもしれないが、あなたはそこからそれを得ることができる必要があります)

+0

それは、ビジネスルールです。これらの文書番号は、ユーザに渡され、実際に使用されます。私は同じ質問をしましたが、彼らはこの問題にしっかり立っています。 :) –

+0

これは残念です... 私が知っている唯一の方法は、未使用のIDが見つかるまですべてをループすることです。あなたの運が大変申し訳ありません。 –

3

ギャップが、あなたは、順番に存在する場合言い換えれば

select top 1 (found.id + 1) nextid from (select id from items union select 0) found 
    where not exists (select * from items blocking 
          where blocking.id = found.id + 1) 
    order by nextid asc 

、その後継者が存在し、その後継者を返さない少なくともIDを検索する:このような何かを持つ第1のギャップを見つけることができます。ギャップがない場合は、現存する最大のIDよりも大きいIDを返します。 1から始まるIDが考慮されることを保証するために、プレースホルダID 0が挿入されます。

これは少なくともn log n時間かかることに注意してください。

insertステートメントにfrom句を使用できるため、プロシージャコードを使用する必要はありません。

11

数値IDでソートすると、探している番号がROW_NUMBER()関数がIDと等しくない最初の番号になります。

+0

+1優れたSQL Server固有のトリックです。これはsubselectで最初の不一致を取り除き、max(id)+1でunionを実行して1回実行することができますか? – bobince

9
SELECT TOP 1 t1.id+1 
FROM mytable t1 
LEFT OUTER JOIN mytable t2 ON (t1.id + 1 = t2.id) 
WHERE t2.id IS NULL 
ORDER BY t1.id; 

これは@Jeffrによって与えられた相関サブクエリを使用して回答に代わるものですアイハントリンと@ダレルミラー。

しかし、あなたが記述しているポリシーは実際には良い考えではありません。 ID値は一意である必要がありますが、連続している必要はありません。

文書#42へのリンクがあるユーザーに電子メールを送信した後、文書を削除するとどうなりますか?後で、ID#42を新しい文書として再利用します。今すぐメールの受信者は、の間違ったドキュメントへのリンクに従います。

+0

私はこれが見つからないと認めます欠けている1の値です。しかし、それは私の本当のポイントである偽の問題なので、私は解決策を思いつくことに興味がありません! :-P –

+0

文書番号は決して削除されません。私はあなたに同意しますが、これはドキュメントを識別する貧弱な方法です。私は私の戦いを選んでいます、そして揚げるために大きな魚があります。 –

12

これまでの回答では、ロックや並行性に関する記述はありません。

は、これらの2人のユーザーがほぼ同時に文書を追加することを検討して: -

User 1    User 2 
Find Id    
         Find Id 
Id = 42    
         Id = 42 
Insert (42..) 
         Insert (42..) 
         Error! 

あなたがする必要があり、次のいずれかa)はそのエラーを処理し、次の使用可能なIdを探し、再びループを一周 、OR b)プロセスの開始時にロックアウトして、特定の時間に1人のユーザーしかIDを探しません。

1

本当に列をIDENTITYに変換してください。 最初にBACKUPを実行し、ROW_NUMBERを使用して文書IDを更新します。これにより、文書番号が1から最大数になるように開始されます。 数値列が他のテーブル(外部キー)で参照として使用されていると、SQL Serverは外部キーを更新しようとし、競合のために失敗する可能性があるため、その時点でWHILEで実行する必要があります。 最後に列のID指定を有効にしてください。

:)これ以上の作業ですが、後で多くのトラブルを回避できます。

2
select 
    MIN(NextID) NextUsableID 
from (
    select (case when c1 = c2 then 0 
      else c1 end) NextID 
    from ( select ROW_NUMBER() over (order by record_id) c1, 
        record_id c2 
      from myTable) 
) 
where NextID > 0 
3
declare @value int 

select @value = case 
        when @value is null or @value + 1 = idcolumn 
        then idcolumn 
        else @value end 
    from table 
    order by idcolumn 

select @value + 1 

1つのスキャンテーブルではなく、2ハッシュ一致をスキャンし、ここでトップの答え

+1

非常に速く、トップ回答+1! –

2

のように参加するには、単純なアプローチですありません。それは速くないかもしれません。最初に見つからない数字は見つからないでしょう。

SELECT MIN(MT1.MyInt+1) 
FROM MyTable MT1 
LEFT OUTER JOIN MyTable MT2 ON (MT1.MyInt+1)=MT2.MyInt 
WHERE MT2.MyInt Is Null 
1

私はこの答えは遅れている知っていますが、再帰テーブル式を使用することにより、最小の未使用の番号を見つけることができます。

CREATE TABLE Test 
(
    ID int NOT NULL 
) 

--Insert values here 

;WITH CTE AS 
(
    --This is called once to get the minimum and maximum values 
    SELECT nMin = 1, MAX(ID) + 1 as 'nMax' 
    FROM Test 
    UNION ALL 
    --This is called multiple times until the condition is met 
    SELECT nMin + 1, nMax 
    FROM CTE 
    WHERE nMin < nMax 
) 

--Retrieves all the missing values in the table. Removing TOP 1 will 
--list all the unused numbers up to Max + 1 
SELECT TOP 1 nMin 
FROM CTE 
WHERE NOT EXISTS 
(
    SELECT ID 
    FROM Test 
    WHERE nMin = ID 
) 
1

はのは、あなたのIDを仮定しますが、常に1で開始する必要があります:

SELECT MIN(a.id) + 1 AS firstfree 
FROM (SELECT id FROM table UNION SELECT 0) a 
LEFT JOIN table b ON b.id = a.id + 1 
WHERE b.id IS NULL 

これは私が考えることができるすべてのケースを扱います - 既存のレコードはまったく含まれていません。

私は、このソリューションについては好きではない唯一のものは、追加の条件がそのように、二回含まなければならないことである。

SELECT MIN(a.id) + 1 AS firstfree 
FROM (SELECT id FROM table WHERE column = 4711 UNION SELECT 0) a 
LEFT JOIN table b ON b.column = 4711 AND b.id = a.id + 1 
WHERE b.id IS NULL 

もロックと同時実行性についてのコメントに注意してください - ギャップを埋めるための要件をほとんどの場合、設計が不良であり、問​​題を引き起こす可能性があります。しかし、Iには、IDが印刷され、人間によって入力され、しばらくしてから多くの桁のIDを持つことは望ましくありませんが、低いものはすべて無料です。

0

私は同様の問題に直面し、これを思い付いた:

Select Top 1 IdGapCheck 
From (Select Id, ROW_NUMBER() Over (Order By Id Asc) AS IdGapCheck 
    From dbo.table) F 
Where Id > IdGapCheck 
Order By Id Asc 
関連する問題