Wie kann ich eine Spalte in SQL mit Zufallszahlen füllen? Ich bekomme in jeder Zeile den gleichen Wert

84
UPDATE CattleProds
SET SheepTherapy=(ROUND((RAND()* 10000),0))
WHERE SheepTherapy IS NULL

Wenn ich dann SELECT mache, sehe ich, dass meine Zufallszahl in jeder Zeile identisch ist . Irgendwelche Ideen, wie man eindeutige Zufallszahlen generiert?

NibblyPig
quelle

Antworten:

166

rand()Verwenden Sie stattdessen use newid(), das für jede Zeile im Ergebnis neu berechnet wird. Der übliche Weg ist, das Modulo der Prüfsumme zu verwenden. Beachten Sie, dass checksum(newid())dies -2.147.483.648 erzeugen und einen Ganzzahlüberlauf verursachen kann. Daher abs()müssen Sie Modulo für den Prüfsummenrückgabewert verwenden, bevor Sie ihn in einen absoluten Wert konvertieren.

UPDATE CattleProds
SET    SheepTherapy = abs(checksum(NewId()) % 10000)
WHERE  SheepTherapy IS NULL

Dies erzeugt eine Zufallszahl zwischen 0 und 9999.

Andomar
quelle
1
Diese Frage / Antwort kann auch hilfreich sein: stackoverflow.com/a/9039661/47226
Aaron Hoffman
Das funktioniert bei mir überhaupt nicht. Muss die Spalte INT sein? Fehler # 1064 jedes Mal. Greifen nach den verrückten Pillen ...
Freeworlder
1
Das ist eine Sache von Schönheit! Gut gemacht. Liebe es. Ein kleines bisschen langsame Leistung, aber immer noch großartig.
Arvin Amir
25

Wenn Sie mit SQL Server 2008 arbeiten, können Sie auch verwenden

 CRYPT_GEN_RANDOM(2) % 10000

Was etwas einfacher erscheint (es wird auch einmal pro Zeile ausgewertet, wie newides unten gezeigt wird)

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

Rückgabe (2 zufällige wahrscheinlich unterschiedliche Zahlen)

col1
----------------------
9693
8573

Das einzige legitime Grund, an das ich denken kann, ist, dass einige Zahlen leicht überrepräsentiert sind, wenn die zufällige Zahl zwischen 0 und 65535 liegt, die nicht gleichmäßig durch 10.000 teilbar ist. Eine Möglichkeit, dies zu umgehen, besteht darin, es in eine skalare UDF zu verpacken, die eine beliebige Zahl über 60.000 wegwirft und sich rekursiv aufruft, um eine Ersatznummer zu erhalten.

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  
Martin Smith
quelle
1
@downvoter - Gibt es einen bestimmten Grund? Vielleicht wolltest du den Aufwärtspfeil drücken, diese Antwort funktioniert gut!
Martin Smith
Was jeder zu vermissen scheint, ist, dass diese Methode VIEL VIEL VIEL besser für die Leistung ist. Ich habe nach einer Alternative zu NEWID () gesucht und das ist genau richtig, danke!
Digs
Jeder gewünschte Bereich ist leicht zu handhaben. Zum Beispiel ergibt ABS (CAST (CRYPT_GEN_RANDOM (8) AS BIGINT)% 10001) eine Zahl von 0-10000. Dies ist der Bereich, den der OP-Code generiert hätte, wenn er wie erhofft funktioniert hätte.
Bielawski
Welches "gleiche" Problem? Die Formel generiert neue Werte pro Zeile (das Problem von op ist gelöst) und das Ergebnis liegt innerhalb des Bereichs, aber sie werden nicht verzerrt, da 64 Bit Startwert und nur 14 Bit Ergebnis vorhanden sind, sodass ein möglicher Versatz nicht erkennbar wäre. Selbst wenn Sie 10 ^ 15 Ergebnisse generiert haben, liegt jeder Versatz, den Sie möglicherweise erkennen, immer noch innerhalb der Fehlergrenze. Das heißt, Sie müssten 2 ^ 19 Ergebnisse generieren, um zu beweisen, dass tatsächlich ein Versatz vorhanden ist.
Bielawski
9

Obwohl ich CHECKSUM sehr gerne benutze, denke ich, dass ein besserer Weg die Verwendung ist NEWID(), nur weil Sie keine komplizierte Mathematik durchlaufen müssen, um einfache Zahlen zu generieren.

ROUND( 1000 *RAND(convert(varbinary, newid())), 0)

Sie können die 1000Zahl durch eine beliebige Zahl ersetzen, die Sie als Grenzwert festlegen möchten, und Sie können immer ein Pluszeichen verwenden, um einen Bereich zu erstellen. Nehmen wir an, Sie möchten eine Zufallszahl zwischen 100und 200, und Sie können Folgendes tun:

100 + ROUND( 100 *RAND(convert(varbinary, newid())), 0)

Fügen Sie es in Ihrer Anfrage zusammen:

UPDATE CattleProds 
SET SheepTherapy= ROUND( 1000 *RAND(convert(varbinary, newid())), 0)
WHERE SheepTherapy IS NULL
Segev-CJ-Shmueli
quelle
1

Ich habe 2 satzbasierte Randomisierungsmethoden gegen RAND () getestet, indem ich jeweils 100.000.000 Zeilen generiert habe. Um das Feld auszugleichen, ist der Ausgang ein Float zwischen 0-1, um RAND () nachzuahmen. Der größte Teil des Codes testet die Infrastruktur, daher fasse ich die Algorithmen hier zusammen:

-- 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

Die Verwendung von CRYPT_GEN_RANDOM war eindeutig die zufälligste, da nur eine Wahrscheinlichkeit von 0,000000001% besteht, dass beim Zupfen von 10 ^ 8 Zahlen aus einem Satz von 10 ^ 18 Zahlen sogar 1 Duplikat angezeigt wird. IOW wir hätten keine Duplikate sehen sollen und diese hatten keine! Die Erstellung dieses Sets auf meinem Laptop dauerte 44 Sekunden.

Cnt     Pct
-----   ----
 1      100.000000  --No duplicates

SQL Server-Ausführungszeiten: CPU-Zeit = 134795 ms, verstrichene Zeit = 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;

Mit fast 15 Größenordnungen weniger zufällig war diese Methode nicht ganz doppelt so schnell und benötigte nur 23 Sekunden, um 100 Millionen Zahlen zu generieren.

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-Ausführungszeiten: CPU-Zeit = 77156 ms, verstrichene Zeit = 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;

RAND () allein ist für die satzbasierte Generierung nutzlos. Das Generieren der Basislinie für den Vergleich der Zufälligkeit dauerte über 6 Stunden und musste mehrmals neu gestartet werden, um schließlich die richtige Anzahl von Ausgabezeilen zu erhalten. Es scheint auch, dass die Zufälligkeit zu wünschen übrig lässt, obwohl es besser ist, als die Prüfsumme (newid ()) zu verwenden, um jede Zeile neu zu säen.

Cnt  Pct
---- ----
1    99.768020
2    00.115840
3    00.000100  -- at least there were comparitively few values returned 3 times

Aufgrund der Neustarts konnte die Ausführungszeit nicht erfasst werden.

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;
bielawski
quelle
PS Da ich dachte, dass die Neustarts für einige der Duplikate verantwortlich sein könnten, testete ich schnell nur 3 Millionen Zeilen, was fast 6 1/2 Minuten dauerte. Ich habe 2101 Dups und 2 Werte wurden dreimal angezeigt (0,07% bzw. 0,000067%), was darauf hinweist, dass Neustarts wahrscheinlich eine Rolle gespielt haben, aber die Zufälligkeit ist immer noch alles andere als herausragend.
Bielawski
Nachdem ich eine andere Antwort bemerkt hatte, die gerade mit newid in varbinary umgewandelt wurde, versuchte ich das auch. Es ist nicht nur nicht schneller als die Verwendung der Prüfsumme, sondern ein Wert wird in diesem Test achtmal angezeigt. Um fair zu sein, war es immer noch 95,447319% einzigartig, was in meinem Test kaum schlechter ist als die 95,450254% von RAND (Checksum (NewId ())). Eine zweite Ausführung ergab den schlimmsten Fall, dass 3 Zahlen fünfmal auftraten und 95,452929% unterschiedlich waren, sodass YMMV auch beim Testen von 100 Millionen Zeilen vorhanden war.
Bielawski
-2
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'");
}
Vaso Nadiradze
quelle
Vielleicht ist es nicht korrekt und am einfachsten, aber es funktioniert)))
Vaso Nadiradze
1
Bitte lesen Sie die Frage sorgfältig durch, bevor Sie mit der Beantwortung beginnen. Übrigens ist das Senden einer UPDATE-Abfrage für jede einzelne Zeile eine SEHR, SEHR SCHLECHTE IDEE, wenn man sogar eine bescheidene Anzahl von Zeilen AKTUALISIEREN muss.
Liebling