development

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

big-blog 2020. 3. 18. 08:08
반응형

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


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

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

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


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())

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


필요에 따라 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 페이지를 .


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

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

보다 정확한 성능의 실제 무작위 표본을 추출하는 가장 좋은 방법은 무작위로 행을 필터링하는 것입니다. SQL Server 온라인 설명서의 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를 최후의 수단으로 사용해야합니다.


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

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

이 링크는 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 의 전체 기사를 읽으십시오 .


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은 변수를 허용하지 않습니다)


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

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

최신 정보

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


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

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

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

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

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

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 */

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


이 시도:

SELECT TOP 10 Field1, ..., FieldN
FROM Table1
ORDER BY NEWID()

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

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

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

 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

위치에 주목


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

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


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

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

참고 URL : https://stackoverflow.com/questions/848872/select-n-random-rows-from-sql-server-table

반응형