Atasi SEPERTI batasan panjang karakter

13

Dengan membaca batasan panjang karakter SEPERTI ini di sini, sepertinya saya tidak dapat mengirim teks lebih dari ~ 4000 karakter dalam klausa LIKE.

Saya mencoba mengambil paket permintaan dari cache paket permintaan untuk permintaan tertentu.

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 '?'

jika permintaan di dalam LIKElebih dari 4000 karakter maka saya mendapatkan 0 hasil bahkan jika permintaan saya ada dalam rencana cache. (Saya mengharapkan setidaknya eror).

Apakah ada cara untuk mengatasi masalah ini atau melakukannya secara berbeda? Saya memiliki pertanyaan yang 10000panjangnya > karakter dan sepertinya saya tidak dapat menemukannya dengan LIKE.

Dan Dinu
sumber
2
Pecah teks mungkin ... karena Anda seharusnya tidak memiliki banyak pertanyaan persis seperti satu sama lain:where st.text like '%MY_QUERY%CHARS%' ESCAPE '?'
scsimon
4
Apakah Anda benar-benar memiliki teks kueri yang identik untuk 4.000 karakter dan kemudian berbeda?
Martin Smith
@ MartinSmith ya, saya punya pertanyaan seperti itu.
Dan Dinu

Jawaban:

9

Tampaknya ini tidak dapat diselesaikan dalam T-SQL murni karena tidak ada CHARINDEXatau tidak PATINDEXmemungkinkan untuk menggunakan lebih dari 8000 byte dalam string "untuk mencari" (yaitu maks 8000 VARCHARatau 4000 NVARCHARkarakter). Ini bisa dilihat pada tes berikut:

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

Kedua kueri tersebut menghasilkan kesalahan berikut:

Msg 8152, Level 16, State 10, Line xxxxx
String atau data biner akan terpotong.

Dan, mengurangi 7000salah satu dari kueri tersebut untuk 3999menghilangkan kesalahan. Nilai 4000dalam kedua kasus juga akan kesalahan (karena N'Z'karakter tambahan di awal).

NAMUN, ini bisa dicapai dengan menggunakan SQLCLR. Cukup mudah untuk membuat fungsi skalar yang menerima dua parameter input tipe NVARCHAR(MAX).

Contoh berikut menggambarkan kemampuan ini menggunakan versi gratis dari perpustakaan SQL # SQLCLR (yang saya buat, tetapi String_Contains lagi tersedia di versi gratis :-).

The String_Contains skalar UDF saat ini memiliki @SearchValueparam masukan sebagai NVARCHAR(4000)bukan NVARCHAR(MAX)(saya tidak harus berpikir orang akan mencari untuk string lebih dari 4000 karakter ;-) tapi itu sangat mudah untuk perubahan dengan membuat mengikuti perubahan satu kali (setelah SQL # telah diinstal, tentu saja):

GO
ALTER FUNCTION [SQL#].[String_Contains](@StringValue [NVARCHAR](MAX),
                                        @SearchValue [NVARCHAR](MAX))
RETURNS [BIT]
WITH EXECUTE AS CALLER
AS EXTERNAL NAME [SQL#].[STRING].[Contains];
GO

MEMPERSIAPKAN

-- 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;

UJI

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

Harap diingat bahwa String_Contains menggunakan perbandingan (huruf, aksen, Kana, dan lebar) yang semuanya sensitif.

Solomon Rutzky
sumber
2

Karena Anda juga meminta pendekatan alternatif, cara lain untuk menemukan rencana spesifik adalah mencarinya plan_hash, dengan mengubah kueri Anda sebagai berikut:

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

Cara tercepat yang saya temukan untuk mendapatkan QueryHashnilai yang dicari adalah dengan menempelkan kueri yang dimaksud ke dalam Jendela Kueri lalu Tampilkan Perkiraan Rencana Eksekusi. Baca output XML dan cari QueryHashatribut dalam StmtSimpleelemen dan ini akan memberi Anda apa yang Anda butuhkan. Masukkan nilai QueryHash ke dalam kueri di atas dan semoga Anda memiliki apa yang Anda cari.

Berikut adalah beberapa tangkapan layar yang menunjukkan cara cepat mendapatkan QueryHashnilai jika saya menjelaskannya dengan buruk.

Tampilkan Perkiraan Rencana Eksekusi

masukkan deskripsi gambar di sini

Tampilkan Rencana Eksekusi XM ...

masukkan deskripsi gambar di sini

Cari Nilai QueryHash

masukkan deskripsi gambar di sini

Jelas trik ini tidak akan berfungsi jika kueri yang Anda cari berbeda dari kueri yang Anda tampilkan untuk Perkiraan Rencana Eksekusi, tetapi ini mungkin lebih cepat daripada semua nuansa yang menyertai rutinitas CLR dan membuat kueri tersebut berfungsi dengan baik.

John Eisbrener
sumber
0

Jika Anda memiliki akses ke teks kueri (artinya Anda dapat memodifikasinya), Anda dapat menambahkan komentar unik kepada yang Anda minati:

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

kemudian cari myUniqueQuery123di cache rencana alih-alih seluruh teks kueri:

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

PS. Tidak diuji

mustaccio
sumber