Подолайте обмеження довжини символів LIKE


13

Читаючи це обмеження довжини символів LIKE , схоже, я не можу надсилати текст довше ~ 4000 символів у пункті LIKE.

Я намагаюся отримати план запиту з кешу плану запитів для конкретного запиту.

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 результатів, навіть якщо мій запит знаходиться в плані кешу. (Я очікував принаймні ерорра).

Чи є спосіб вирішити це питання або зробити це по-іншому? У мене є запити, які можуть бути> 10000символами довгими, і виглядає так, що я не можу їх знайти за допомогою LIKE.


2
Розбийте текст, можливо ... оскільки у вас не повинно бути багато запитів так, як один до одного:where st.text like '%MY_QUERY%CHARS%' ESCAPE '?'
scsimon

4
Чи є у вас фактично тексти запитів, однакові на 4000 символів, а потім різняться?
Мартін Сміт

@MartinSmith так, у мене є такі запити.
Дан Діну

Відповіді:


9

Не здається, що це можна вирішити в чистому T-SQL, оскільки ні він CHARINDEXне PATINDEXдозволяє використовувати більше 8000 байт у рядку "для пошуку" (тобто максимум 8000 VARCHARабо 4000 NVARCHARсимволів). Це можна побачити в наступних тестах:

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

Обидва ці запити повертають таку помилку:

Msg 8152, рівень 16, стан 10, рядок xxxxx
Рядок або двійкові дані будуть усічені.

І, зменшуючи 7000будь-який із цих запитів, щоб 3999позбутися помилки. Значення 4000в обох випадках також буде помилковим (через додатковий N'Z'символ на початку).

ЗАРАЗ, це може бути здійснено за допомогою SQLCLR. Досить просто створити скалярну функцію, яка приймає два вхідні параметри типу NVARCHAR(MAX).

Наступний приклад ілюструє цю здатність за допомогою Безкоштовної версії бібліотеки 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 використовує порівнянне з урахуванням усе (регістр, акцент, Kana та ширина).


2

Оскільки ви також попросили альтернативних підходів, ще одним способом пошуку конкретного плану є пошук його 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значення, якщо я пояснюю це погано.

Відобразити передбачуваний план виконання

введіть тут опис зображення

Показати план виконання XM ...

введіть тут опис зображення

Пошук значення QueryHash

введіть тут опис зображення

Очевидно, що трюк не буде працювати, якщо запит, який ви шукаєте, відрізняється від запиту, для якого відображається Розрахунковий план виконання, але це може бути швидше, ніж усі нюанси, що поставляються з процедурами CLR та змушують їх працювати належним чином.


0

Якщо у вас є доступ до текстів запитів (тобто ви можете їх змінювати), ви можете додати унікальні коментарі до тих, хто вас цікавить:

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

потім шукайте myUniqueQuery123в кеші плану замість всього тексту запиту:

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

PS. Не перевірений

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.