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()
, тому нам потрібно використовувати modulo для значення контрольної суми, перш ніж перетворити його в абсолютне значення.
UPDATE CattleProds
SET SheepTherapy = abs(checksum(NewId()) % 10000)
WHERE SheepTherapy IS NULL
Це генерує випадкове число від 0 до 9999.
Якщо ви використовуєте SQL Server 2008, ви також можете використовувати
CRYPT_GEN_RANDOM(2) % 10000
Що здається дещо простішим (воно також оцінюється один раз на рядок, як newid
є - показано нижче)
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, яке не рівномірно ділиться на 10000, деякі числа будуть трохи перевизначені. Шляхом цього можна було б обернути його в скалярний ОДС, який викидає будь-яке число понад 60 000 і рекурсивно викликає себе, щоб отримати номер заміни.
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
Я перевірив 2 методи рандомізації на основі набору проти RAND (), створивши 100 000 000 рядків з кожним. Для вирівнювання поля на виході є плаваюче значення між 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 було явно найбільш випадковим, оскільки існує лише шанс .000000001% побачити навіть 1 дублікат при зриванні 10 ^ 8 чисел ІЗ набору 10 ^ 18 чисел. IOW ми не повинні були бачити жодних дублікатів, і цього не було! Цей набір зайняв 44 секунди на моєму ноутбуці.
Cnt Pct
----- ----
1 100.000000 --No duplicates
Час виконання SQL Server: час процесора = 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 порядків величини менш випадковим цей метод був не зовсім вдвічі швидшим, для створення 100 мільйонів чисел потрібно лише 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: час процесора = 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 годин і довелося перезапустити кілька разів, щоб остаточно отримати потрібну кількість вихідних рядків. Також здається, що випадковість залишає бажати кращого, хоча це краще, ніж використання контрольної суми (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'");
}