45

SQL Server 列で使用されていない最小の数値を見つけるにはどうすればよいですか?

手動で記録された大量のレコードを Excel から SQL Server テーブルにインポートしようとしています。それらはすべて数値 ID (文書番号と呼ばれる) を持っていますが、もはや適用されない理由により、順番に割り当てられていませんでした。つまり、今後、私の Web サイトが新しいレコードを記録するとき、可能な限り小さい文書番号を割り当てる必要があります (ゼロより大きい) まだ取得されていません。

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

ありがとう!

編集

並行性の問題を提起してくれたWWに特に感謝します。これが Web アプリであることを考えると、定義上マルチスレッドであり、同じ問題に直面した場合は、競合を防ぐためにコードまたは DB レベルのロックを検討する必要があります。

リンク

参考までに-これは、次のコードを使用して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

4

15 に答える 15

64

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
于 2009-03-26T01:14:33.633 に答える
12

数値 ID で並べ替えると、探している番号は、ROW_NUMBER() 関数が ID と等しくない最初の番号になります。

于 2009-03-26T01:13:13.557 に答える
12

これまでのどの回答にも、ロックや同時実行性についての言及はありません。

次の 2 人のユーザーがほぼ同時にドキュメントを追加しているとします。

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

次のいずれかを行う必要があります: a) そのエラーを処理し、次の利用可能な ID を探すために再びループを回る、または b) プロセスの開始時にロックアウトして、特定の時間に 1 人のユーザーだけが ID を探すようにする

于 2009-03-26T01:20:52.057 に答える
10
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;

これは、@Jeffrey Hantlin と @Darrel Miller によって与えられた相関サブクエリを使用した回答の代替です。

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

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

于 2009-03-26T01:15:52.303 に答える
5
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

一番上の回答のようなハッシュ一致と結合を2回スキャンするのではなく、1回テーブルスキャンしますか

于 2009-08-27T10:39:49.660 に答える
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 を見つけ、その後継者を返します。ギャップがない場合は、現存する最大の ID より 1 大きい値を返します。1 で始まる ID が確実に考慮されるように、0 のプレースホルダー ID が挿入されます。

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

Microsoft SQL ではステートメントでfrom句を使用できるinsertため、手続き型コードに頼る必要がない場合があります。

于 2009-03-26T01:12:02.890 に答える
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% 正確ではないかもしれませんが、そこから取得できるはずです)

于 2009-03-26T00:56:53.160 に答える
1

列をIDENTITYに変換してみてください。BACKUP は最初に ROW_NUMBER を使用してドキュメント ID を更新し、1 から開始してドキュメント数まで増やします。数値列が他のテーブル (外部キー) で参照として使用されている場合、SQL Server は外部キーを更新しようとし、競合のために失敗する可能性があるため、一度に WHILE で実行する必要があります。最後に、列の ID 仕様を有効にするだけです。

:) 今はもっと手間がかかりますが、後で多くの手間を省くことができます。

于 2009-03-26T01:55:20.227 に答える