문제

순수 SQL에서 무작위 행(또는 가능한 한 무작위에 가까운 행)을 요청하려면 어떻게 해야 합니까?

도움이 되었습니까?

해결책

이 게시물을 참조하세요: 데이터베이스 테이블에서 임의의 행을 선택하는 SQL.MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2 및 Oracle에서 이 작업을 수행하는 방법을 살펴봅니다(다음은 해당 링크에서 복사됨).

MySQL을 사용하여 임의의 행을 선택합니다.

SELECT column FROM table
ORDER BY RAND()
LIMIT 1

PostgreSQL을 사용하여 임의의 행을 선택합니다.

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

Microsoft SQL Server에서 임의의 행을 선택하십시오.

SELECT TOP 1 column FROM table
ORDER BY NEWID()

IBM DB2를 사용하여 임의의 행 선택

SELECT column, RAND() as IDX 
FROM table 
ORDER BY IDX FETCH FIRST 1 ROWS ONLY

Oracle을 사용하여 무작위 레코드를 선택하십시오.

SELECT column FROM
( SELECT column FROM table
ORDER BY dbms_random.value )
WHERE rownum = 1

다른 팁

Jeremies와 같은 솔루션:

SELECT * FROM table ORDER BY RAND() LIMIT 1

작동하지만 모든 테이블에 대한 순차적 스캔이 필요합니다(각 행과 관련된 임의의 값을 계산해야 하기 때문에 가장 작은 값을 결정할 수 있기 때문에). 중간 크기 테이블의 경우에도 상당히 느릴 수 있습니다.제가 권장하는 것은 일종의 인덱스 숫자 열(많은 테이블이 기본 키로 이를 가짐)을 사용한 후 다음과 같이 작성하는 것입니다.

SELECT * FROM table WHERE num_value >= RAND() * 
    ( SELECT MAX (num_value ) FROM table ) 
ORDER BY num_value LIMIT 1

이는 테이블 크기에 관계없이 로그 시간으로 작동합니다. num_value 색인되어 있습니다.한 가지 주의사항:이는 다음과 같이 가정합니다. num_value 범위 내에서 균등하게 분포됩니다. 0..MAX(num_value).데이터 세트가 이 가정에서 크게 벗어나면 왜곡된 결과를 얻게 됩니다(일부 행은 다른 행보다 더 자주 나타납니다).

이것이 얼마나 효율적인지는 모르겠지만 이전에 사용해본 적이 있습니다.

SELECT TOP 1 * FROM MyTable ORDER BY newid()

GUID는 매우 무작위이므로 순서는 임의의 행을 얻는다는 것을 의미합니다.

ORDER BY NEWID()

걸립니다 7.4 milliseconds

WHERE num_value >= RAND() * (SELECT MAX(num_value) FROM table)

걸립니다 0.0065 milliseconds!

나는 반드시 후자의 방법을 택하겠다.

어떤 서버를 사용하고 있는지 말하지 않았습니다.이전 버전의 SQL Server에서는 다음을 사용할 수 있습니다.

select top 1 * from mytable order by newid()

SQL Server 2005 이상에서는 다음을 사용할 수 있습니다. TABLESAMPLE 반복 가능한 무작위 샘플을 얻으려면 다음을 수행하십시오.

SELECT FirstName, LastName
FROM Contact 
TABLESAMPLE (1 ROWS) ;

SQL 서버의 경우

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

TABLESAMPLE()은 성능 측면에서는 좋지만 결과가 덩어리지게 됩니다(페이지의 모든 행이 반환됨).

더 나은 성능의 실제 무작위 샘플을 위한 가장 좋은 방법은 행을 무작위로 필터링하는 것입니다.SQL Server 온라인 설명서 문서에서 다음 코드 샘플을 찾았습니다. TABLESAMPLE을 사용하여 결과 세트 제한:

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

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

SalesOrderID 열은 체크섬 표현식에 포함되므로 NewID ()가 행당 한 번 평가하여 샘플링을 수행합니다.표현식 캐스트 (checksum (newId (), salesOrderId) & 0x7ffffff as float / cast (int로서 0x7ffffff)는 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는 결과 집합이 큰 경우 최후의 수단으로 사용해야 합니다.

가능하다면 저장된 문을 사용하여 RND()에 대한 두 인덱스의 비효율성과 레코드 번호 필드 생성을 방지하세요.

PREPARE RandomRecord FROM "SELECT * FROM table LIMIT ?,1";
SET @n=FLOOR(RAND()*(SELECT COUNT(*) FROM table));
EXECUTE RandomRecord USING @n;

가장 좋은 방법은 해당 목적을 위해 새 열에 임의의 값을 넣고 다음과 같은 것을 사용하는 것입니다(의사 코드 + SQL).

randomNo = random()
execSql("SELECT TOP 1 * FROM MyTable WHERE MyTable.Randomness > $randomNo")

이것이 MediaWiki 코드에서 사용하는 솔루션입니다.물론 더 작은 값에 대한 편견이 있지만 행을 가져오지 않을 때 임의의 값을 0으로 감싸는 것으로 충분하다는 것을 발견했습니다.

newid() 솔루션은 각 행에 새로운 guid를 할당할 수 있도록 전체 테이블 스캔이 필요할 수 있으며, 이는 성능이 훨씬 떨어집니다.

rand() 솔루션이 전혀 작동하지 않을 수 있습니다(예:MSSQL 사용) 함수는 한 번만 평가되기 때문입니다. 모든 행에는 동일한 "임의" 번호가 할당됩니다.

SQL Server 2005 및 2008의 경우 개별 행의 무작위 샘플을 원하는 경우( 온라인 도서):

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

대신해서 권장되지 않으므로 RAND() 사용, 간단히 최대 ID(=Max)를 얻을 수 있습니다.

SELECT MAX(ID) FROM TABLE;

1..Max(=My_Generated_Random) 사이에서 무작위를 얻습니다.

My_Generated_Random = rand_in_your_programming_lang_function(1..Max);

그런 다음 이 SQL을 실행합니다.

SELECT ID FROM TABLE WHERE ID >= My_Generated_Random ORDER BY ID LIMIT 1

ID가 선택한 값과 같거나 높은 행을 확인합니다.테이블에서 아래 행을 찾아서 My_Generated_Random과 같거나 낮은 ID를 얻은 다음 쿼리를 다음과 같이 수정할 수도 있습니다.

SELECT ID FROM TABLE WHERE ID <= My_Generated_Random ORDER BY ID DESC LIMIT 1

@cnu의 답변에 대한 @BillKarwin의 의견에서 지적했듯이 ...

LIMIT와 결합할 때 실제 행을 직접 정렬하는 것보다 무작위 순서로 JOIN하는 것이 훨씬 더 나은 성능을 발휘한다는 것을 발견했습니다(적어도 PostgreSQL 9.1에서는).예를 들어

SELECT * FROM tbl_post AS t
JOIN ...
JOIN ( SELECT id, CAST(-2147483648 * RANDOM() AS integer) AS rand
       FROM tbl_post
       WHERE create_time >= 1349928000
     ) r ON r.id = t.id
WHERE create_time >= 1349928000 AND ...
ORDER BY r.rand
LIMIT 100

'r'이 조인된 복잡한 쿼리에서 가능한 모든 키 값에 대해 'rand' 값을 생성하는지 확인하세요. 단, 가능한 경우 'r' 행 수를 제한하세요.

정수형 CAST는 정수 및 단정밀도 부동 유형에 대한 특정 정렬 최적화 기능이 있는 PostgreSQL 9.2에 특히 유용합니다.

여기에서 대부분의 솔루션은 정렬을 피하는 것을 목표로 하지만 여전히 테이블에 대해 순차적 스캔을 수행해야 합니다.

인덱스 스캔으로 전환하여 순차 스캔을 피하는 방법도 있습니다.임의 행의 인덱스 값을 알고 있으면 거의 즉시 결과를 얻을 수 있습니다.문제는 - 인덱스 값을 추측하는 방법입니다.

다음 솔루션은 PostgreSQL 8.4에서 작동합니다.

explain analyze select * from cms_refs where rec_id in 
  (select (random()*(select last_value from cms_refs_rec_id_seq))::bigint 
   from generate_series(1,10))
  limit 1;

위의 솔루션에서는 범위 0에서 10개의 다양한 임의 인덱스 값을 추측합니다.[id의 마지막 값].

숫자 10은 임의적입니다. (놀랍게도) 응답 시간에 큰 영향을 미치지 않으므로 100 또는 1000을 사용할 수 있습니다.

한 가지 문제도 있습니다. ID가 희박한 경우 너는 놓칠지도 모른다.해결책은 백업 계획을 갖고 있다 :) 이 경우에는 무작위() 쿼리에 의한 순수 이전 주문입니다.결합된 ID는 다음과 같습니다.

explain analyze select * from cms_refs where rec_id in 
    (select (random()*(select last_value from cms_refs_rec_id_seq))::bigint 
     from generate_series(1,10))
    union all (select * from cms_refs order by random() limit 1)
    limit 1;

아니 노동 조합 모두 절.이 경우 첫 번째 부분이 데이터를 반환하면 두 번째 부분은 절대 실행되지 않습니다!

늦었지만 Google을 통해 여기에 왔으므로 후손을 위해 대체 솔루션을 추가하겠습니다.

또 다른 접근 방식은 순서를 번갈아 가며 TOP를 두 번 사용하는 것입니다.TOP에서는 변수를 사용하기 때문에 "순수 SQL"인지는 모르겠지만 SQL Server 2008에서는 작동합니다.다음은 임의의 단어를 원하는 경우 사전 단어 테이블에 대해 사용하는 예입니다.

SELECT TOP 1
  word
FROM (
  SELECT TOP(@idx)
    word 
  FROM
    dbo.DictionaryAbridged WITH(NOLOCK)
  ORDER BY
    word DESC
) AS D
ORDER BY
  word ASC

물론 @idx는 대상 테이블에서 1부터 COUNT(*)까지의 범위를 포함하여 무작위로 생성된 정수입니다.열이 색인화되어 있으면 이점도 얻을 수 있습니다.또 다른 장점은 NEWID()가 허용되지 않으므로 함수에서 사용할 수 있다는 것입니다.

마지막으로 위 쿼리는 동일한 테이블에서 NEWID() 유형 쿼리 실행 시간의 약 1/10에 실행됩니다.YYMV.

다음을 사용해 볼 수도 있습니다. new id() 기능.

쿼리를 작성하고 주문 방법을 사용하세요. new id() 기능.꽤 무작위입니다.

MySQL이 임의의 레코드를 얻으려면

 SELECT name
  FROM random AS r1 JOIN
       (SELECT (RAND() *
                     (SELECT MAX(id)
                        FROM random)) AS id)
        AS r2
 WHERE r1.id >= r2.id
 ORDER BY r1.id ASC
 LIMIT 1

자세한 세부 사항 http://jan.kneschke.de/projects/mysql/order-by-rand/

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

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

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

MSSQL(11.0.5569에서 테스트)에서 다음을 사용합니다.

SELECT TOP 100 * FROM employee ORDER BY CRYPT_GEN_RANDOM(10)

보다 훨씬 빠릅니다.

SELECT TOP 100 * FROM employee ORDER BY NEWID()

SQL Server에서는 TABLESAMPLE을 NEWID()와 결합하여 꽤 좋은 무작위성을 얻으면서도 여전히 속도를 유지할 수 있습니다.이는 실제로 1개 또는 적은 수의 행만 원하는 경우에 특히 유용합니다.

SELECT TOP 1 * FROM [table] 
TABLESAMPLE (500 ROWS) 
ORDER BY NEWID()
 SELECT * FROM table ORDER BY RAND() LIMIT 1

나는 CD-MaN에 동의해야 합니다:"ORDER BY RAND()"를 사용하면 작은 테이블이나 SELECT를 몇 번만 수행할 때 잘 작동합니다.

나는 또한 "num_value >= RAND() * ..." 기술을 사용하고, 정말로 임의의 결과를 얻고 싶다면 테이블에 하루에 한 번씩 업데이트하는 특별한 "무작위" 열이 있습니다.단일 UPDATE 실행에는 시간이 좀 걸리지만(특히 해당 열에 인덱스가 있어야 하기 때문에) 선택이 실행될 때마다 모든 행에 대해 임의의 숫자를 생성하는 것보다 훨씬 빠릅니다.

TableSample은 실제로 임의의 행 샘플을 반환하지 않으므로 주의하세요.이는 행을 구성하는 8KB 페이지의 무작위 샘플을 보도록 쿼리를 지시합니다.그런 다음 해당 페이지에 포함된 데이터에 대해 쿼리가 실행됩니다.이러한 페이지에서 데이터가 그룹화되는 방식(삽입 순서 등)으로 인해 실제로는 무작위 샘플이 아닌 데이터가 발생할 수 있습니다.

보다: http://www.mssqltips.com/tip.asp?tip=1308

TableSample에 대한 이 MSDN 페이지에는 실제로 무작위 데이터 샘플을 생성하는 방법에 대한 예가 포함되어 있습니다.

http://msdn.microsoft.com/en-us/library/ms189108.aspx

나열된 아이디어 중 상당수가 여전히 순서 지정을 사용하는 것 같습니다.

그러나 임시 테이블을 사용하는 경우 (많은 솔루션에서 제안한 것처럼) 임의의 인덱스를 할당한 다음 0과 1 사이의 임의의 숫자보다 큰 첫 번째 인덱스를 가져올 수 있습니다.

예를 들어(DB2의 경우):

WITH TEMP AS (
SELECT COMLUMN, RAND() AS IDX FROM TABLE)
SELECT COLUMN FROM TABLE WHERE IDX > .5
FETCH FIRST 1 ROW ONLY

간단하고 효율적인 방법 http://akinas.com/pages/en/blog/mysql_random_row/

SET @i = (SELECT FLOOR(RAND() * COUNT(*)) FROM table); PREPARE get_stmt FROM 'SELECT * FROM table LIMIT ?, 1'; EXECUTE get_stmt USING @i;

dbms_random.value를 사용하는 대신 Oracle을 위한 더 나은 솔루션이 있지만 dbms_random.value로 행을 정렬하려면 전체 스캔이 필요하며 큰 테이블의 경우 상당히 느립니다.

대신 이것을 사용하십시오:

SELECT *
FROM employee sample(1)
WHERE rownum=1

파이어버드의 경우:

Select FIRST 1 column from table ORDER BY RAND()

SQL Server 2012+에서는 다음을 사용할 수 있습니다. 오프셋 가져오기 쿼리 단일 임의 행에 대해 이 작업을 수행하려면

select  * from MyTable ORDER BY id OFFSET n ROW FETCH NEXT 1 ROWS ONLY

여기서 id는 ID 열이고 n은 원하는 행입니다. 테이블의 0과 count()-1 사이의 임의의 숫자로 계산됩니다(오프셋 0은 결국 첫 번째 행입니다).

이는 ORDER BY 절에 대해 작업할 인덱스가 있는 한 테이블 데이터의 구멍에 대해 작동합니다.무작위성에 있어서도 매우 좋습니다. 전달하기 위해 직접 작업을 수행하지만 다른 방법의 문제는 존재하지 않습니다.게다가 성능은 꽤 좋습니다. 작은 데이터 세트에서는 잘 유지되지만 수백만 행에 대해 심각한 성능 테스트를 시도하지는 않았습니다.

SQL Server 2005 이상의 경우 @GreyPanther의 답변을 다음과 같은 경우로 확장합니다. num_value 연속된 값이 없습니다.이는 데이터 세트가 균등하게 분산되지 않은 경우와 다음과 같은 경우에도 작동합니다. num_value 숫자가 아니라 고유 식별자입니다.

WITH CTE_Table (SelRow, num_value) 
AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY ID) AS SelRow, num_value FROM table
) 

SELECT * FROM table Where num_value = ( 
    SELECT TOP 1 num_value FROM CTE_Table  WHERE SelRow >= RAND() * (SELECT MAX(SelRow) FROM CTE_Table)
)

SQL의 임의 함수가 도움이 될 수 있습니다.또한 단 하나의 행으로 제한하려면 끝에 추가하면 됩니다.

SELECT column FROM table
ORDER BY RAND()
LIMIT 1
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top