데이터베이스 테이블의 임의 레코드 (T-SQL)


85

SQL Server 테이블에서 임의의 레코드를 검색하는 간결한 방법이 있습니까?

내 단위 테스트 데이터를 무작위로 만들고 싶으므로 테이블에서 임의의 ID를 선택하는 간단한 방법을 찾고 있습니다. 영어의 경우 선택은 "ID가 테이블의 가장 낮은 ID와 테이블의 가장 높은 ID 사이의 임의의 숫자 인 테이블에서 하나의 ID를 선택하십시오."입니다.

쿼리를 실행하고 null 값을 테스트 한 다음 null 인 경우 다시 실행하지 않고는 방법을 알아낼 수 없습니다.

아이디어?


여기에 몇 가지 방법이 있습니다. brettb.com/SQL_Help_Random_Numbers.asp
Mesh

2
이 접근 방식을 사용 하시겠습니까? 단위 테스트 데이터는 무작위가 아니어야합니다. 실제로 단위 테스트를 몇 번 실행하더라도 동일한 결과를 얻을 수 있습니다. 임의의 데이터가 있으면 단위 테스트의 기본 원칙을 위반할 수 있습니다.
고삐

@Mesh의 위 링크는 더 이상 활성화되지 않습니다.
Robert Sievers

답변:


145

SQL Server 테이블에서 임의의 레코드를 검색하는 간결한 방법이 있습니까?

SELECT TOP 1 * FROM table ORDER BY NEWID()

설명

A NEWID()는 각 행에 대해 생성되고 테이블은이를 기준으로 정렬됩니다. 첫 번째 레코드 (즉, GUID가 "가장 낮은"레코드)가 반환됩니다.

메모

  1. GUID는 버전 4부터 의사 난수로 생성됩니다.

    버전 4 UUID는 진정한 난수 또는 의사 난수에서 UUID를 생성하기위한 것입니다.

    알고리즘은 다음과 같습니다.

    • clock_seq_hi_and_reserved의 최상위 비트 2 개 (비트 6 및 7)를 각각 0과 1로 설정합니다.
    • time_hi_and_version 필드의 최상위 4 비트 (비트 12 ~ 15)를 섹션 4.1.3의 4 비트 버전 번호로 설정합니다.
    • 다른 모든 비트를 무작위로 (또는 의사 무작위로) 선택한 값으로 설정합니다.

    UUID (Universally Unique IDentifier) ​​URN 네임 스페이스-RFC 4122

  2. 대안 SELECT TOP 1 * FROM table ORDER BY RAND()은 생각대로 작동하지 않습니다. RAND()쿼리 당 하나의 단일 값을 반환하므로 모든 행이 동일한 값을 공유합니다.

  3. GUID 값은 의사 난수이지만 더 까다로운 응용 프로그램에는 더 나은 PRNG가 필요합니다.

  4. 일반적인 성능은 약 1,000,000 행에 대해 10 초 미만이며 물론 시스템에 따라 다릅니다. 인덱스에 도달하는 것은 불가능하므로 성능이 상대적으로 제한됩니다.


내가 찾던 바로 그것. 나는 그것이 내가 만드는 것보다 더 간단하다고 느꼈다.
제레미

1
NEWID가 의사 난수 값을 생성한다고 가정합니다. 순차 값을 생성 할 가능성이 높습니다. NEWID는 단지 고유 한 값을 생성합니다. 그러나 RAND는 의사 난수 값을 생성합니다.
Skizz

1,671,145 개의 행이있는 인덱스가 많은 테이블에서 실행 중이며 반환하는 데 7 초가 걸립니다. 테이블도 매우 최적화되어 있습니다. 사실상 데이터베이스의 핵심이므로 처리됩니다.
Tom Ritter

@ ÂviewAnew. 인덱스에 맞지 않는 (그리고 할 수없는) select에서 160 만 행과 7 초는 나쁘지 않습니다.
Sklivvz

7
@Skizz, rand는 그렇게 작동하지 않습니다. SELECT 전에 단일 임의 값이 생성됩니다. 따라서 "SELECT TOP 10 RAND () ..."를 시도하면 항상 동일한 값을 얻습니다
Sklivvz

27

더 큰 테이블 TABLESAMPLE에서는 전체 테이블을 스캔하지 않도록이를 위해 사용할 수도 있습니다 .

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

ORDER BY NEWID여전히 데이터 페이지에서 먼저 표시 만 반환 행을 방지하기 위해 필요합니다.

사용할 수는 테이블의 크기와 정의에 따라 신중하게 선택해야하며 행이 반환되지 않으면 재시도 논리를 고려할 수 있습니다. 이 기술의 배후에있는 수학 및 기술이 작은 테이블에 적합하지 않은 이유가 여기에서 설명됩니다.


다음 조건 중 하나에 해당하면 TABLESAMPLE을 사용하여 큰 테이블에서 샘플을 빠르게 반환 할 수 있습니다. 샘플은 개별 행 수준에서 실제로 무작위 샘플 일 필요는 없습니다. 테이블의 개별 페이지에있는 행은 동일한 페이지에있는 다른 행과 상관되지 않습니다.
Mark Entingh

1
@MarkEntingh- TOP 1같은 페이지의 행이 상관 관계가 있는지 여부는 중요하지 않습니다. 당신은 그들 중 하나만 선택합니다.
Martin Smith

9

또한 MIN (Id)과 MAX (Id) 사이의 임의의 ID를 얻는 방법을 시도한 다음

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

항상 한 행을 얻습니다.


2
-1, 이것은 최소와 최대 사이에 누락 된 ID가없는 경우에만 작동합니다. 하나가 삭제되면 동일한 ID가 임의 함수에 의해 생성되고 0 개의 레코드가 다시 반환됩니다.
Neil N

6
@Neil, 실제로는 아닙니다. ID가 누락 된 경우 Id가 난수보다 큰 첫 번째 행을 가져옵니다. 여기서 문제는 각 행이 나올 확률이 일정하지 않다는 것입니다. 그러나 이것은 대부분의 경우에 충분합니다.
Sklivvz 2011 년

1
+1. 충분히 좋은 다른 값에 도달해야하는 단위 테스트의 경우-실제 랜덤이 필요한 경우 이것은 다른 것입니다. 그러나 OP 컨텍스트에서는 충분해야합니다.
TomTom

7

대용량 데이터를 선택하려는 경우 내가 아는 가장 좋은 방법은 다음과 같습니다.

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

출처 : MSDN


확실하지 않지만 NEWID () 대신 RAND ()를 사용하여 진정한 난수를 생성하는 것이 선택 프로세스에서 NEWID ()를 사용하는 단점 때문에 더 좋을 수 있다고 생각합니다.
qmaster에

이 방법을 퍼센트 기준보다 정확한 레코드 수로 사용하려고 시도합니다. 선택 범위를 확장하고 TOP n으로 제한하여 수행했습니다. 제안 사항이 있습니까?
qmaster에

이 시나리오에서 또 다른 문제를 발견했습니다. group by를 사용하면 항상 무작위로 선택된 행의 순서가 동일하므로 작은 테이블에서 @skilvvz 접근 방식이 가장 적절합니다.
qmaster에

0

나는 내가 시도한 방법을 개선하기 위해이 게시물을 보았습니다. 나는 그것이 오래되었다는 것을 알고 있지만이 방법은 나열되지 않았습니다. 테스트 데이터를 만들고 적용하고 있습니다. 이것은 @st (두 문자 상태)로 호출 된 SP에서 "주소"에 대한 방법을 보여줍니다.

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

개별 행의 무작위 샘플을 정말로 원한다면 TABLESAMPLE을 사용하는 대신 행을 무작위로 필터링하도록 쿼리를 수정하십시오. 예를 들어 다음 쿼리는 NEWID 함수를 사용하여 Sales.SalesOrderDetail 테이블 행의 약 1 %를 반환합니다.

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

SalesOrderID 열은 CHECKSUM 식에 포함되므로 NEWID ()는 행당 한 번씩 평가하여 행 단위로 샘플링을 수행합니다. 표현식 CAST (CHECKSUM (NEWID (), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int)는 0과 1 사이의 임의의 float 값으로 평가됩니다. "

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

이에 대해서는 아래에서 자세히 설명합니다.

어떻게 작동합니까? WHERE 절을 분리하여 설명해 봅시다.

CHECKSUM 함수는 목록의 항목에 대한 체크섬을 계산합니다. NEWID ()는 새로운 임의의 GUID를 반환하는 함수이므로 SalesOrderID가 필요한지 여부는 논쟁의 여지가 있습니다. 따라서 임의의 숫자에 상수를 곱하면 어떤 경우에도 임의의 값이 생성됩니다. 실제로 SalesOrderID를 제외해도 아무런 차이가없는 것 같습니다. 당신이 예리한 통계 학자이고 이것의 포함을 정당화 할 수 있다면, 아래 코멘트 섹션을 사용하고 왜 내가 틀렸는 지 알려주세요!

CHECKSUM 함수는 VARBINARY를 반환합니다. 바이너리에서 (111111111 ...)에 해당하는 0x7fffffff로 비트 AND 연산을 수행하면 사실상 0과 1의 임의 문자열을 나타내는 10 진수 값이 생성됩니다. 계수 0x7fffffff로 나누면이 10 진수 숫자를 0과 1 사이의 숫자로 효과적으로 정규화합니다. 그런 다음 각 행이 최종 결과 집합에 포함될 수 있는지 여부를 결정하기 위해 임계 값 1 / x (이 경우 0.01)가 사용됩니다. x는 샘플로 검색 할 데이터의 백분율입니다.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.