SQL WHERE ID IN (id1, id2,…, idn)


170

큰 ID 목록을 검색하려면 쿼리를 작성해야합니다.

우리는 많은 백엔드 (MySQL, Firebird, SQLServer, Oracle, PostgreSQL ...)를 지원하므로 표준 SQL을 작성해야합니다.

ID 세트의 크기가 클 수 있으며 쿼리는 프로그래밍 방식으로 생성됩니다. 그렇다면 가장 좋은 방법은 무엇입니까?

1) IN을 사용하여 쿼리 작성

SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)

내 질문은 여기입니다. n이 매우 큰 경우 어떻게됩니까? 또한 성능은 어떻습니까?

2) OR를 사용하여 쿼리 작성

SELECT * FROM TABLE WHERE ID = id1 OR ID = id2 OR ... OR ID = idn

이 접근법에는 n 제한이 없다고 생각하지만 n이 매우 큰 경우 성능은 어떻습니까?

3) 프로그래밍 솔루션 작성 :

  foreach (var id in myIdList)
  {
      var item = GetItemByQuery("SELECT * FROM TABLE WHERE ID = " + id);
      myObjectList.Add(item);
  }

데이터베이스 서버가 네트워크를 통해 쿼리 될 때이 접근 방식에 일부 문제가 발생했습니다. 일반적으로 작은 결과를 많이 만드는 것보다 모든 결과를 검색하는 하나의 쿼리를 수행하는 것이 좋습니다. 어쩌면 내가 틀렸을 수도 있습니다.

이 문제에 대한 올바른 해결책은 무엇입니까?


1
옵션 1은 일부 존재하지 않는 7k ID를 선택하여 SQL 서버 응답 시간을 크게 줄입니다. 일반적으로 쿼리는 약 1300ms가 걸렸으며 IN!를 사용하여 80ms로 줄었습니다 . 나는 당신의 솔루션 1 + 3으로 내 것을했습니다. 마지막 쿼리는 실행하기 위해 SQL로 전송 된 하나의 긴 쿼리 문자열이었습니다.
Piotr Kula

답변:


108

옵션 1만이 유일하게 좋은 솔루션입니다.

왜?

  • 옵션 2도 동일하지만 열 이름을 여러 번 반복합니다. 또한 SQL 엔진은 값이 고정 목록의 값 중 하나인지 확인 하려는지 즉시 알지 못합니다. 그러나 좋은 SQL 엔진은와 같은 성능을 갖도록 최적화 할 수 있습니다 IN. 그래도 여전히 가독성 문제가 있습니다 ...

  • 옵션 3은 단순히 성능 측면에서 끔찍합니다. 루프마다 쿼리를 보내고 작은 쿼리로 데이터베이스를 망치게합니다. 또한 "값이 주어진 목록에있는 것 중 하나"에 대한 최적화를 사용하지 못하게합니다.


2
동의하지만 목록에 많은 RDMS가 제한되어 있으므로 @Ed Guiness의 솔루션을 사용해야하지만 여기서 임시 테이블은 RDBMS마다 다릅니다. (복잡한 문제에 대해서는 순수한 표준 SQL 만 사용할 수는 없습니다.)
mmmmmm

28

다른 방법은 다른 테이블을 사용하여 id 값을 포함하는 것입니다. 그런 다음이 다른 테이블을 TABLE에서 내부 조인하여 반환 된 행을 제한 할 수 있습니다. 이것은 동적 SQL이 필요하지 않을 때 (가장 좋은 경우가 많음) 무한한 긴 IN 절이 없다는 주요 이점이 있습니다.

이 다른 테이블을 자르고 많은 수의 행을 삽입 한 다음 결합 성능을 돕기 위해 인덱스를 작성하십시오. 또한 데이터 검색에서 이러한 행의 누적을 분리하여 성능을 조정하는 더 많은 옵션을 제공 할 수 있습니다.

업데이트 : 임시 테이블을 사용할 수는 있지만 반드시해야한다고 암시하지는 않았습니다. 임시 데이터에 사용되는 영구 테이블은 여기에 설명 된 것 이상의 장점을 가진 일반적인 솔루션입니다.


1
그러나 필요한 ID 목록을 어떻게 전달 하시겠습니까? (범위 또는 이와 유사한 것을 선택할 수 없음).
raam86

1
@ raam86 : select다른 테이블 의 명령문을 사용하여 ID 목록을 얻었을 수 있습니다 . 목록은 상대하는 다른 테이블로 전달됩니다 inner join.
bdforbes

19

Ed Guiness가 제안한 것은 실제로 성능 향상 기입니다.

select * from table where id in (id1,id2.........long list)

제가 한 :

DECLARE @temp table(
            ID  int
            )
insert into @temp 
select * from dbo.fnSplitter('#idlist#')

그런 다음 inner는 메인 테이블로 temp를 결합했습니다.

select * from table inner join temp on temp.id = table.id

그리고 성능이 크게 향상되었습니다.


1
안녕하세요, fnSplitter는 MSSQL의 함수입니까? 내가 그것을 찾을 수 없었기 때문에.
WiiMaxx

표준이 아닙니다. 그들은이 목적을 위해 해당 기능을 작성했거나 예를 들어 이미 기능을 제공 한 응용 프로그램이 있음을 의미해야합니다.
underscore_d

fnSplitter는 Ritu에서 만든 기능입니다. 인터넷 / 구글에서 이와 유사한 기능을 찾을 수 있습니다.
Bashar Abu Shamaa

9

첫 번째 옵션은 확실히 최고의 옵션입니다.

SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)

그러나 ID 목록이 매우 크다는 것을 고려할 때 수백만의 경우 다음과 같이 청크 크기를 고려해야합니다.

  • ID 목록을 고정 번호의 청크로 나눕니다 (100).
  • 청크 크기는 서버의 메모리 크기에 따라 결정되어야합니다.
  • 10000 개의 ID가 있다고 가정하면 10000/100 = 100 개의 청크를 갖게됩니다
  • 한 번에 하나의 청크를 처리하여 select에 대한 100 개의 데이터베이스 호출

왜 덩어리로 나누어야합니까?

당신과 같은 시나리오에서 매우 일반적인 메모리 오버플로 예외는 결코 얻지 못할 것입니다. 데이터베이스 호출 수를 최적화하여 성능을 향상시킵니다.

그것은 항상 저에게 매력처럼 작용했습니다. 그것이 동료 개발자들에게도 효과가 있기를 바랍니다. :)


4

5 억 개의 레코드가있는 Azure SQL 테이블의 id in () 명령에서 SELECT * FROM MyTable을 수행하면 대기 시간이 7 분보다 길었습니다!

대신 이렇게하면 결과가 즉시 반환됩니다.

select b.id, a.* from MyTable a
join (values (250000), (2500001), (2600000)) as b(id)
ON a.id = b.id

조인을 사용하십시오.


3

대부분의 데이터베이스 시스템 IN (val1, val2, …)과 일련의 시스템 OR은 동일한 계획에 최적화되어 있습니다.

세 번째 방법은 값 목록을 임시 테이블로 가져 와서 값이 많은 경우 대부분의 시스템에서 더 효율적인 값을 조인하는 것입니다.

이 기사를 읽고 싶을 수도 있습니다.


3

샘플 3은 명백한 이유없이 데이터베이스를 셀 수없이 많은 시간을 들이기 때문에 그 중에서도 가장 성능이 떨어지는 것입니다.

임시 테이블에 데이터를로드 한 다음 조인하는 것이 훨씬 빠릅니다. 그 후 IN은 OR 그룹보다 약간 빠르게 작동해야합니다.


2

SqlServer를 의미한다고 생각하지만 Oracle에서는 지정할 수있는 IN 요소 수를 1000으로 제한합니다.


1
~ 40k IN 요소 이후에도 SQL Server가 작동을 멈 춥니 다. MSDN에 따르면 : IN 절에 매우 많은 수의 값 (수천 개)을 포함하면 리소스가 소비되고 오류 8623 또는 8632가 반환 될 수 있습니다.이 문제를 해결하려면 IN 목록의 항목을 테이블에 저장하십시오.
jahav
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.