3

私が求めているものと同様のトピックをいくつか読みましたが、どれも私にとってあまり役に立たないようです。

Unique 制約を持つ列に格納されるコードをユーザーが生成できるフォームがあります。コードは 7 文字の長さの文字列です。ユーザーが数字を入力すると、プログラムはその数のコードを生成し、コードの最大数に達するまでこれを繰り返すことができます。

私の問題は、値が重複していることです。しかし、新しいエントリを入力する瞬間にデータベースにすでに存在する値ではありません(私はそれらを正常にチェックします)が、コードの新しいグループ(たとえば10000)のエントリの一部は(おそらく)同一です。したがって、私のコードは同じトランザクションで 2 つ (またはそれ以上) の同一のコードを生成し、DB の Unique 制約がそれについて文句を言います。

エントリごとにデータベースをチェックすることを考えましたが、10000 またはそれ以上のエントリについて話していることを考えると、非常に時間がかかります。

したがって、唯一のオプションは、最初にそれらを生成するコードを変更することだと思います。これは、非効率的で double を生成するように思われるためです。

問題の大部分は必要なコードの長さです。それ以外の場合は、純粋な 'uniqid()' または同様のものを使用しますが、7 文字に制限する必要があるため、さらに悪化すると思います。また、コードの一部の文字 ['problem_characters'] をコードから除外する必要があります。

これがコードです。一意の値のみを生成するように適切に変更できませんでした。

$problem_characters = array("0", "o", "O", "I", "1", 1);

$code = md5(uniqid(rand(), true));

$extId = strtoupper(str_replace($problem_characters,rand(2,9),substr($code, 0, 7)));

//insert $extId in the database

@Geo OK、私はあなたの解決策を試してみましたが、それは(もちろん)うまくいきましたが、その後、新しい問題が発生しました - あなたの「if」の「else」の部分で、

$extId = strtoupper(str_replace($problem_characters,rand(2,9),substr($code, 0, 7)));

while(true){     

      if((!in_array($extId, $allExternalIdsHandled)) && (!in_array($extId, $newEnteredValues))){
       break;
        }else{
 $extId = strtoupper(str_replace($problem_characters,rand(2,9),substr($code, 0, 7)));   }
               }
//insert the modified value in the DB here

そのため、今は無限ループに入り、「random」呼び出しの実行で変更してから if に入って抜け出す必要がありますが、「break」コマンドで抜け出していません...

ここに問題はありません。誰かが私に指示を与えることができますか?

編集:ハングすることもあれば、ハングしないこともあります。10000 個の値を入力したところ、「else」パスを介して 2 つのエントリが変更されました。ログを使用してこれを観察しました。

4

3 に答える 3

3

すでにハードワークを行っているライブラリがあり、文字列と文字列の長さを生成するときに使用する「アルファベット」を選択できます。

「同一エントリ」の問題は衝突と呼ばれ、回避することはできません。

編集だから、Geoによって提案されたものと同様に、私はを使用して一意のエントリPHPのリストを作成しています。n違いは、SQL挿入が失敗する可能性があることです。そのため、必要な総数を確実に満たすために、2層の反復があります。

<?php

require('hashids.php'); // I'm using the library I suggested

$hashids = new hashids('some salt', 7); // use the default alphabet, feel free to pass the 3rd parameter with the alphabet you want to use

$generationTries = 0;

$hashesInDBCount = 0; // get from your database
$desiredHashesCount = 50; // use a parameter
$totalDesiredHashes = $hashesInDBCount + $desiredHashesCount;
do
{
    // when coming back in the loop, only generate what's still required
    $desiredHashesCount = $totalDesiredHashes - $hashesInDBCount; 
    $generatedHashesCount = 0;
    $generatedHashes = array();

    while($generatedHashesCount < $desiredHashesCount)
    {
        $hash = $hashids->encrypt($generationTries++);
        if(!in_array($hash, $generatedHashes))
        {
            array_push($generatedHashes, $hash);
            ++$generatedHashesCount;
        }
    }

    // insert $generatedHashes in your Database

    $hashesInDBCount = 50; // again, query your database as you might come through this loop more than once, 
                           // I'm hardcoding the value to have a working example
}
while($hashesInDBCount < $totalDesiredHashes);

echo "Generated " . count($generatedHashes) . " hashes in " . $generationTries . " tries\n";
var_dump($generatedHashes);

これは私に次のような出力を与えます:

Generated 50 hashes in 50 tries
array(50) {
  [0]=>
  string(7) "eAcgAcx"
  [1]=>
  string(7) "Exidai8"
  [2]=>
  string(7) "ExTbqT8"
  [3]=>
  string(7) "4Acz8cB"
  [4]=>
  string(7) "LRipxir"
  [5]=>
  string(7) "zATe5Tx"
  ...
}

ランダムソルトを追加すると、毎回ランダムな値が得られます

于 2012-12-26T15:51:46.823 に答える
1

最初に-文字列コードを生成するためにmd5を使用しています。md5は16進エンコードされた文字列であるため、可能な組み合わせの数を大幅に減らしています。30文字のランダム文字列を生成すると、21ビリオン(10 ^ 9)の可能性が得られます。 16進文字で2億6800万(10 ^ 6)

さらに、本当に一意の値を作成することはできません(GUIDはマシン固有です)。同じ値を2回生成する確率は、文字列が短いほど高くなります。

私は3つの異なるアプローチ(少なくとも30の問題のない文字があると仮定します)を使用して、一意の非ランダム値を作成できます。2つのカウンター生成要求カウントと要求カウンターがあるとしましょう。したがって、ユーザー1が100個のコードを要求した場合、user_request_counter-code_counter:'00 -00-00_00-00-00-01'から'00-00-01_00-00-03-00'のようなコードは必ず一意になります(実際には7-30までの2桁の各グループは1文字で表すことができます(16進数が16文字で行うのと同じように-好きなベースを選択できます)これにより、30 ^ 4(810,000)コードを30^まで作成できます3(27,000)ユーザー。これにより、高価なランダムコールを使用したり、コードの重複を心配したりする必要がなくなります。

DBにランダムなコードを入力してユーザーに割り当てるために一度使用した2番目のアプローチは、たまにしか実行する必要がなく、新しいコードの生成をオフラインで実行できるため便利です。 (dbのダンプを使用して)次にサーバーにプッシュすると、テーブルを更新する必要がないため、phpコードでのコード生成でo(1)、dbサーバー側でo(1)という素晴らしいパフォーマンスが得られます。 phpで生成されたコードをデータベースに挿入するときと同じように、何千回もインデックスを作成します。

唯一の問題がphpで生成された値に重複がある場合の、3番目のアプローチは、それらを配列に入れてから、値が新しいかどうかを確認することです。PHP配列はハッシュテーブルとして実装されているため、かなり良いパフォーマンスが得られます)。

PHPでコードをランダムに生成することを選択した場合、常に2つの問題に直面する必要があります。1つ目はコードがデータベースに存在しないという保証がないため、常にダブルキーの問題を処理する必要があります。多くのコードを生成する必要があるため、それらをdbに挿入すると、SQLサーバー側でかなりコストがかかり、コードが増えると、スクリプトが大幅に遅くなります。

于 2012-12-26T16:23:11.853 に答える
1
<?php

$problem_characters = array('0', 'o', 'O', 'I', '1', 1);
$length = 10000;
$i = 0;
$hashes = array();
while ($i < $length) {
    $code = md5(uniqid(rand(), TRUE));
    $extId = strtoupper(str_replace($problem_characters, rand(2, 9), substr($code, 0, 7)));
    if ( ! in_array($extId, $hashes)) {
        $hashes[] = $extId;
        $i++;
        // insert $extId in the database
    }
}
于 2012-12-26T16:10:46.470 に答える