WHERE 절의 100,000 개가 넘는 항목으로 인한 SQL Server 오류 8632


16

내 문제 (또는 적어도 오류 메시지)는 쿼리 프로세서에 내부 리소스가 부족한 매우 긴 SQL 쿼리 와 매우 유사 합니다 .

고객이 정확히 100,000 개의 항목이있는 where-clause를 포함하는 SQL select-query를 사용하고 있습니다.

오류 8632 및 오류 메시지와 함께 쿼리가 실패합니다

내부 오류 : 식 서비스 제한에 도달했습니다. 쿼리에서 잠재적으로 복잡한 표현식을 찾아서 단순화하십시오.)

이 오류 메시지가 정확히 100,000 개의 항목에 표시되는 것이 매우 특이하므로 이것이 구성 가능한 값인지 궁금합니다. 이 경우에 해당합니까? 그렇다면이 값을 더 높은 값으로 늘리려면 어떻게해야합니까?

MSDN ,이 쿼리를 다시 재 작성 할 수있는 제안이지만, 나는이 문제를 방지하고 싶습니다.

한편 나는 내가 말하는 항목 목록에 자연수가 포함되어 있음을 발견했으며, 그중 일부는 순차적 인 것처럼 보입니다 ((1,2,3,6,7,8,9,10,12, 13,15,16,17,18,19,20).

이것은 SQL where-clause를 다음과 같이 만듭니다.

where entry in (1,2,3,6,7,8,9,10,12,13,15,16,17,18,19,20)

나는 이것을 다음으로 바꿀 수있다 :

where (entry between 1 and 3) OR
      (entry between 6 and 10) OR
      (entry between 12 and 13) OR
      (entry between 15 and 20)

이를 통해 다음을 단축 할 수 있습니까?

where entry in (1,...,3,6,...,10,12,13,15,...,20)

... 또는 비슷한 것? (긴 샷이지만 소프트웨어 업데이트를 더 쉽고 읽기 쉽게 만듭니다)

정보 : where-clause의 데이터는 다른 테이블에서 수행 된 계산 결과입니다. 먼저 해당 테이블의 항목을 처음에 읽고 필터링 한 다음 추가 처리가 수행됩니다 (사용할 수 없음) 추가 처리의 결과는 더 많은 필터링이며 결과는 where 절에서 사용됩니다. SQL에서 전체 필터링을 작성하는 것이 불가능했기 때문에 언급 된 방법이 사용되었습니다. where-clause의 내용은 모든 처리에서 변경 될 수 있으므로 동적 솔루션이 필요합니다.


7
편집에 대한 응답으로 : 아니요, WHERE IN해당 종류의 범위 구문을 지원하지 않습니다. 또한 WHERE () OR () OR ()AND 가 아니 어야합니다 . 그러나 브렌트의 제안을 사용하기 위해 실제로 전체 쿼리를 변경할 필요는 없습니다 WHERE IN (SELECT myID FROM #biglist). 그리고 #biglist실제 (영구적) 테이블 또는 임시 테이블 일 수도 있습니다.
BradC

전체 쿼리와 외부에서 계산하는 내용을 게시하십시오. 이는 실제로 SQL에서 완전히 수행 할 수있는 것입니다. 개인 정보 보호 또는 기타 문제가 우려되는 경우 필드 이름을 바꾸십시오.
Mike

답변:


67

100,000 개가 넘는 값을 검색하려면 검색하려는 값당 한 행씩 임시 테이블에 입력하십시오. 그런 다음 쿼리를 해당 임시 테이블에 조인하여 필터링하십시오.

100,000 개가 넘는 값을 가진 것은 매개 변수가 아니라 테이블입니다. 한계를 높이는 것에 대해 생각하는 대신 Swart의 10 % 규칙을 고려하십시오 . SQL Server 한계의 10 %에 도달하면 시간이 오래 걸릴 수 있습니다.


1
의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
폴 화이트 복원 모니카

10

어쨌든 앱을 변경하려는 경우 다음 중 하나를 고려하십시오.

(a) 전체 값 세트에 TVP 사용- 여기에서 설명하는 것처럼 DataTableC #에서를 작성하고 이를 사용하여 저장 프로 시저에 전달합니다 . (어떻게 사용하든 관계없이 확장 성이 문제가 될 수 있기를 바라지 만 100,000 개의 항목이 정상이 아닙니다.)StructuredType

CREATE TYPE dbo.optionA AS TABLE(value int PRIMARY KEY);
GO

CREATE PROCEDURE dbo.procedure_optionA
  @t dbo.optionA READONLY
AS
BEGIN
  SET NOCOUNT ON;
  SELECT <cols> FROM dbo.<table> AS t
    INNER JOIN @t AS tvp
    ON t.entry = tvp.value;
END
GO

또는

(b) TVP를 사용하여 범위의 상한 및 하한을 통과하고 약간 다른 조인을 작성합니다 (@ypercube 덕분에).

  CREATE TYPE dbo.optionB AS TABLE
  (
    LowerBound int,
    UpperBound int,
    PRIMARY KEY (LowerBound, UpperBound)
  );
  GO

  CREATE PROCEDURE dbo.procedure_optionB
    @t dbo.OptionB READONLY
  AS
  BEGIN
    SET NOCOUNT ON;
    SELECT <cols> FROM dbo.<table> AS t
      INNER JOIN @t AS tvp
      ON  t.entry >= tvp.LowerBound 
      AND t.entry <= tvp.UpperBound;
  END
  GO


4

쿼리 조건을 더 짧게 만드는 것과 관련하여 내 2 ¢ :-

가능한 모든 값을 entry미리 결정할 수 있다면 쿼리를 보완하면 가능할까요?

전에

where entry in (1,2,3,6,7,8,9,10,12,13,15,16,17,18,19,20)

where entry not in (4,5,11,14)

0

실제로 쿼리를 볼 수없는 상태에서 수행하려는 작업을 파악하기는 어렵지만 쿼리에는 데이터가있는 테이블과 피하고자하는 값이있는 테이블이 두 개만 필요하다고 가정합니다.

  • where entry in (<list of 100.000 values>)효율성과 유지 관리 측면에서 사용 은 엄청나게 끔찍합니다.
  • 사용하는 where entry in (select whatever from table)것은 이전만큼 끔찍하게 끔찍합니다. 그러나 표의 특성과 SQL 엔진에 따라 각막 암에도 불구하고 정상적으로 작동 할 수 있습니다. (Oracle에서는 괜찮을 수도 있고 MySQL에서는 절대 사용할 수 없으며 MSSQL에 대해서는 기억할 수 없습니다).
  • 이것을 달성하기 위해 PLSQL을 사용하는 것은 단순히 끔찍합니다.
  • 내 의견으로는 (질문을 전혀 몰라도) 다음과 같이 쿼리를 다시 작성해야합니다.

    • 나머지 쿼리에 의존하지 않고 100.000 값이 항상 동일한 경우 해당 값을 테이블 (table_fixed_values)에 업로드하고

      SELECT * -- whatever you might need
      FROM
         table_a A
         LEFT JOIN table_fixed_values ON A.entry=B.entry
      WHERE B.entry IS NOT NULL
    • 이 100.000 값이 같지 않으면 ON이전 쿼리에 포함해야 할 논리 인 100.000 값을 가져 오는 일종의 논리가 있어야 합니다.


3
LEFT JOIN table_fixed_values ON A.entry=B.entry WHERE B.entry IS NOT NULL동일하고 간단하고 읽기 쉬운가 INNER JOIN table_fixed_values ON A.entry=B.entry?
ypercubeᵀᴹ

@ ypercubeᵀᴹ, INNER JOIN이 더 합리적입니다.
glezo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.