UPDATE CattleProds
SET SheepTherapy=(ROUND((RAND()* 10000),0))
WHERE SheepTherapy IS NULL
次にSELECTを実行すると、乱数がすべての行で同一であることがわかります。一意の乱数を生成する方法はありますか?
UPDATE CattleProds
SET SheepTherapy=(ROUND((RAND()* 10000),0))
WHERE SheepTherapy IS NULL
次にSELECTを実行すると、乱数がすべての行で同一であることがわかります。一意の乱数を生成する方法はありますか?
回答:
の代わりにrand()、newid()結果の各行に対して再計算されるを使用します。通常の方法は、チェックサムのモジュロを使用することです。にchecksum(newid())-2,147,483,648が生成されabs()、で整数オーバーフローが発生する可能性があるため、チェックサムの戻り値を絶対値に変換する前にモジュロを使用する必要があることに注意してください。
UPDATE CattleProds
SET SheepTherapy = abs(checksum(NewId()) % 10000)
WHERE SheepTherapy IS NULL
これにより、0から9999までの乱数が生成されます。
SQL Server 2008を使用している場合は、次を使用することもできます。
CRYPT_GEN_RANDOM(2) % 10000
これはやや単純に見えます(newid以下に示すように、行ごとに1回評価されます)
DECLARE @foo TABLE (col1 FLOAT)
INSERT INTO @foo SELECT 1 UNION SELECT 2
UPDATE @foo
SET col1 = CRYPT_GEN_RANDOM(2) % 10000
SELECT * FROM @foo
戻り値(2つのランダムなおそらく異なる数)
col1
----------------------
9693
8573
説明のつかない反対票をつぶすと、私が考えることができる唯一の正当な理由は、生成される乱数が0〜65535であり、10,000で割り切れないため、一部の数値がわずかに過剰に表現されるためです。これを回避する方法は、60,000を超える任意の番号を破棄し、それ自体を再帰的に呼び出して置換番号を取得するスカラーUDFでラップすることです。
CREATE FUNCTION dbo.RandomNumber()
RETURNS INT
AS
BEGIN
DECLARE @Result INT
SET @Result = CRYPT_GEN_RANDOM(2)
RETURN CASE
WHEN @Result < 60000
OR @@NESTLEVEL = 32 THEN @Result % 10000
ELSE dbo.RandomNumber()
END
END
私はCHECKSUMを使用するのが大好きNEWID()ですが、単純な数値を生成するために複雑な計算を行う必要がないという理由だけで、を使用する方が良い方法だと思います。
ROUND( 1000 *RAND(convert(varbinary, newid())), 0)
を1000制限として設定する任意の数値に置き換えることができ、いつでもプラス記号を使用して範囲を作成できます。たとえば、100との間の乱数200が必要な場合は、次のように実行できます。
100 + ROUND( 100 *RAND(convert(varbinary, newid())), 0)
クエリにまとめる:
UPDATE CattleProds
SET SheepTherapy= ROUND( 1000 *RAND(convert(varbinary, newid())), 0)
WHERE SheepTherapy IS NULL
それぞれ1億行を生成することにより、RAND()に対して2つのセットベースのランダム化メソッドをテストしました。フィールドを平準化するために、出力は0-1からRAND()を模倣するフロートになります。ほとんどのコードはインフラストラクチャをテストしているので、ここでアルゴリズムを要約します。
-- Try #1 used
(CAST(CRYPT_GEN_RANDOM(8) AS BIGINT)%500000000000000000+500000000000000000.0)/1000000000000000000 AS Val
-- Try #2 used
RAND(Checksum(NewId()))
-- and to have a baseline to compare output with I used
RAND() -- this required executing 100000000 separate insert statements
CRYPT_GEN_RANDOMの使用は、10 ^ 18個の数字のセットから10 ^ 8個の数字を抽出するときに、重複が1つでも表示される可能性が.000000001%しかないため、明らかに最もランダムでした。IOW重複は見られないはずでしたが、これには重複がありませんでした。このセットは私のラップトップで生成するのに44秒かかりました。
Cnt Pct
----- ----
1 100.000000 --No duplicates
SQL Serverの実行時間:CPU時間= 134795ミリ秒、経過時間= 39274ミリ秒。
IF OBJECT_ID('tempdb..#T0') IS NOT NULL DROP TABLE #T0;
GO
WITH L0 AS (SELECT c FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS D(c)) -- 2^4
,L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B) -- 2^8
,L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B) -- 2^16
,L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B) -- 2^32
SELECT TOP 100000000 (CAST(CRYPT_GEN_RANDOM(8) AS BIGINT)%500000000000000000+500000000000000000.0)/1000000000000000000 AS Val
INTO #T0
FROM L3;
WITH x AS (
SELECT Val,COUNT(*) Cnt
FROM #T0
GROUP BY Val
)
SELECT x.Cnt,COUNT(*)/(SELECT COUNT(*)/100 FROM #T0) Pct
FROM X
GROUP BY x.Cnt;
ランダム性がほぼ15桁少ないため、この方法は2倍の速さではなく、1億個の数値を生成するのに23秒しかかかりませんでした。
Cnt Pct
---- ----
1 95.450254 -- only 95% unique is absolutely horrible
2 02.222167 -- If this line were the only problem I'd say DON'T USE THIS!
3 00.034582
4 00.000409 -- 409 numbers appeared 4 times
5 00.000006 -- 6 numbers actually appeared 5 times
SQL Serverの実行時間:CPU時間= 77156ミリ秒、経過時間= 24613ミリ秒。
IF OBJECT_ID('tempdb..#T1') IS NOT NULL DROP TABLE #T1;
GO
WITH L0 AS (SELECT c FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS D(c)) -- 2^4
,L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B) -- 2^8
,L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B) -- 2^16
,L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B) -- 2^32
SELECT TOP 100000000 RAND(Checksum(NewId())) AS Val
INTO #T1
FROM L3;
WITH x AS (
SELECT Val,COUNT(*) Cnt
FROM #T1
GROUP BY Val
)
SELECT x.Cnt,COUNT(*)*1.0/(SELECT COUNT(*)/100 FROM #T1) Pct
FROM X
GROUP BY x.Cnt;
RAND()だけではセットベースの生成には役に立たないため、ランダム性を比較するためのベースラインの生成には6時間以上かかり、最終的に正しい数の出力行を取得するには数回再起動する必要がありました。また、checksum(newid())を使用して各行を再シードするよりも優れていますが、ランダム性には多くの要望が残されているようです。
Cnt Pct
---- ----
1 99.768020
2 00.115840
3 00.000100 -- at least there were comparitively few values returned 3 times
再起動のため、実行時間をキャプチャできませんでした。
IF OBJECT_ID('tempdb..#T2') IS NOT NULL DROP TABLE #T2;
GO
CREATE TABLE #T2 (Val FLOAT);
GO
SET NOCOUNT ON;
GO
INSERT INTO #T2(Val) VALUES(RAND());
GO 100000000
WITH x AS (
SELECT Val,COUNT(*) Cnt
FROM #T2
GROUP BY Val
)
SELECT x.Cnt,COUNT(*)*1.0/(SELECT COUNT(*)/100 FROM #T2) Pct
FROM X
GROUP BY x.Cnt;
require_once('db/connect.php');
//rand(1000000 , 9999999);
$products_query = "SELECT id FROM products";
$products_result = mysqli_query($conn, $products_query);
$products_row = mysqli_fetch_array($products_result);
$ids_array = [];
do
{
array_push($ids_array, $products_row['id']);
}
while($products_row = mysqli_fetch_array($products_result));
/*
echo '<pre>';
print_r($ids_array);
echo '</pre>';
*/
$row_counter = count($ids_array);
for ($i=0; $i < $row_counter; $i++)
{
$current_row = $ids_array[$i];
$rand = rand(1000000 , 9999999);
mysqli_query($conn , "UPDATE products SET code='$rand' WHERE id='$current_row'");
}