SQL Server 테이블에서 n 개의 임의 행을 선택하십시오.


309

약 50,000 개의 행이있는 SQL Server 테이블이 있습니다. 그 행 중 약 5,000 행을 무작위로 선택하고 싶습니다. 복잡한 방법을 생각했습니다. "임의의 숫자"열이있는 임시 테이블을 생성하고 테이블을 복사하고 임시 테이블을 반복하고 각 행을로 업데이트 RAND()한 다음 난수 열이 < 0.1. 가능한 한 단일 진술로 더 간단한 방법을 찾고 있습니다.

이 기사NEWID()함수 사용을 제안 합니다. 유망한 것처럼 보이지만 특정 비율의 행을 안정적으로 선택할 수있는 방법을 알 수 없습니다.

아무도 전에 이것을 한 적이 있습니까? 어떤 아이디어?


3
MSDN에는 다음과 같은 많은 문제를 다루는 좋은 기사가 있습니다 . 큰 테이블에서 임의로 행 선택
KyleMit

답변:


387
select top 10 percent * from [yourtable] order by newid()

큰 테이블에 대한 "순수한 휴지통"주석에 대한 응답으로 성능을 향상시키기 위해 이와 같이 할 수 있습니다.

select  * from [yourtable] where [yourPk] in 
(select top 10 percent [yourPk] from [yourtable] order by newid())

이 비용은 값의 주요 스캔 + 결합 비용으로, 적은 비율로 선택하는 큰 테이블에서는 합리적이어야합니다.


1
나는 그가 언급 한 기사를 사용하는 것 보다이 접근법을 훨씬 더 좋아합니다.
JoshBerke

14
newid ()는 적어도 rand ()만큼 좋지는 않지만 실제로는 의사 난수 생성기가 아니라는 것을 명심하는 것이 좋습니다. 그러나 막연하게 무작위 샘플이 필요하고 수학적 특성에 관심이 없다면 충분할 것입니다. 그렇지 않으면 당신이 필요합니다 : stackoverflow.com/questions/249301/…
user12861

1
음,이게 분명하다면 미안 해요 .. 무슨 [yourPk]말입니까? 편집 : Nvm, 알아 냈습니다 ... 기본 키. Durrr
Snailer

4
newid-guid는 독특하지만 무작위는 아닙니다 .. 잘못된 접근법
Brans Ds

2
예를 들어 1 백만 개가 넘는 newid()정렬 예상 I / O 비용이 많은 행을 사용 하면 성능이 향상됩니다.
aadi1295

81

필요에 따라 TABLESAMPLE거의 임의적이고 더 나은 성능을 얻을 수 있습니다. MS SQL Server 2005 이상에서 사용할 수 있습니다.

TABLESAMPLE 임의의 행 대신 임의의 페이지에서 데이터를 반환하므로 반환하지 않는 데이터는 검색하지 않습니다.

매우 큰 테이블에서 테스트했습니다

select top 1 percent * from [tablename] order by newid()

20 분 이상 걸렸습니다.

select * from [tablename] tablesample(1 percent)

2 분이 걸렸습니다.

작은 샘플에서는 성능이 향상되는 TABLESAMPLE반면, 그렇지 않은 경우에는 성능이 향상됩니다 newid().

이것은 newid()방법 만큼 무작위 적이지는 않지만 적절한 샘플링을 제공 한다는 점을 명심하십시오 .

참고 항목 MSDN 페이지를 .


7
대단히 짧은 결과를 tablesampling 아래 롭 Boek에 의해 지적, 따라서 얻을 수있는 좋은 방법 아니므로 작은 임의 결과 수
오스카 Austegard

newid ()가 [tablename]의 열이 아니므로 newid ()로 [tablename] 순서에서 상위 1 % *를 선택하십시오. SQL Server가 각 행에 내부적으로 newid () 열을 추가 한 다음 정렬합니까?
FrenkyB

매우 큰 테이블에서 복잡한 쿼리를 수행 할 때 테이블 샘플이 가장 적합했습니다. 틀림없이 빠르다는 데는 의문의 여지가 없습니다. 이 작업을 여러 번 실행했을 때 반환되는 레코드 수에 차이가 있었지만 모두 허용 가능한 오류 범위 내에있었습니다.
jessier3

38

newid () / order by는 작동하지만 모든 행에 대해 id를 생성 한 다음 정렬해야하기 때문에 큰 결과 집합에는 비용이 많이 듭니다.

TABLESAMPLE ()은 성능 관점에서는 좋지만 결과가 뭉치 게됩니다 (페이지의 모든 행이 반환 됨).

더 나은 성능의 실제 무작위 샘플을 얻으려면 가장 좋은 방법은 행을 무작위로 필터링하는 것입니다. SQLS 온라인 설명서의 TABLESAMPLE을 사용하여 결과 집합 제한 에서 다음 코드 샘플을 찾았습니다 .

개별 행의 임의 샘플을 실제로 원한다면 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 사이의 임의의 부동 소수점 값으로 평가됩니다.

1,000,000 개의 행이있는 테이블에 대해 실행할 때 내 결과는 다음과 같습니다.

SET STATISTICS TIME ON
SET STATISTICS IO ON

/* newid()
   rows returned: 10000
   logical reads: 3359
   CPU time: 3312 ms
   elapsed time = 3359 ms
*/
SELECT TOP 1 PERCENT Number
FROM Numbers
ORDER BY newid()

/* TABLESAMPLE
   rows returned: 9269 (varies)
   logical reads: 32
   CPU time: 0 ms
   elapsed time: 5 ms
*/
SELECT Number
FROM Numbers
TABLESAMPLE (1 PERCENT)

/* Filter
   rows returned: 9994 (varies)
   logical reads: 3359
   CPU time: 641 ms
   elapsed time: 627 ms
*/    
SELECT Number
FROM Numbers
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float) 
              / CAST (0x7fffffff AS int)

SET STATISTICS IO OFF
SET STATISTICS TIME OFF

TABLESAMPLE을 사용하여 벗어날 수 있다면 최상의 성능을 제공합니다. 그렇지 않으면 newid () / filter 메소드를 사용하십시오. 결과 세트가 큰 경우 newid () / order by를 최후의 수단으로 사용해야합니다.


이 기사도 보았고 코드에서 시도해 보았습니다. NewID()내가 싫어하는 행당 대신 한 번만 평가되는 것 같습니다.
Andrew Mao

23

MSDN 의 큰 테이블 에서 무작위로 행 선택하기 대규모 성능 문제를 해결하는 간단하고 잘 설명 된 솔루션이 있습니다.

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

매우 흥미로운. 기사를 읽은 후 왜 RAND()각 행에 대해 동일한 값을 반환하지 않는지 이해 하지 못합니다 ( BINARY_CHECKSUM()논리를 잃을 것입니다). SELECT 절의 일부가 아닌 다른 함수 내에서 호출되기 때문입니까?
존 M 간트

이 쿼리는 1 초 이내에 6MM 행이있는 테이블에서 실행되었습니다.
Mark Melville

2
35 개의 항목이있는 테이블 에서이 쿼리를 실행했으며 결과 집합에 두 개가 매우 자주있었습니다. 이것은 위의 문제 rand()또는 조합 문제 일 수 있지만 그 이유 때문에이 솔루션을 사용하지 않았습니다. 또한 결과 수는 1에서 5까지 다양하므로 일부 시나리오에서는 허용되지 않을 수도 있습니다.
Oliver

RAND ()가 모든 행에 대해 동일한 값을 반환하지 않습니까?
인삼차

RAND()모든 행에 대해 동일한 값을 반환 하므로이 솔루션이 빠릅니다. 그러나 매우 가까운 이진 체크섬이있는 행은 비슷한 체크섬 결과를 생성 할 위험이 높으며 RAND()크기가 작을 때 덩어리 가 발생 합니다. 예를 들어 (ABS(CAST((BINARY_CHECKSUM(111,null,null) * 0.1) as int))) % 100== SELECT (ABS(CAST((BINARY_CHECKSUM(113,null,null) * 0.1) as int))) % 100입니다. 데이터에이 문제가 발생하면 BINARY_CHECKSUM9923을 곱 하십시오.
Brian

12

이 링크는 Orderby (NEWID ())와 1, 7, 1,300 만 행의 테이블에 대한 다른 메소드를 흥미롭게 비교합니다.

토론 그룹에서 임의 행을 선택하는 방법에 대한 질문이있을 때 종종 NEWID 쿼리가 제안됩니다. 간단하고 작은 테이블에 매우 효과적입니다.

SELECT TOP 10 PERCENT *
  FROM Table1
  ORDER BY NEWID()

그러나 NEWID 쿼리는 큰 테이블에 사용할 때 큰 단점이 있습니다. ORDER BY 절은 테이블의 모든 행이 tempdb 데이터베이스에 복사되어 정렬됩니다. 이로 인해 두 가지 문제가 발생합니다.

  1. 정렬 작업에는 일반적으로 관련 비용이 높습니다. 정렬은 많은 디스크 I / O를 사용할 수 있으며 오랫동안 실행될 수 있습니다.
  2. 최악의 경우 tempdb에 공간이 부족할 수 있습니다. 최상의 시나리오에서 tempdb는 수동 축소 명령 없이는 다시 확보 할 수없는 디스크 공간을 많이 차지할 수 있습니다.

필요한 것은 tempdb를 사용하지 않고 테이블이 커질수록 훨씬 느려지지 않는 행을 무작위로 선택하는 방법입니다. 이를 수행하는 방법에 대한 새로운 아이디어는 다음과 같습니다.

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

이 쿼리의 기본 개념은 테이블의 각 행에 대해 0에서 99 사이의 난수를 생성 한 다음 난수가 지정된 백분율 값보다 작은 모든 행을 선택한다는 것입니다. 이 예에서는 약 10 %의 행을 임의로 선택하려고합니다. 따라서 난수가 10보다 작은 모든 행을 선택합니다.

MSDN 의 전체 기사를 읽으십시오 .


2
안녕 Deumber, 좋은 발견, 당신은 링크 만 답변이 삭제 될 가능성이 있기 때문에 그것을 육체 수 있습니다.
bummi

1
@bummi 나는 링크 전용 답변을 피하기 위해 그것을 바꿨다 :)
QMaster

이것이 가장 좋은 대답입니다. 'ORDER BY NEWID ()는'대부분의 경우 (작은 테이블)에서 작동하지만, 분명히 refrenced 링크의 벤치 마크 표시로 테이블이 성장함에 따라 그 뒤에 폭포
페드 람 bashiri

10

OP와 달리 특정 수의 레코드가 필요하고 (CHECKSUM 접근 방식을 어렵게 함) TABLESAMPLE 자체가 제공하는 것보다 더 임의의 샘플을 원하고 CHECKSUM보다 더 빠른 속도를 원하는 경우, 다음과 같은 TABLESAMPLE 및 NEWID () 메소드

DECLARE @sampleCount int = 50
SET STATISTICS TIME ON

SELECT TOP (@sampleCount) * 
FROM [yourtable] TABLESAMPLE(10 PERCENT)
ORDER BY NEWID()

SET STATISTICS TIME OFF

제 경우에는 이것이 무작위성 (실제로 아는 것은 아닙니다)과 속도 사이의 가장 직접적인 타협입니다. TABLESAMPLE 백분율 (또는 행)을 적절하게 변경하십시오. 백분율이 높을수록 샘플이 더 무작위 적이지만 속도가 선형으로 떨어질 것으로 예상합니다. (TableSAMPLE은 변수를 허용하지 않습니다)


9

임의의 숫자로 테이블을 정렬하고를 사용하여 처음 5,000 행을 얻으십시오 TOP.

SELECT TOP 5000 * FROM [Table] ORDER BY newid();

최신 정보

그냥 시도하고 newid()전화가 충분합니다-모든 캐스트와 수학이 필요하지 않습니다.


10
'모든 캐스트와 모든 수학'이 사용되는 이유는 더 나은 성능을위한 것입니다.
hkf

6

이것은 초기 시드 아이디어와 체크섬의 조합으로 NEWID () 비용없이 올바르게 임의의 결과를 제공하는 것으로 보입니다.

SELECT TOP [number] 
FROM table_name
ORDER BY RAND(CHECKSUM(*) * RAND())

3

MySQL에서는 다음을 수행 할 수 있습니다.

SELECT `PRIMARY_KEY`, rand() FROM table ORDER BY rand() LIMIT 5000;

3
작동하지 않습니다. select 문은 원자 적이므로 하나의 난수 만 가져 와서 각 행마다 복제합니다. 변경하려면 각 행에서 다시 시드해야합니다.
Tom H

4
음 ... 벤더의 차이를 좋아합니다. 선택은 MySQL에서 원자 적이지만 다른 방식으로 가정합니다. 이것은 MySQL에서 작동합니다.
Jeff Ferland

2

아직 답변 에서이 변형을 보지 못했습니다. 매번 동일한 행 집합을 선택하기 위해 초기 시드가 주어지면 추가 제약 조건이있었습니다.

MS SQL의 경우 :

최소 예 :

select top 10 percent *
from table_name
order by rand(checksum(*))

정규화 된 실행 시간 : 1.00

NewId () 예제 :

select top 10 percent *
from table_name
order by newid()

정규화 된 실행 시간 : 1.02

NewId() ~보다 현저히 느리다 rand(checksum(*)) 므로 큰 레코드 세트에 사용하지 않을 수 있습니다.

초기 종자를 사용한 선택 :

declare @seed int
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */

select top 10 percent *
from table_name
order by rand(checksum(*) % @seed) /* any other math function here */

시드가 지정된 동일한 세트를 선택 해야하는 경우 작동하는 것 같습니다.


RAND ()에 대해 특수 @seed를 사용하면 어떤 이점이 있습니까?
QMaster

절대적으로, 당신은 시드 매개 변수를 사용하고 날짜 매개 변수로 채 웁니다 .RAND () 함수는 전체 시간 값을 사용하는 것을 제외하고 동일하게 수행합니다 .RAND () 위의 시드와 같은 편리한 생성 매개 변수를 사용하는 것이 유리하다는 것을 알고 싶습니다.
QMaster

아!. 이것은 프로젝트의 요구 사항이었습니다. 결정적인 방식으로 n 개의 임의 행 목록을 생성해야했습니다. 기본적으로 리더십은 행을 선택하고 처리하기 며칠 전에 어떤 "무작위"행을 선택해야하는지 알고 싶었습니다. 연도 / 월을 기준으로 시드 값을 작성하면 해당 연도에 대한 호출이 동일한 "임의"목록을 반환하도록 보장 할 수 있습니다. 나는 이상했다. 아마 더 좋은 방법이 있었지만 효과가 있었지만 ...
klyd

HAHA :) 알지만 무작위로 선택된 레코드의 일반적인 의미는 다른 실행 쿼리에서 동일한 레코드가 아니라고 생각합니다.
QMaster


0

newid ()를 where 절에서 사용할 수 없으므로이 솔루션에는 내부 쿼리가 필요합니다.

SELECT *
FROM (
    SELECT *, ABS(CHECKSUM(NEWID())) AS Rnd
    FROM MyTable
) vw
WHERE Rnd % 100 < 10        --10%

0

하위 쿼리에서 사용하고 하위 쿼리에서 동일한 행을 반환했습니다.

 SELECT  ID ,
            ( SELECT TOP 1
                        ImageURL
              FROM      SubTable 
              ORDER BY  NEWID()
            ) AS ImageURL,
            GETUTCDATE() ,
            1
    FROM    Mytable

그런 다음 부모 테이블 변수를 포함하여 해결했습니다.

SELECT  ID ,
            ( SELECT TOP 1
                        ImageURL
              FROM      SubTable 
              Where Mytable.ID>0
              ORDER BY  NEWID()
            ) AS ImageURL,
            GETUTCDATE() ,
            1
    FROM    Mytable

위치에 주목


0

사용중인 서버 측 처리 언어 (예 : PHP, .net 등)는 지정되어 있지 않지만 PHP 인 경우 필요한 수 (또는 모든 레코드)를 잡고 쿼리에서 무작위 화하는 대신 PHP의 셔플 기능을 사용하십시오. .net에 동등한 기능이 있는지는 모르겠지만 .net을 사용하는 경우 사용합니다.

ORDER BY RAND ()는 관련된 레코드 수에 따라 성능이 저하 될 수 있습니다.


나는 당시에 이것을 사용하고 있었던 것을 정확하게 기억하지 못하지만 아마도 C #, 아마도 서버 또는 클라이언트 응용 프로그램에서 일하고 있었는지 확실하지 않습니다. C #에는 PHP의 셔플 afaik과 직접 비교할만한 것이 없지만 Select 작업 내에서 Random 객체의 함수를 적용하고 결과를 정렬 한 다음 상위 10 %를 차지하여 수행 할 수 있습니다. 그러나 DB 서버의 디스크에서 전체 테이블을 읽고 네트워크를 통해 전송해야하며 해당 데이터의 90 % 만 폐기해야합니다. DB에서 직접 처리하는 것이 거의 확실합니다.
John M Gant

-2

이것은 나를 위해 작동합니다 :

SELECT * FROM table_name
ORDER BY RANDOM()
LIMIT [number]

9
@ user537824, SQL Server에서 시도 했습니까? RANDOM은 기능이 아니며 LIMIT는 키워드가 아닙니다. 수행중인 작업에 대한 SQL Server 구문은 select top 10 percent from table_name order by rand()이지만 rand ()는 모든 행에서 동일한 값을 반환하기 때문에 작동하지 않습니다.
John M Gant
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.