web-dev-qa-db-ja.com

SQL ServerのIN結果に制限はありますか?

INフィルターが処理できるコンテンツに制限はありますか?例えば:

SELECT Name
FROM People
WHERE Job IN (All the values goes here)

[〜#〜] in [〜#〜] のMicrosoftドキュメントは言う:

"括弧内に非常に多数の値(カンマで区切られた数千の値)を明示的に含めると、IN句でリソースを消費し、エラー8623または8632を返す可能性があります。この問題を回避するには、アイテムをINリストに保存しますテーブル、およびIN句内でSELECTサブクエリを使用します。」

しかし、正確またはおおよその数はありますか

何千もの値

5
Unnamed

場合によります。真剣に。そのため、環境の低下に気づく場所についてドキュメントを具体的にすることはできません。

解決策は、これをやめて(そしてそれを心配せずに)、 table-valued parameter を使用することです。

カンマ区切りのリストを文字列として構築してクエリで連結できるような方法でC#の値を持っている場合、C#の値は、DataTableなどに詰め込むことができます。構造体(最初にそこから取得されている場合があるかもしれません)、その構造体をパラメータとしてストアドプロシージャに渡します。

10
Aaron Bertrand

厳密に言うと、65536の値でクエリが失敗することが保証されています。そうは言っても、実際には32768を上限として考えるのはかなり安全だと思いますが、それは最低の上限ではありません。最小の上限は、クエリおよび他のローカル要因で他に何が起こっているかに依存します。簡単な例でこれを見ることができます。最初に10万行をヒープに入れます。

_DROP TABLE IF EXISTS dbo.Q228695;

CREATE TABLE dbo.Q228695 (ID BIGINT);

INSERT INTO dbo.Q228695 WITH (TABLOCK)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);
_

次の形式のクエリのIN句に32768の値を挿入するとします。SELECT COUNT(*) FROM dbo.Q228695 WHERE ID IN ();これは、SQL Server 2017で_STRING_AGG_を使用して簡単に実行できます。

_DECLARE @nou VARCHAR(MAX);

SELECT @nou = 'SELECT COUNT(*) FROM dbo.Q228695 WHERE ID IN (' + STRING_AGG(CAST(RN AS VARCHAR(MAX)), ',') + ')'
FROM
(
    SELECT TOP (32768) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
    FROM master..spt_values t1
    CROSS JOIN master..spt_values t2
) q
OPTION (MAXDOP 1);

EXEC (@nou);
_

次のエラーが発生します。

メッセージ8632、レベル17、状態2、行1

内部エラー:式サービスの制限に達しました。クエリで潜在的に複雑な式を探し、それらを簡略化してください。

IN句から1つの値を削除すると、クエリは24秒後に成功します。この信じられないほど単純なクエリの場合、クエリの実行を許可する値の最大数は32767です。 エラー8632に関するMicrosoftのドキュメント は次のように述べています。

この問題は、SQL Serverがクエリの単一の式に含めることができる識別子と定数の数を制限するために発生します。この制限は65,535です。

32767は機能し、32768は機能しません。 65535/2 = 32767.5というのは偶然ではないと思います。何らかの理由で、観測された動作は、IN句の各定数が制限に向かって2つとしてカウントされることです。

これは間違った質問だと思います。同じ値を一時テーブルに入れると、クエリは0秒で実行されます。

_DROP TABLE IF EXISTS #t;

CREATE TABLE #t (ID BIGINT);

INSERT INTO #t WITH (TABLOCK)
SELECT TOP (32768) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);

SELECT COUNT(*)
FROM dbo.Q228695
WHERE ID IN (
    SELECT t.ID
    FROM #t t
);
_

クエリでエラーがスローされない場合でも、IN句に入力する値が多すぎると、パフォーマンスの負荷が大きくなります。

5
Joe Obbish