Wie generiere ich eine Zufallszahl für jede Zeile in einer TSQL-Auswahl?

328

Ich benötige eine andere Zufallszahl für jede Zeile in meiner Tabelle. Der folgende scheinbar offensichtliche Code verwendet für jede Zeile denselben Zufallswert.

SELECT table_name, RAND() magic_number 
FROM information_schema.tables 

Ich würde gerne ein INT oder ein FLOAT daraus machen. Der Rest der Geschichte ist, dass ich diese Zufallszahl verwenden werde, um einen zufälligen Datumsversatz von einem bekannten Datum zu erstellen, z. B. 1-14 Tage Versatz von einem Startdatum.

Dies ist für Microsoft SQL Server 2000.

MatthewMartin
quelle
4
Gibt es eine Lösung dafür, die NEWID () nicht verwendet? Ich möchte in der Lage sein, die gleiche Folge von Zufallszahlen für einen bestimmten Samen zu erzeugen.
Rory MacLeod
@Rory Stellen Sie das als neue Frage, es wird mehr Aufmerksamkeit bekommen. (Meine Antwort wäre, feste Tabellen mit Zufallszahlen zu verwenden, z. B. diesen berühmten Standardsatz von Zufallszahlen: rand.org/pubs/monograph_reports/MR1418/index.html )
MatthewMartin
2
Look @ RAND (Transact-SQL)
AminM
RAND wurde 2005 eingeführt, diese Frage wurde 2009 gestellt, welche Organisationen SQL 2000 noch verwendeten, da dies die erste Version war, die für die Verwendung für immer gut genug war.
Matthew Martin
Rory MacLeod fragte: "Gibt es eine Lösung dafür, die NEWID () nicht verwendet? Ich möchte in der Lage sein, dieselbe Folge von Zufallszahlen für einen bestimmten Startwert zu generieren." Die Antwort ist ja, aber es ist ein bisschen verworren. 1. Erstellen Sie eine Ansicht, die select rand () zurückgibt. 2. Erstellen Sie eine UDF, die den Wert aus der Ansicht auswählt. 3. Setzen Sie vor der Auswahl Ihrer Daten die Funktion rand (). 4. Verwenden Sie die UDF in Ihrer select-Anweisung. Ich werde ein vollständiges Beispiel unten
posten

Antworten:

516

Werfen Sie einen Blick auf SQL Server - Set-basierte Zufallszahlen, die eine sehr detaillierte Erklärung enthalten.

Zusammenfassend erzeugt der folgende Code eine Zufallszahl zwischen 0 und einschließlich 13 mit einer gleichmäßigen Verteilung:

ABS(CHECKSUM(NewId())) % 14

Um Ihren Bereich zu ändern, ändern Sie einfach die Zahl am Ende des Ausdrucks. Seien Sie besonders vorsichtig, wenn Sie einen Bereich benötigen, der sowohl positive als auch negative Zahlen enthält. Wenn Sie es falsch machen, können Sie die Zahl 0 doppelt zählen.

Eine kleine Warnung für die Mathe-Nüsse im Raum: Dieser Code enthält eine sehr leichte Abweichung. CHECKSUM()führt zu Zahlen, die über den gesamten Bereich des SQL Int-Datentyps einheitlich sind oder zumindest so nahe, wie meine (der Editor-) Tests zeigen können. Es wird jedoch eine gewisse Verzerrung geben, wenn CHECKSUM () eine Zahl ganz oben in diesem Bereich erzeugt. Jedes Mal, wenn Sie vor dieser maximalen Ganzzahl eine Zahl zwischen der maximal möglichen Ganzzahl und dem letzten exakten Vielfachen der Größe Ihres gewünschten Bereichs (in diesem Fall 14) erhalten, werden diese Ergebnisse gegenüber dem verbleibenden Teil Ihres Bereichs bevorzugt, aus dem nicht erzeugt werden kann das letzte Vielfache von 14.

Stellen Sie sich als Beispiel vor, der gesamte Bereich des Int-Typs ist nur 19. 19 ist die größtmögliche Ganzzahl, die Sie halten können. Wenn CHECKSUM () zu 14-19 führt, entsprechen diese den Ergebnissen 0-5. Diese Zahlen würden gegenüber 6-13 stark bevorzugt, da CHECKSUM () sie mit doppelter Wahrscheinlichkeit generiert. Es ist einfacher, dies visuell zu demonstrieren. Nachfolgend finden Sie die gesamte mögliche Ergebnismenge für unseren imaginären ganzzahligen Bereich:

Prüfsumme Ganzzahl: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Bereich Ergebnis: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5

Sie können hier sehen, dass es mehr Chancen gibt, einige Zahlen zu produzieren als andere: Voreingenommenheit. Zum Glück ist der tatsächliche Bereich des Int-Typs viel größer ... so sehr, dass in den meisten Fällen die Vorspannung fast nicht nachweisbar ist. Es ist jedoch etwas zu beachten, wenn Sie dies jemals für einen ernsthaften Sicherheitscode tun.

SQLMenace
quelle
28
Diese verlinkte Seite hatte die Lösung: ABS (CHECKSUM (NewId ()))% 14
MatthewMartin
7
% 14 würde Zahlen zwischen 0 und 13 zurückgeben
CoderDennis
7
@ Tennis Palmer, fügen Sie einfach 1
KM hinzu.
59
Wir haben gerade einen genialen Fehler entdeckt. Da die Prüfsumme ein int zurückgibt und der Bereich eines int -2 ^ 31 (-2,147,483,648) bis 2 ^ 31-1 (2,147,483,647) beträgt, kann die Funktion abs () einen Überlauffehler zurückgeben, wenn das Ergebnis genau -2,147,483,648 ist ! Die Chancen sind offensichtlich sehr gering, ungefähr 1 zu 4 Milliarden, aber wir haben es jeden Tag über einen ~ 1,8b-Zeilentisch laufen lassen, also passierte es ungefähr einmal pro Woche! Fix ist, die Prüfsumme vor den Bauchmuskeln auf Bigint zu setzen.
EvilPuppetMaster
17
Ich denke, dies sollte "eine gleichmäßige Verteilung" heißen, nicht "normalisierte Verteilung" - jede Zahl ist gleich wahrscheinlich, es ist keine Glockenkurve. "Normalisiert" hat eine spezifische mathematische Bedeutung.
AnotherParker
95

Bei mehrmaligem Aufruf in einem Stapel gibt rand () dieselbe Nummer zurück.

Ich würde vorschlagen, convert ( varbinary, newid()) als Startargument zu verwenden:

SELECT table_name, 1.0 + floor(14 * RAND(convert(varbinary, newid()))) magic_number 
FROM information_schema.tables

newid() Es wird garantiert, dass bei jedem Aufruf ein anderer Wert zurückgegeben wird, auch innerhalb derselben Charge. Wenn Sie ihn also als Startwert verwenden, wird rand () jedes Mal aufgefordert, einen anderen Wert anzugeben.

Bearbeitet, um eine zufällige ganze Zahl von 1 bis 14 zu erhalten.

Jeremy Smyth
quelle
Wie bekommt man eine Nummer aus einem Guid oder Varbinary? Ich werde die Frage aktualisieren, um anzuzeigen, dass ich auf eine Ganzzahl hoffe.
Matthew
1
Sie multiplizieren es mit einer Zahl und legen es fest :) Wenn Sie also fünf Ziffern möchten, multiplizieren Sie es mit 100000 und konvertieren Sie es in ein int. Hässlich, aber einfach genug.
Jeremy Smyth
1
Als weiteres Addendum - das gibt Ihnen bis zu fünf Ziffern - müssen Sie einen char-Datentyp verwenden und replizieren, um bis zu fünf Ziffern auf null zu setzen.
Jeremy Smyth
Wenn Sie die
Deckenfunktion
Selbst wenn ich dies benutze, gibt es Zeiten, in denen RAND () immer das gleiche Ergebnis liefert. Noch seltsamer, es gibt Zeiten, in denen es von einem korrekten zu einem falschen Verhalten springt, abhängig davon, wie oft ich es benutze. Ich versuche einen RANDOM INNER JOIN zu implementieren und wenn ich nach mehr als 19 (!!!) Zeilen frage, bekomme ich immer das gleiche Ergebnis ...
Johannes Wentu
72
RAND(CHECKSUM(NEWID()))

Das Obige erzeugt eine (Pseudo-) Zufallszahl zwischen 0 und 1, exklusiv. Bei Verwendung in einer Auswahl wird, da sich der Startwert für jede Zeile ändert, eine neue Zufallszahl für jede Zeile generiert (es wird jedoch nicht garantiert, dass pro Zeile eine eindeutige Nummer generiert wird).

Beispiel in Kombination mit einer Obergrenze von 10 (ergibt die Zahlen 1 - 10):

CAST(RAND(CHECKSUM(NEWID())) * 10 as INT) + 1

Transact-SQL-Dokumentation:

  1. CAST(): https://docs.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql
  2. RAND(): http://msdn.microsoft.com/en-us/library/ms177610.aspx
  3. CHECKSUM(): http://msdn.microsoft.com/en-us/library/ms189788.aspx
  4. NEWID(): https://docs.microsoft.com/en-us/sql/t-sql/functions/newid-transact-sql
Aaron Hoffman
quelle
39

Zufallszahlengenerierung zwischen 1000 und 9999 einschließlich:

FLOOR(RAND(CHECKSUM(NEWID()))*(9999-1000+1)+1000)

"+1" - um Obergrenzenwerte einzuschließen (9999 für vorheriges Beispiel)

Vova
quelle
Die Obergrenze ist exklusiv bei dieser Methode. Wenn Sie also die oberste Zahl FLOOR(RAND(CHECKSUM(NEWID()))*(10000-1000)+1000)
angeben
20

Beantwortung der alten Frage, aber diese Antwort wurde bisher noch nicht gegeben, und hoffentlich ist dies nützlich für jemanden, der diese Ergebnisse über eine Suchmaschine findet.

Mit SQL Server 2008 wurde eine neue Funktion eingeführt, CRYPT_GEN_RANDOM(8)die CryptoAPI verwendet, um eine kryptografisch starke Zufallszahl zu erzeugen, die als zurückgegeben wird VARBINARY(8000). Hier ist die Dokumentationsseite: https://docs.microsoft.com/en-us/sql/t-sql/functions/crypt-gen-random-transact-sql

Um eine Zufallszahl zu erhalten, können Sie die Funktion einfach aufrufen und in den erforderlichen Typ umwandeln:

select CAST(CRYPT_GEN_RANDOM(8) AS bigint)

oder um einen floatWert zwischen -1 und +1 zu erhalten, können Sie Folgendes tun:

select CAST(CRYPT_GEN_RANDOM(8) AS bigint) % 1000000000 / 1000000000.0
Andrei Tanas
quelle
13

Die Rand () - Funktion generiert dieselbe Zufallszahl, wenn sie in einer Tabellen-SELECT-Abfrage verwendet wird. Gleiches gilt, wenn Sie einen Startwert für die Rand-Funktion verwenden. Eine alternative Möglichkeit besteht darin, Folgendes zu verwenden:

SELECT ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS [RandomNumber]

Ich habe die Informationen von hier erhalten , was das Problem sehr gut erklärt.

MicSim
quelle
5

Haben Sie in jeder Zeile einen ganzzahligen Wert, den Sie als Startwert an die RAND-Funktion übergeben können?

Um eine ganze Zahl zwischen 1 und 14 zu erhalten, würde dies meiner Meinung nach funktionieren:

FLOOR( RAND(<yourseed>) * 14) + 1
CoderDennis
quelle
Dies funktioniert theoretisch, aber in der Praxis habe ich festgestellt, dass das RAND(<seed>)für geringfügige Änderungen in nicht sehr zufällig zu sein scheint <seed>. Zum Beispiel einen Schnelltest, den ich gemacht habe: Ich habe <seed>184380, 184383, 184386 sein lassen, und die entsprechenden RAND(<seed>)Werte waren: 0,14912, 0,14917, 0,14923.
ImaginaryHuman072889
Vielleicht, um mehr "scheinbar" zufällige Ergebnisse zu erhalten, versuchen Sie etwas wie:RAND(<seed>)*100000) - FLOOR(RAND(<seed>)*100000)
ImaginaryHuman072889
5

Wenn Sie Ihren Samen so aufbewahren müssen, dass er jedes Mal die "gleichen" Zufallsdaten generiert, können Sie Folgendes tun:

1. Erstellen Sie eine Ansicht, die select rand () zurückgibt.

if object_id('cr_sample_randView') is not null
begin
    drop view cr_sample_randView
end
go

create view cr_sample_randView
as
select rand() as random_number
go

2. Erstellen Sie eine UDF, die den Wert aus der Ansicht auswählt.

if object_id('cr_sample_fnPerRowRand') is not null
begin
    drop function cr_sample_fnPerRowRand
end
go

create function cr_sample_fnPerRowRand()
returns float
as
begin
    declare @returnValue float
    select @returnValue = random_number from cr_sample_randView
    return @returnValue
end
go

3. Setzen Sie vor der Auswahl Ihrer Daten die Funktion rand () und verwenden Sie dann die UDF in Ihrer select-Anweisung.

select rand(200);   -- see the rand() function
with cte(id) as
(select row_number() over(order by object_id) from sys.all_objects)
select 
    id,
    dbo.cr_sample_fnPerRowRand()
from cte
where id <= 1000    -- limit the results to 1000 random numbers
Mitselplik
quelle
4

Versuchen Sie, einen Startwert im RAND (seedInt) zu verwenden. RAND () wird nur einmal pro Anweisung ausgeführt, weshalb Sie jedes Mal dieselbe Nummer sehen.

Nordpol
quelle
Am einfachsten! Obwohl die Werte viel verstreuter zu sein scheinen, verwenden Sie Ziffern aus der Mitte RIGHT(CONVERT(BIGINT, RAND(RecNo) * 1000000000000), 2) (Anmerkung: Ich sehe RIGHTimplizit die Konvertierung BIGINTin CHAR, aber um streng zu sein, hätten Sie eine andere CONVERTdarin).
Doug_Ivison
4

Wenn Sie nicht benötigen, dass es sich um eine Ganzzahl handelt, sondern um eine zufällige eindeutige Kennung, können Sie diese verwenden newid()

SELECT table_name, newid() magic_number 
FROM information_schema.tables

quelle
4

Sie müssten RAND () für jede Zeile aufrufen. Hier ist ein gutes Beispiel

https://web.archive.org/web/20090216200320/http://dotnet.org.za/calmyourself/archive/2007/04/13/sql-rand-trap-same-value-per-row.aspx

David
quelle
Toter Link :(
Gibt es
Er setzt RAND()eine Ansicht ein, fügt eine SELECTdieser Ansichten in eine Funktion ein und ruft die Funktion dann von überall auf. Klug.
Doug_Ivison
Ich habe eine Lösung veröffentlicht, die das Problem genauso löst wie im verlinkten Artikel, aber hier in diesem Blog direkt als Antwort vor fünf Beiträgen! Niemand rief mich klug Neid Gesicht hehe
Mitselplik
4
select round(rand(checksum(newid()))*(10)+20,2)

Hier liegt die Zufallszahl zwischen 20 und 30. Sie roundergibt maximal zwei Dezimalstellen.

Wenn Sie negative Zahlen wollen, können Sie es mit tun

select round(rand(checksum(newid()))*(10)-60,2)

Dann ist der minimale Wert -60 und der maximale Wert -50.

Tirthankar
quelle
3

Es ist so einfach wie:

DECLARE @rv FLOAT;
SELECT @rv = rand();

Und dies wird eine Zufallszahl zwischen 0 und 99 in eine Tabelle einfügen:

CREATE TABLE R
(
    Number int
)

DECLARE @rv FLOAT;
SELECT @rv = rand();

INSERT INTO dbo.R
(Number)
    values((@rv * 100));

SELECT * FROM R
Rosjier Hall
quelle
2

Das Problem, das ich manchmal mit der ausgewählten "Antwort" habe, ist, dass die Verteilung nicht immer gleichmäßig ist. Wenn Sie eine sehr gleichmäßige Verteilung der zufälligen 1 - 14 auf viele Zeilen benötigen, können Sie Folgendes tun (meine Datenbank verfügt über 511 Tabellen, sodass dies funktioniert. Wenn Sie weniger Zeilen als die Zufallszahlenspanne haben, funktioniert dies nicht Gut):

SELECT table_name, ntile(14) over(order by newId()) randomNumber 
FROM information_schema.tables

Diese Art macht das Gegenteil von normalen Zufallslösungen in dem Sinne, dass die Zahlen sequenziert bleiben und die andere Spalte randomisiert wird.

Denken Sie daran, ich habe 511 Tabellen in meiner Datenbank (was nur relevant ist, wenn wir aus dem Informationsschema auswählen). Wenn ich die vorherige Abfrage in eine temporäre Tabelle #X stelle und diese Abfrage dann für die resultierenden Daten ausführe:

select randomNumber, count(*) ct from #X
group by randomNumber

Ich erhalte dieses Ergebnis und zeige, dass meine Zufallszahl SEHR gleichmäßig auf die vielen Zeilen verteilt ist:

Geben Sie hier die Bildbeschreibung ein

Trevor
quelle
2
select ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) as [Randomizer]

hat immer für mich gearbeitet

die Liga
quelle
2

Verwenden Sie newid ()

select newid()

oder möglicherweise das

select binary_checksum(newid())
Chris Klepeis
quelle
1
    DROP VIEW IF EXISTS vwGetNewNumber;
    GO
    Create View vwGetNewNumber
    as
    Select CAST(RAND(CHECKSUM(NEWID())) * 62 as INT) + 1 as NextID,
    'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'as alpha_num;

    ---------------CTDE_GENERATE_PUBLIC_KEY -----------------
    DROP FUNCTION IF EXISTS CTDE_GENERATE_PUBLIC_KEY;  
    GO
    create function CTDE_GENERATE_PUBLIC_KEY()
    RETURNS NVARCHAR(32)
    AS 
    BEGIN
        DECLARE @private_key NVARCHAR(32);
        set @private_key = dbo.CTDE_GENERATE_32_BIT_KEY();
        return @private_key;
    END;
    go

---------------CTDE_GENERATE_32_BIT_KEY -----------------
DROP FUNCTION IF EXISTS CTDE_GENERATE_32_BIT_KEY;  
GO
CREATE function CTDE_GENERATE_32_BIT_KEY()
RETURNS NVARCHAR(32)
AS 
BEGIN
    DECLARE @public_key NVARCHAR(32);
    DECLARE @alpha_num NVARCHAR(62);
    DECLARE @start_index INT = 0;
    DECLARE @i INT = 0;
    select top 1 @alpha_num = alpha_num from vwGetNewNumber;
        WHILE @i < 32
        BEGIN
          select top 1 @start_index = NextID from vwGetNewNumber;
          set @public_key = concat (substring(@alpha_num,@start_index,1),@public_key);
          set @i = @i + 1;
        END;
    return @public_key;
END;
    select dbo.CTDE_GENERATE_PUBLIC_KEY() public_key;
ichak khoury
quelle
sorry @arnt wenn ich nicht gut erklärt habe,
ichak khoury
sorry @arnt, wir haben hier zwei Funktionen: CTDE_GENERATE_32_BIT_KEY , die einen alphanumerischen 32-Bit-Schlüssel generieren (kann mehr oder weniger erweitert werden), und den anderen mit dem Namen CTDE_GENERATE_PUBLIC_KEY , der die erste Funktion aufruft und den öffentlichen 32-Bit-Schlüssel zurückgibt , oder Sie können zurückkehren ein privater Schlüssel von 16 Bit ... Sie müssen nur select dbo.CTDE_GENERATE_PUBLIC_KEY () als öffentlichen Schlüssel aufrufen ; Die Logik dahinter ist, dass wir 32 Mal ein Zeichen aus der Liste der alphanumerischen Zeichen auswählen und sie miteinander verketten, um den zufälligen alphanumerischen Schlüssel zu erhalten. nach der Forschung.
Ichak Khoury
Nett. Diese Erklärung macht es zu einer viel besseren Antwort. (Jemand hat es zum Löschen markiert; ich habe dafür gestimmt, es offen zu lassen und diesen Kommentar für Sie
hinterlassen
0

Versuche dies:

SELECT RAND(convert(varbinary, newid()))*(b-a)+a magic_number 

Wo aist die untere Zahl und bist die obere Zahl

Rutendo
quelle
1
Können Sie versuchen, bei der Beantwortung einer Frage klarer zu sein?
Yunus Temurlenk
0
Update my_table set my_field = CEILING((RAND(CAST(NEWID() AS varbinary)) * 10))

Nummer zwischen 1 und 10.

user3478586
quelle