web-dev-qa-db-ja.com

SQL Serverエラー8632(WHERE句のエントリが100,000を超えるため)

私の問題(または少なくともエラーメッセージ)は クエリプロセッサが内部リソースを使い果たしました-非常に長いSQLクエリ によく似ています。

私の顧客は、100,000エントリのwhere句を含む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句は次のようになります。

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節のデータは計算の結果であり、別のテーブルで実行されます。最初にそのテーブルのエントリが読み取られ、最初にフィルター処理されてから、追加の処理が実行されます(これを使用して実行することは不可能です) SQL)、その余分な処理の結果はより多くのフィルタリングであり、その結果はwhere節で使用されます。 SQLで完全なフィルタリングを書くことは不可能だったので、前述の方法が使用されました。明らかにwhere節の内容は処理ごとに変わる可能性があるため、動的なソリューションが必要です。

16
Dominique

100,000を超える値を検索するには、それらを一時テーブルに配置します。検索する値ごとに1行です。次に、クエリをその一時テーブルに結合してフィルタリングします。

100,000を超える値を持つものはパラメータではなく、テーブルです。制限を上げることを考えるのではなく、 スワーツの10%ルール を検討してください。SQLServerの制限の10%に近づいていると、おそらく悪い時間になるでしょう。

67
Brent Ozar

とにかくアプリを変更する場合は、どちらかを検討してください

(a)値のセット全体にTVPを使用する-C#でDataTableを作成し、それをStructuredTypeを使用してストアドプロシージャに渡します 私はここで説明します =。 (使用するアプローチに関係なくスケーラビリティが問題になる可能性があるため、100,000エントリは正常ではありません。)

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
10
Aaron Bertrand

いいえ、構成可能ではなく、これをより高い値に増やすことはできません。

あなたが言及したMSDNの記事と他の記事で提案された回避策があります。ここでは2つ説明しましたが、さらに検索できます。

6
SqlWorldWide

クエリ条件を短くすることに関する私の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)
4
Zephyr

クエリを実際に見ることができずに何を達成しようとしているのかを理解するのは難しいのですが、ここでは、クエリに2つのテーブルのみが必要であり、1つはデータ、もう1つは避けたい値が必要であると仮定します。

  • where entry in (<list of 100.000 values>)の使用は、効率とメンテナンスの両方の観点から、途方もなくひどいものです。
  • where entry in (select whatever from table)の使用は、以前のものほどひどく恐ろしいほどひどいものではありません。それでも、表の特異性とSQLエンジンの両方に応じて、角膜癌にもかかわらず、正常に機能する場合があります。 (Oracleでは大丈夫かもしれませんが、MySQLでは決してできません。MSSQLを思い出せません)。
  • PLSQLを使用してこれを実現するのは、恐ろしいことです。
  • 私の意見では(クエリをまったく知らなくても)、次のようにクエリを書き直す必要があります。

    • これらの100.000の値がalwaysと同じで、残りのクエリに依存していない場合は、それらの値をテーブル(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の値が同じでない場合は、これらの100.000の値を取得するための何らかのロジックが必要です。このロジックは、前のクエリのONに埋め込む必要があります。

0
glezo