web-dev-qa-db-ja.com

LIKE文字の長さ制限を克服する

これを読むと LIKE文字の長さの制限 ここでは、LIKE句で〜4000文字を超えるテキストを送信できないようです。

特定のクエリのクエリプランキャッシュからクエリプランをフェッチしようとしています。

SELECT *
FROM sys.dm_exec_cached_plans AS cp 
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp 
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS st
where st.text like '%MY_QUERY_LONGER_THAN_4000_CHARS%' ESCAPE '?'

LIKE内のクエリが4000文字よりも長い場合、クエリがキャッシュプランにある場合でも結果が0になります。 (私は少なくともerorrを期待していた)。

この問題を回避する方法または別の方法を使用する方法はありますか? > 10000文字になるクエリがありますが、LIKEでは検索できないようです。

13
Dan Dinu

CHARINDEXPATINDEXも「to search for」文字列で8000バイトを超えることができないため(つまり、最大8000 VARCHARまたは4000 NVARCHAR文字)、これは純粋なT-SQLでは解決できないようです。これは、次のテストで確認できます。

_SELECT 1 WHERE CHARINDEX(N'Z' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 7000),
                         N'Z' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 6000)) > 0

SELECT 1 WHERE PATINDEX(N'Z' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 7000),
                        N'Z' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 6000)) > 0
_

これらのクエリはどちらも次のエラーを返します。

メッセージ8152、レベル16、状態10、行xxxxx
文字列型やバイナリは省略されます。

また、これらのクエリのいずれかで_7000_を_3999_に減らすと、エラーが解消されます。どちらの場合も_4000_の値もエラーになります(先頭に余分な_N'Z'_文字があるため)。

ただし、これはSQLCLRを使用して実行できます。タイプNVARCHAR(MAX)の2つの入力パラメーターを受け入れるスカラー関数を作成するのはかなり簡単です。

次の例は、無料バージョンの SQL# SQLCLRライブラリを使用してこの機能を示しています(私が作成したライブラリですが、String_Containsが再び無料版で利用可能:-)。

[〜#〜]セットアップ[〜#〜]

_-- DROP TABLE #ContainsData;
CREATE TABLE #ContainsData
(
  ContainsDataID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  Col1 NVARCHAR(MAX) NOT NULL
);

INSERT INTO #ContainsData ([Col1])
VALUES (N'Q' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 15000)),
       (N'W' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 20000)),
       (N'Z' + REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 70000));

-- verify the lengths being over 8000
SELECT tmp.[ContainsDataID], tmp.[Col1], DATALENGTH(tmp.[Col1])
FROM   #ContainsData tmp;
_

[〜#〜]テスト[〜#〜]

_SELECT tmp.[ContainsDataID], tmp.[Col1], DATALENGTH(tmp.[Col1])
FROM   #ContainsData tmp
WHERE  SQL#.String_Contains(tmp.[Col1], REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 15100)) = 1;
-- IDs returned: 2 and 3

SELECT tmp.[ContainsDataID], tmp.[Col1], DATALENGTH(tmp.[Col1])
FROM   #ContainsData tmp
WHERE  SQL#.String_Contains(tmp.[Col1], REPLICATE(CONVERT(NVARCHAR(MAX), N'a'), 26100)) = 1;
-- IDs returned: 3
_

String_Containsはすべてを区別する(大文字、小文字、アクセント、かな、幅)比較を使用していることに注意してください。

9
Solomon Rutzky

別のアプローチも求めたため、特定のプランを見つける別の方法は、次のようにクエリを変更して、そのplan_hashを検索することです。

SELECT *
FROM sys.dm_exec_cached_plans AS cp 
INNER JOIN sys.dm_exec_query_stats qs
    ON cp.plan_handle = qs.plan_handle
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp 
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS st
WHERE qs.query_hash = 0xE4026347B5F49802

検索するQueryHash値を取得する最も簡単な方法は、問題のクエリをクエリウィンドウに貼り付けてから、推定実行プランを表示することです。 XML出力を読み、QueryHash要素でStmtSimple属性を探します。これにより、必要なものが得られます。 QueryHash値を上記のクエリにプラグインすると、期待どおりの結果が得られるはずです。

以下は、QueryHashの値をすばやく取得する方法を示すスクリーンショットです。

推定実行計画を表示

enter image description here

実行プランXMを表示...

enter image description here

QueryHash値の検索

enter image description here

探しているクエリが推定実行プランを表示しているクエリと異なる場合、このトリックは機能しませんが、これはCLRルーチンに付属しているすべてのニュアンスよりも速く、それらを正しく機能させることができます。

2
John Eisbrener

クエリテキストにアクセスできる場合(変更できることを意味します)、興味のあるユーザーに一意のコメントを追加できます。

select /* myUniqueQuery123 */ whatever from somewhere ...

次に、クエリテキスト全体ではなく、プランキャッシュでmyUniqueQuery123を検索します。

... where st.text like '%myUniqueQuery123%'

PS。未検証

0
mustaccio