UPDATE CattleProds
SET SheepTherapy=(ROUND((RAND()* 10000),0))
WHERE SheepTherapy IS NULL
Jeśli następnie wykonam SELECT, widzę, że moja liczba losowa jest identyczna w każdym wierszu . Jakieś pomysły, jak wygenerować unikalne liczby losowe?
UPDATE CattleProds
SET SheepTherapy=(ROUND((RAND()* 10000),0))
WHERE SheepTherapy IS NULL
Jeśli następnie wykonam SELECT, widzę, że moja liczba losowa jest identyczna w każdym wierszu . Jakieś pomysły, jak wygenerować unikalne liczby losowe?
Odpowiedzi:
Zamiast rand()
używać newid()
, które jest obliczane ponownie dla każdego wiersza wyniku. Zwykłym sposobem jest użycie modulo sumy kontrolnej. Zauważ, że checksum(newid())
może to dać -2,147,483,648 i spowodować przepełnienie całkowitoliczbowe abs()
, więc musimy użyć modulo na zwracanej wartości sumy kontrolnej przed konwersją jej na wartość bezwzględną.
UPDATE CattleProds
SET SheepTherapy = abs(checksum(NewId()) % 10000)
WHERE SheepTherapy IS NULL
To generuje liczbę losową z zakresu od 0 do 9999.
Jeśli korzystasz z programu SQL Server 2008, możesz również użyć
CRYPT_GEN_RANDOM(2) % 10000
Co wydaje się nieco prostsze (jest również oceniane raz na wiersz, jak newid
jest - pokazane poniżej)
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
Zwroty (2 losowe prawdopodobnie różne liczby)
col1
----------------------
9693
8573
Rozważając niewyjaśnione głosy przeciwne, jedyny uzasadniony powód, jaki przychodzi mi do głowy, jest taki, że ponieważ wygenerowana liczba losowa zawiera się w przedziale 0-65535, co nie jest podzielne równo przez 10000, niektóre liczby będą nieco nadreprezentowane. Sposobem na obejście tego byłoby umieszczenie go w skalarnym UDF, który odrzuca dowolną liczbę powyżej 60 000 i wywołuje się rekurencyjnie, aby uzyskać numer zastępczy.
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
Chociaż uwielbiam używać CHECKSUM, czuję, że lepszym sposobem jest użycie NEWID()
, tylko dlatego, że nie musisz przechodzić przez skomplikowane obliczenia matematyczne, aby wygenerować proste liczby.
ROUND( 1000 *RAND(convert(varbinary, newid())), 0)
Możesz zastąpić 1000
dowolną liczbą, którą chcesz ustawić jako limit, i zawsze możesz użyć znaku plusa, aby utworzyć zakres, powiedzmy, że chcesz losową liczbę od 100
do 200
, możesz zrobić coś takiego:
100 + ROUND( 100 *RAND(convert(varbinary, newid())), 0)
Łącząc to w swoim zapytaniu:
UPDATE CattleProds
SET SheepTherapy= ROUND( 1000 *RAND(convert(varbinary, newid())), 0)
WHERE SheepTherapy IS NULL
Przetestowałem 2 metody randomizacji oparte na zestawach względem RAND (), generując z nich 100 000 000 wierszy. Aby wyrównać pole, wynikiem jest liczba zmiennoprzecinkowa z zakresu 0-1, aby naśladować RAND (). Większość kodu testuje infrastrukturę, więc podsumuję algorytmy tutaj:
-- 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
Używanie CRYPT_GEN_RANDOM było najwyraźniej najbardziej losowe, ponieważ istnieje tylko 0,000000001% szansy na zobaczenie nawet 1 duplikatu podczas wyrywania 10 ^ 8 liczb Z zestawu 10 ^ 18 liczb. IOW, nie powinniśmy byli widzieć żadnych duplikatów, a ten nie miał żadnego! Wygenerowanie tego zestawu na moim laptopie zajęło 44 sekundy.
Cnt Pct
----- ----
1 100.000000 --No duplicates
Czas wykonania programu SQL Server: czas procesora = 134795 ms, czas, który upłynął = 39274 ms.
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;
Przy prawie 15 rzędach wielkości mniej losowych ta metoda nie była dwa razy szybsza, a wygenerowanie 100 milionów liczb zajęło tylko 23 sekundy.
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
Czas wykonania programu SQL Server: czas procesora = 77156 ms, czas, który upłynął = 24613 ms.
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;
Samo RAND () jest bezużyteczne do generowania opartego na zestawie, więc wygenerowanie linii bazowej do porównania losowości zajęło ponad 6 godzin i musiało być kilkakrotnie uruchamiane ponownie, aby ostatecznie uzyskać odpowiednią liczbę wierszy wyjściowych. Wydaje się również, że losowość pozostawia wiele do życzenia, chociaż jest lepsza niż użycie sumy kontrolnej (newid ()) do ponownego wpisania każdego wiersza.
Cnt Pct
---- ----
1 99.768020
2 00.115840
3 00.000100 -- at least there were comparitively few values returned 3 times
Z powodu ponownych uruchomień nie można było zarejestrować czasu wykonania.
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'");
}