2017-10-10 19 views
0

MySQLの内蔵自動インクリメントは私の要件を満たしていないので、新しいものを作ることを考えています。ここに私の要件は次のとおりです。PHPで自分自身の自動インクリメントを作る方法は?

  1. 欠番に行(レコード)を挿入できる増分シリアル番号
  2. を作成します。たとえば、私は5行をして、3行目を削除します。その後、別の2行を挿入します。私はそれが3行目の位置に挿入されることを期待しています。

私の考えでは、テーブル内のすべての行をチェックするためにループを使用することです。欠落している位置が見つかった場合は、欠けている位置に新しい行が追加されます(a)。それ以外の場合は、テーブルの最後に新しい行を追加し続けます。

そして、あなたが見ることができるように、アイデアは非常に小さなテーブルで動作します。テーブルが10MBのように拡張されているとします。その後、サーバーに大きな問題が発生します。

私は、誰もがより良いアルゴリズムを得た場合、私を啓発してくださいだろうか。

+0

これはひどい考えです。代わりに、**あなたの要件が何であるかを説明し、専門家によって作成された自動インクリメントIDでそれを満たす方法についてのヘルプを求める新しい質問**をお願いします。 「あなた自身」を作成することは、初心者ではありません。 – alexis

答えて

2

が一般的に必要ではなく、より多くのそれは価値があるよりもトラブルです。自動インクリメントは行番号ではありません。連続している必要はなく、ユニークである必要があります。あなたはギャップを埋めるためにしたい場合は、あなたが見つける問題の

一つはrace conditionです。つまり、使用するギャップを見つけるPHPスクリプトとそのギャップに挿入するミリ秒の間に、別のPHPリクエストが同じことをして、同じギャップを見つけて、それを埋めるようになっているかもしれません。

これを解決するには、使用するギャップを検索する前に、の前にPHPスクリプトがテーブル全体をロックする必要があります。ある値を持つ行が存在しないギャップを探しているので、行をロックすることはできません。ギャップがどこにあるかを調べる前に(ギャップが存在する場合でも)わからないので、テーブル全体をロックする必要があります。それは一度に挿入することができる唯一のPHP要求を意味するので、テーブルロックに頼る

は、高価な犠牲です。これは、アプリケーションのスケーラビリティのボトルネックになります。

あなたの実装についてです。見つからない番号を見つける方法数字はデータベースにあるので、前のIDがテーブルにないIDを検索することができます。

SELECT t1.id FROM mytable AS t1 
LEFT OUTER JOIN mytable AS t1 ON t2.id = t1.id - 1 
WHERE t2.id IS NULL 
ORDER BY t1.id 
LIMIT 1 

代わりに、アプリケーション内のすべてのIDのキャッシュを保持することもできます。しかしこれはまた、すべての同時PHP要求がキャッシュへのアクセスを必要とし、一度に1つのPHP要求だけがそれを検索して更新できるようにキャッシュをロックする能力を意味します。

いずれにしても、アプリケーションのボトルネックが発生しています。

私はこのことについて、私の本の第22章、SQL Antipatterns: Avoiding the Pitfalls of Database Programmingとして書きました。

+0

ありがとうございますが、IDが限界に達すると何が起きるかイメージできません。 AIはテーブルを圧倒しますか? –

+1

次に、BIGINTを使用します。 BIGINTのオーバーフローには何千年もかかるでしょう。私の答えはhttps://stackoverflow.com/questions/24007583/integer-overflow-what-will-be-next/24007747#24007747を参照してください –

0

実際には、idカラムの 'ギャップ'を埋めるとデータベースやシステム全体の参照整合性が損なわれる可能性があるため、実際には何をしているのか非常に注意しなければなりません。テーブルは他のテーブルによって参照されます。

そうでない場合は、簡単な方法は、例えば、最初にMySQLを使用して、既存の行で、これらの「空」のIDを埋めることですテーブルは昇順でidカラムにソートされていることを確認した後、次のようなもので、phpmyadminのを使用して:

SET @count = 0; 
UPDATE the_table SET id = @count:= @count + 1; 
#Then after this you do your insert operations. 

上記の順番にすべての既存のIDを更新します、そしてあなたが挿入する項目は次のようになりますテーブルの総数行に等しいidにauto_incrementedするだけです。

しかし、あなたはそのまま、既に空でないIDを残しておきたい場合は、PHPでこのような何かを行うことができます:自動インクリメント列のギャップを埋める

/* Assume that you want to insert into a table called the_table with columns id, col1, col2, col3 a new row with values for the three cols 
$value1, $value2, $value3 respectively, using an existing 'gap' in the id numbering: */ 

/* Get an array of all present ids: */ 
$arr = []; 
$q1 = mysqli_query($con,"SELECT id from the_table"); 
while(list($id) = mysqli_fetch_array($q1)){ 
$arr[] = $id; 
} 

/* Get the currently largest id in the table as $largest_id */ 
$q2 = mysqli_query($con,"SELECT MAX(id) from the_table"); 
list($largest_id) = mysqli_fetch_array($q2); 

/* Loop through all integers up to $largest_id + 1 */ 
/* And do the insert operation just one time, once you find a number not in $arr */ 
/* Use the $not_yet_inserted variable to break out of the loop */ 
$not_yet_inserted = true; 
for($j = 1; $j <= $largest_id + 1; $j += 1){ 
    if(!in_array($j,$arr) && $not_yet_inserted){ 
    mysqli_query($con,"INSERT INTO the_table (id, col1, col2,col3) values ('$j','$value1','$value3,'$value3'')"); 
    $not_yet_inserted = false; 
    } 
} 
0

バイナリ検索の種類のアルゴリズムを使用できます。

まず、データベースに挿入されたすべてのIDを取得します。次に、最大のIDとリストの長さを比較します。両方が同じ場合は、次のIDを持つ行を挿入します。そうでない場合は、リストの半分のIDとの長さ/ 2を比較してください。両方が同じであれば、欠落しているIDはリストの前半の後であり、リストの前半にあることを意味します。あなたが私が何を言おうとしているのか理解できればと思います。