[sql] SQL WHERE ID IN (id1, id2,…, idn)

큰 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만이 유일하게 좋은 솔루션입니다.

왜?

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

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


답변

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

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

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


답변

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

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


답변

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

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

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

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

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

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

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


답변

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

조인을 사용하십시오.


답변

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

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

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


답변

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

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