T-SQL

Vygenerování náhodného čísla pomocí NEWID()

Generování náhodných čísel v SQL Serveru může být čas od času pěkný oříšek, zejména pokud začínající vývojáři sáhnout po funkci RAND(). První, na co narazí, je, že tato funkce generuje stále stejné hodnoty, pokud ji chceme použít v dotazu s více řádky, jak je hezky vidět v tomto příkladu:

SELECT TOP(10)
	[object_id], RAND() AS 'RAND'
FROM sys.[objects]
GO

A kdyžmáme dost odvahy a použijeme funkci s parametrem RAND(SEED), můžeme se těšit na téměř záhadné výsledky. Zkuste si následující kód spustit v Management Studiu několikrát po sobě:

SELECT RAND(100) RND
UNION ALL
SELECT RAND()
GO

SELECT RAND() RND
UNION ALL
SELECT RAND()
GO

SELECT RAND() RND
UNION ALL
SELECT RAND()
GO

Každá funkce RAND() ve skriptu nám vygeneruje novou hodnotu, ale pokud si pustíme celý skript opakovaně, zjistíme, že nám všechny funkce vrací stále své původní hodnoty z prvního běhu. Celé je to způsobeno použitím SEED parametru u první RAND() funkce, který “zamrazí” generování nových hodnot v rámci daného připojení. SEED totiž generuje pro stejného hodnoty stále stejná “pseudonáhodná” čísla. Debugovat něco podobného může vydat opravdu za hodiny hledání jehly v kupce sena.

Mé doporučení zní jednoznačně: pokud potřebujeme opravdu náhodné hodnoty, je nejlepší sáhnout po funkci NEWID() a patřičným způsobem s jejím výsledkem pracovat.

Základní rozdíl mezi RAND() a NEWID() je vidět, pokusíme-li se vygenerovat více náhodných hodnot v rámci jednoho dotazu:

SELECT TOP(10)
	[o].[object_id], 
	RAND() AS 'RAND',
	ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS 'NEWID'
FROM sys.[objects] [o]
GO

Vidíme, že RAND() nám vrátil stejnou hodnotu pro všechny řádky, zatímco NEWID() krásně generuje náhodná čísla pro každý řádek.

Vhodným přetypováním NEWID() si můžeme nechat vytvořit náhodná čísla libovolné velikosti:

SELECT 
	ABS(CAST(CAST(NEWID() AS VARBINARY) AS TINYINT)) AS 'TINYINT',
	ABS(CAST(CAST(NEWID() AS VARBINARY) AS SMALLINT)) AS 'SMALLINT',
	ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS 'INT',
	ABS(CAST(CAST(NEWID() AS VARBINARY) AS BIGINT)) AS 'BIGINT'
GO

Ale jak je u SQL Serveru zvykem: It depends! Takže i NEWID() nás někdy může nemile překvapit: Change in Query Plan Can Affect Query Result Set (Table Spool)

 

Leave a Reply

Your email address will not be published. Required fields are marked *