Jak wygenerować losową liczbę dla każdego wiersza w TSQL Select?

Potrzebuję innej liczby losowej dla każdego wiersza w tabeli. Następujący pozornie oczywisty kod używa tej samej wartości losowej dla każdego wiersza.

SELECT table_name, RAND() magic_number 
FROM information_schema.tables 
Chciałbym uzyskać INT lub FLOAT z tego. Reszta historii jest to, że zamierzam użyć tej losowej liczby, aby utworzyć losowe przesunięcie daty od znanej daty, np. przesunięcie 1-14 dni od daty rozpoczęcia.

To jest dla Microsoft SQL Server 2000.

Author: rjose, 2009-06-25

16 answers

Spójrz na SQL Server - zestaw liczb losowych , który ma bardzo szczegółowe wyjaśnienie.

Podsumowując, poniższy kod generuje liczbę losową z zakresu od 0 do 13 włącznie z rozkładem znormalizowanym:

ABS(CHECKSUM(NewId())) % 14

Aby zmienić zakres, po prostu zmień liczbę na końcu wyrażenia. Bądź bardzo ostrożny, jeśli potrzebujesz zakresu, który obejmuje zarówno liczby dodatnie, jak i ujemne. Jeśli zrobisz to źle, możliwe jest podwójne zliczenie liczby 0.

A małe ostrzeżenie dla Math nuts w pokoju: jest bardzo niewielki błąd w tym kodzie. CHECKSUM() wyniki w liczbach, które są jednolite w całym zakresie Int datatype SQL, lub przynajmniej tak blisko, jak moje (edytor) testowanie może pokazać. Będzie jednak pewne odchylenie, gdy CHECKSUM () wytworzy liczbę na samym górnym końcu tego zakresu. Za każdym razem otrzymasz liczbę między maksymalną możliwą liczbą całkowitą a ostatnią dokładną wielokrotnością rozmiaru żądanego zakresu (14 w tym przypadku) przed tym Maksymalna liczba całkowita, wyniki te są preferowane w stosunku do pozostałej części zakresu, który nie może być wyprodukowany z tej ostatniej wielokrotności 14.

Jako przykład, wyobraź sobie, że cały zakres typu Int wynosi TYLKO 19. 19 to największa możliwa liczba całkowita. Gdy CHECKSUM () daje wyniki 14-19, odpowiadają one wynikom 0-5. Liczby te byłyby mocno faworyzowane przez 6-13, ponieważ CHECKSUM () jest dwa razy bardziej prawdopodobne, aby je wygenerować. Łatwiej jest to zademonstrować wizualnie. Poniżej znajduje się cały możliwy zestaw wyników dla naszego urojonego zakresu liczb całkowitych:

Checksum Integer: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Range Result:     0 1 2 3 4 5 6 7 8 9 10 11 12 13  0  1  2  3  4  5

Widać tutaj, że jest więcej szans na wyprodukowanie niektórych liczb niż innych: stronniczość. Na szczęście rzeczywisty zakres typu Int jest dużo większy... tak bardzo, że w większości przypadków stronniczość jest prawie niewykrywalna. Jednak jest to coś, o czym należy pamiętać, jeśli kiedykolwiek znajdziesz się robi to dla poważnego kodu bezpieczeństwa.

 407
Author: SQLMenace,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-25 13:46:13

Gdy wywołane wiele razy w jednej partii, rand () zwraca tę samą liczbę.

Proponuję użyć convert(varbinary,newid()) jako argument zalążkowy:

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

newid() gwarantowane jest zwrócenie innej wartości za każdym razem, gdy zostanie wywołana, nawet w ramach tej samej partii, więc użycie jej jako ziarna spowoduje, że rand() poda inną wartość za każdym razem.

Edytowane, aby uzyskać losową liczbę całkowitą od 1 do 14.

 82
Author: Jeremy Smyth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-19 07:36:42
RAND(CHECKSUM(NEWID()))

Powyższe wygeneruje (pseudo) losową liczbę z zakresu od 0 do 1, wyłącznie. Jeśli zostanie użyty w select, ponieważ wartość zalążkowa zmienia się dla każdego wiersza, wygeneruje nową liczbę losową dla każdego wiersza(nie jest jednak gwarantowane wygenerowanie unikalnej liczby dla każdego wiersza).

Przykład w połączeniu z górną granicą 10 (daje liczby 1-10):

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

Dokumentacja Transact-SQL:

  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
 58
Author: Aaron Hoffman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-13 09:13:56

Generowanie liczb losowych od 1000 do 9999 włącznie:

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

"+1" - to include upper bound values (9999 dla poprzedniego przykładu)

 26
Author: Vova,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-24 20:10:09

Odpowiadam na Stare pytanie, ale tej odpowiedzi nie udzielono wcześniej, i mam nadzieję, że będzie to przydatne dla kogoś, kto znajdzie te wyniki za pomocą wyszukiwarki.

W SQL Server 2008 wprowadzono nową funkcję, CRYPT_GEN_RANDOM(8), która wykorzystuje CryptoAPI do wytworzenia kryptograficznie silnej liczby losowej, zwracanej jako VARBINARY(8000). Oto strona dokumentacji: https://docs.microsoft.com/en-us/sql/t-sql/functions/crypt-gen-random-transact-sql

Więc aby uzyskać liczba losowa, można po prostu wywołać funkcję i wrzucić ją do wymaganego typu:

select CAST(CRYPT_GEN_RANDOM(8) AS bigint)

Lub aby uzyskać float pomiędzy -1 a +1, możesz zrobić coś takiego:

select CAST(CRYPT_GEN_RANDOM(8) AS bigint) % 1000000000 / 1000000000.0
 13
Author: Andrei Tanas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-14 20:13:55

Funkcja Rand() wygeneruje tę samą liczbę losową, jeśli zostanie użyta w zapytaniu wyboru tabeli. To samo dotyczy przypadku użycia nasion do funkcji Rand. Alternatywnym sposobem na to jest użycie tego:

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

Dostałem informacje z tutaj , co bardzo dobrze wyjaśnia problem.

 11
Author: MicSim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-23 20:15:29

Czy masz wartość całkowitą w każdym wierszu, którą możesz przekazać jako ziarno do funkcji RAND?

Aby uzyskać liczbę całkowitą z zakresu od 1 do 14 wierzę, że to zadziała:

FLOOR( RAND(<yourseed>) * 14) + 1
 5
Author: CoderDennis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-25 17:38:41

Jeśli chcesz zachować swoje ziarno tak, aby generowało "te same" losowe DANE za każdym razem, możesz wykonać następujące czynności:

1. Utwórz widok, który zwróci select rand()

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. Utwórz UDF, który wybiera wartość z widoku.

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. Przed wybraniem danych należy uruchomić funkcję rand (), a następnie użyć UDF w instrukcji select.

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
 5
Author: Mitselplik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-11 17:15:34

Spróbuj użyć wartości seed w RAND (seedInt). RAND() uruchomi się tylko raz na każde polecenie, dlatego widzisz ten sam numer za każdym razem.

 4
Author: northpole,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-25 17:22:42

Jeśli nie potrzebujesz liczby całkowitej, ale dowolnego losowego unikalnego identyfikatora, możesz użyć newid()

SELECT table_name, newid() magic_number 
FROM information_schema.tables
 4
Author: Peter Cooper Jr.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-25 17:22:42
 4
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-11 13:47:00
select round(rand(checksum(newid()))*(10)+20,2)

Tutaj losowa liczba pojawi się pomiędzy 20 a 30. round da maksymalnie dwa miejsca po przecinku.

Jeśli chcesz liczby ujemne, możesz to zrobić za pomocą

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

Wtedy wartość min wyniesie -60, a max -50.

 3
Author: Tirthankar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-07 11:25:06

Select nevid()

Lub ewentualnie to select binary_checksum (nevid ())

 2
Author: Chris Klepeis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-25 17:22:23

Problem, który czasami mam z wybraną "odpowiedzią", polega na tym, że dystrybucja nie zawsze jest równa. Jeśli potrzebujesz bardzo równomiernego rozkładu losowych 1 - 14 między wieloma wierszami, możesz zrobić coś takiego (moja baza danych ma 511 tabel, więc to działa. Jeśli masz mniej wierszy niż liczba losowa, to nie działa dobrze):

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

Ten rodzaj robi przeciwieństwo normalnych losowych rozwiązań w tym sensie, że utrzymuje liczby uporządkowane i losuje inne kolumna.

Pamiętaj, że mam 511 tabel w bazie danych (co jest istotne tylko b / c wybieramy z information_schema). Jeśli wezmę poprzednie zapytanie i umieszczę je w tabeli temp #X, a następnie uruchom to zapytanie na danych wynikowych:

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

Otrzymuję ten wynik, pokazując mi, że moja losowa liczba jest bardzo równomiernie rozłożona między wiele rzędów:

Tutaj wpisz opis obrazka

 2
Author: Trevor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-04 02:17:14
select ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) as [Randomizer]

Zawsze działało dla mnie

 1
Author: bteague,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-24 20:31:29
    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;
 1
Author: ichak khoury,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-28 07:14:38