Bir veritabanı tablosundan rastgele kayıt (T-SQL)


85

Bir sql server tablosundan rastgele bir kaydı almanın kısa ve öz bir yolu var mı?

Birim test verilerimi rastgele hale getirmek istiyorum, bu yüzden bir tablodan rastgele bir kimlik seçmenin basit bir yolunu arıyorum. İngilizcede seçim, "Kimliğin tablodaki en düşük kimlik ile tablodaki en yüksek kimlik arasında rastgele bir sayı olduğu tablodan bir kimlik seçin" olacaktır.

Sorguyu çalıştırmadan bunu yapmanın bir yolunu bulamıyorum, boş değer için test yapıp null ise yeniden çalıştırıyorum.

Fikirler?


burada birkaç yöntem var brettb.com/SQL_Help_Random_Numbers.asp
Mesh

2
Bu yaklaşımı benimsemek istediğinizden emin misiniz? Birim test verileri rastgele olmamalıdır - aslında, birim testini kaç defa yaparsanız yapın aynı sonuçları alacağınız garanti edilmelidir. Rasgele verilere sahip olmak, birim testinin bu temel ilkesini ihlal edebilir.
dizginler

@Mesh'in yukarıdaki bağlantısı artık aktif değil.
Robert Sievers

Yanıtlar:


146

Bir sql server tablosundan rastgele bir kaydı almanın kısa ve öz bir yolu var mı?

Evet

SELECT TOP 1 * FROM table ORDER BY NEWID()

Açıklama

NEWID()Her satır için A oluşturulur ve tablo buna göre sıralanır. İlk kayıt döndürülür (yani "en düşük" GUID'ye sahip kayıt).

Notlar

  1. GUID'ler, dördüncü sürümden bu yana sözde rastgele sayılar olarak oluşturulur:

    Sürüm 4 UUID'si, gerçek rasgele veya sözde rasgele sayılardan UUID'ler oluşturmak içindir.

    Algoritma aşağıdaki gibidir:

    • Clock_seq_hi_and_reserved'in en önemli iki bitini (bit 6 ve 7) sırasıyla sıfıra ve bire ayarlayın.
    • Time_hi_and_version alanının en önemli dört bitini (12'den 15'e kadar olan bitler) Bölüm 4.1.3'teki 4 bitlik sürüm numarasına ayarlayın.
    • Diğer tüm bitleri rastgele (veya sözde rastgele) seçilen değerlere ayarlayın.

    - Evrensel Olarak Benzersiz Bir Tanımlayıcı (UUID) URN Ad Alanı - RFC 4122

  2. Alternatif SELECT TOP 1 * FROM table ORDER BY RAND()düşündüğü gibi çalışmayacaktır. RAND()sorgu başına tek bir değer döndürür, bu nedenle tüm satırlar aynı değeri paylaşır.

  3. GUID değerleri sözde rasgele olsa da, daha zorlu uygulamalar için daha iyi bir PRNG'ye ihtiyacınız olacaktır.

  4. Tipik performans yaklaşık 1.000.000 satır için 10 saniyeden azdır - tabii ki sisteme bağlıdır. Bir dizine ulaşmanın imkansız olduğunu ve bu nedenle performansın nispeten sınırlı olacağını unutmayın.


Tam olarak aradığım şey. Yaptığımdan daha basit olduğunu hissettim.
Jeremy

1
NEWID'nin sözde rasgele değerler ürettiğini varsayıyorsunuz. Sıralı değerler üretme olasılığı yüksektir. NEWID yalnızca benzersiz değerler üretir. Ancak RAND, sözde rasgele değerler üretir.
Skizz

1.671.145 satır içeren yoğun şekilde indekslenmiş bir tabloda çalıştırıyorum ve geri dönmesi 7 saniye sürüyor. Tablo da oldukça uygundur - veritabanımızın neredeyse kalbidir, bu yüzden ilgilenilir.
Tom Ritter

@ ÂviewAnew. Bir dizine ulaşmayan (ve olamayan) bir seçimde 1,6 milyon satır ve 7 saniye fena değil.
Sklivvz

7
@Skizz, rand böyle çalışmıyor. SEÇİM'den önce TEK bir rastgele değer oluşturulur. Yani "İLK 10
RANDI

27

Daha büyük masalarda TABLESAMPLE, tüm tabloyu taramaktan kaçınmak için bunun için de kullanabilirsiniz .

SELECT  TOP 1 *
FROM YourTable
TABLESAMPLE (1000 ROWS)
ORDER BY NEWID()

ORDER BY NEWIDHala veri sayfasında ilk görünen sadece dönen satırları önlemek için gereklidir.

Kullanılacak sayının, tablonun boyutu ve tanımı için dikkatlice seçilmesi gerekir ve herhangi bir satır döndürülmezse mantığı yeniden denemeyi düşünebilirsiniz. Bunun arkasındaki matematik ve tekniğin neden küçük tablolar için uygun olmadığı burada tartışılmaktadır.


Bunu Microsoft'un web sitesinde buldum: Aşağıdaki koşullardan herhangi biri doğru olduğunda büyük bir tablodan bir örneği hızlı bir şekilde döndürmek için TABLESAMPLE'ı kullanabilirsiniz: Örnek, tek tek satırlar düzeyinde gerçekten rastgele bir örnek olmak zorunda değildir. Tablonun ayrı sayfalarındaki satırlar, aynı sayfadaki diğer satırlarla ilişkilendirilmez.
Mark Entingh

1
@MarkEntingh - TOP 1Aynı sayfadaki satırların ilişkilendirilmiş olup olmaması önemli değildir. Sadece birini seçiyorsun.
Martin Smith

9

Ayrıca MIN (Id) ve MAX (Id) arasında rastgele bir Kimlik elde etmek için yönteminizi deneyin ve ardından

SELECT TOP 1 * FROM table WHERE Id >= @yourrandomid

Her zaman size bir satır kazandırır.


2
-1, Bu yalnızca minimum ve maks. Arasında eksik kimlik olmadığında çalışır. Biri silinirse, aynı kimlik rastgele işlev tarafından üretilirse, sıfır kayıt geri alırsınız.
Neil N

6
@Neil, gerçekten değil - eğer eksik kimlikler varsa rastgele sayıdan daha büyük bir kimliği olan ilk satırı alacaktır. Buradaki sorun, her satırın çıkma olasılığının sabit olmamasıdır. Ancak yine de çoğu durumda bu yeterlidir.
Sklivvz

1
+1. Yeterince iyi olan farklı değerlere ulaşması gereken birim testi için - gerçek bir rastgele talep ediyorsanız, bu başka bir şeydir. Ancak OP bağlamında yeterince iyi olmalıdır.
TomTom

7

Büyük verileri seçmek istiyorsanız, bildiğim en iyi yol şudur:

SELECT * FROM Table1
WHERE (ABS(CAST(
    (BINARY_CHECKSUM
    (keycol1, NEWID())) as int))
    % 100) < 10

Kaynak: MSDN


Emin değilim ama gerçekten rasgele sayılar oluşturmak için RAND () yerine NEWID () kullanmanın, seçme işleminde NEWID () kullanmanın dezavantajları nedeniyle daha iyi olabileceğini düşünüyorum.
QMaster

Bu yöntemi tam kayıt sayısı yerine yüzde taban ile kullanmayı deniyorum, seçim aralığını genişletip TOP n ile sınırlandırarak yaptım, herhangi bir öneri var mı?
QMaster

Bu senaryoda başka bir sorun buldum, eğer grup kullanırsanız her zaman rastgele seçilen satırların aynı sırasını alırsınız, bu nedenle küçük tablolarda @skilvvz yaklaşımı en uygunudur.
QMaster

0

Denediğim yöntemleri geliştirmek istiyordum ve bu yazıya rastladım. Eski olduğunun farkındayım ama bu yöntem listelenmemiş. Test verilerini oluşturuyor ve uyguluyorum; bu, @st (iki karakter durumu) ile çağrılan bir SP'deki "adres" için yöntemi gösterir

Create Table ##TmpAddress (id Int Identity(1,1), street VarChar(50), city VarChar(50), st VarChar(2), zip VarChar(5))
Insert Into ##TmpAddress(street, city, st, zip)
Select street, city, st, zip 
From tbl_Address (NOLOCK)
Where st = @st


-- unseeded RAND() will return the same number when called in rapid succession so
-- here, I seed it with a guaranteed different number each time. @@ROWCOUNT is the count from the most recent table operation.

Set @csr = Ceiling(RAND(convert(varbinary, newid())) * @@ROWCOUNT)

Select street, city, st, Right(('00000' + ltrim(zip)),5) As zip
From ##tmpAddress (NOLOCK)
Where id = @csr

0

Tek tek satırların rastgele bir örneğini gerçekten istiyorsanız, sorgunuzu TABLESAMPLE kullanmak yerine satırları rastgele filtreleyecek şekilde değiştirin. Örneğin, aşağıdaki sorgu, Sales.SalesOrderDetail tablosunun satırlarının yaklaşık yüzde birini döndürmek için NEWID işlevini kullanır:

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)

SalesOrderID sütunu CHECKSUM ifadesine dahil edilir, böylece NEWID () satır başına örnekleme elde etmek için her satırda bir değerlendirme yapar. CAST (CHECKSUM (NEWID (), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int) ifadesi, 0 ile 1 arasında rastgele bir kayan değer olarak değerlendirilir. "

Kaynak: http://technet.microsoft.com/en-us/library/ms189108(v=sql.105).aspx

Bu, aşağıda daha ayrıntılı açıklanmıştır:

Bu nasıl çalışıyor? WHERE cümlesini ayıralım ve açıklayalım.

CHECKSUM işlevi, listedeki öğeler üzerinde bir sağlama toplamı hesaplıyor. NEWID () yeni bir rastgele GUID döndüren bir işlev olduğundan, SalesOrderID'nin gerekli olup olmadığı tartışılabilir, bu nedenle rastgele bir rakamı bir sabitle çarpmak her durumda rastgele sonuçlanmalıdır. Gerçekten de SalesOrderID'nin hariç tutulması bir fark yaratmıyor gibi görünüyor. Eğer istekli bir istatistikçiyseniz ve bunun dahil edilmesini haklı çıkarabiliyorsanız, lütfen aşağıdaki yorumlar bölümünü kullanın ve neden yanıldığımı bana bildirin!

CHECKSUM işlevi bir VARBINARY döndürür. İkilide (111111111 ...) 'e eşdeğer olan 0x7fffffff ile bitsel AND işlemi gerçekleştirmek, 0'lar ve 1'lerden oluşan rastgele bir dizenin etkin bir şekilde temsili olan ondalık bir değer verir. Eş-verimli 0x7fffffff ile bölmek, bu ondalık rakamı 0 ile 1 arasındaki bir rakama etkili bir şekilde normalleştirir. Ardından, her bir satırın nihai sonuç kümesine dahil edilmeyi hak edip etmediğine karar vermek için, 1 / x'lik bir eşik kullanılır (bu durumda, 0,01) burada x, örnek olarak alınacak verilerin yüzdesidir.

Kaynak: https://www.mssqltips.com/sqlservertip/3157/different-ways-to-get-random-data-for-sql-server-data-sampling

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.