Überwindung der LIKE-Zeichenlängenbeschränkung


13

Wenn ich diese Einschränkung der LIKE-Zeichenlänge hier lese , kann ich anscheinend keinen Text senden, der länger als ~ 4000 Zeichen in einer LIKE-Klausel ist.

Ich versuche, den Abfrageplan für eine bestimmte Abfrage aus dem Abfrageplan-Cache abzurufen.

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

Wenn die Abfrage in der LIKElänger als 4000 Zeichen ist, erhalte ich 0 Ergebnisse, auch wenn sich meine Abfrage im Cache-Plan befindet. (Ich hatte zumindest einen Fehler erwartet).

Gibt es eine Möglichkeit, dieses Problem zu umgehen oder es anders zu machen? Ich habe Anfragen, die> 10000Zeichen lang sein können, und es sieht so aus, als ob ich sie mit der nicht finden kann LIKE.


2
Brechen Sie den Text vielleicht auf ... da Sie nicht viele Fragen haben sollten, die einander genau gleichen:where st.text like '%MY_QUERY%CHARS%' ESCAPE '?'
scsimon

4
Haben Sie tatsächlich Abfragetexte, die für 4.000 Zeichen identisch sind und sich dann unterscheiden?
Martin Smith

@MartinSmith Ja, ich habe solche Fragen.
Dan Dinu

Antworten:


9

Es scheint nicht möglich zu sein, dies in reinem T-SQL zu lösen, da weder CHARINDEXnochPATINDEX erlauben für die Verwendung von mehr als 8000 Bytes in der Zeichenfolge (dh max von 8000 „suchen“ VARCHARoder 4000 NVARCHARZeichen). Dies zeigt sich in folgenden Tests:

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

Beide Abfragen geben den folgenden Fehler zurück:

Meldung 8152, Ebene 16, Status 10, Zeile xxxxx
oder Binärdaten werden abgeschnitten.

Reduzieren Sie den Wert 7000in einer dieser Abfragen, 3999um den Fehler zu beheben. Ein Wert von 4000in beiden Fällen wird ebenfalls fehlerhaft (aufgrund des zusätzlichen N'Z'Zeichens am Anfang).

Dies kann jedoch mit SQLCLR erreicht werden. Es ist ziemlich einfach, eine Skalarfunktion zu erstellen, die zwei Eingabeparameter vom Typ akzeptiert NVARCHAR(MAX).

Das folgende Beispiel veranschaulicht diese Möglichkeit mit der kostenlosen Version der SQL # SQLCLR-Bibliothek (die ich erstellt habe, aber String_Contains ist in der kostenlosen Version wieder verfügbar :-).

Die String_Contains skalare UDF hat zur Zeit den @SearchValueEingang param als NVARCHAR(4000)statt NVARCHAR(MAX)(ich muss nicht gedacht haben Menschen auf der Suche würde für Strings von über 4000 Zeichen ;-) , aber das ist sehr einfach zu ändern , indem Sie die folgende einmalige Änderung vorgenommen (nach SQL # wurde natürlich installiert):

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

INSTALLIEREN

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

TESTS

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

Beachten Sie, dass String_Contains einen allsensitiven Vergleich (Groß- / Kleinschreibung, Akzent, Kana und Breite) verwendet.


2

Da Sie auch nach alternativen Ansätzen gefragt haben, können Sie einen bestimmten Plan auch suchen plan_hash , indem Sie Ihre Abfrage wie folgt ändern:

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

Der schnellste Weg, den ich gefunden habe, um den QueryHashzu suchenden Wert zu erhalten, besteht darin, die betreffende Abfrage in ein Abfragefenster einzufügen und dann den geschätzten Ausführungsplan anzuzeigen. Lesen Sie die XML-Ausgabe und suchen Sie nachQueryHash Attribut im StmtSimpleElement. Dies sollte Ihnen das geben, was Sie benötigen. Fügen Sie den QueryHash-Wert in die obige Abfrage ein, und Sie sollten hoffentlich das haben, wonach Sie suchen.

Hier sind einige Screenshots, die zeigen, wie Sie das schnell bekommen QueryHash Wert falls ich ihn schlecht erkläre.

Geschätzten Ausführungsplan anzeigen

Bildbeschreibung hier eingeben

Ausführungsplan XM anzeigen ...

Bildbeschreibung hier eingeben

Suchen Sie nach QueryHash Value

Bildbeschreibung hier eingeben

Offensichtlich funktioniert der Trick nicht, wenn sich die gesuchte Abfrage von der Abfrage unterscheidet, für die Sie den geschätzten Ausführungsplan anzeigen. Dies ist jedoch möglicherweise schneller als all die Nuancen, die mit CLR-Routinen einhergehen und deren ordnungsgemäße Funktionsweise sicherstellen.


0

Wenn Sie Zugriff auf die Abfragetexte haben (dh Sie können sie ändern), können Sie denjenigen, die Sie interessieren, eindeutige Kommentare hinzufügen:

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

Suchen Sie dann myUniqueQuery123im Plan-Cache nach anstelle des gesamten Abfragetexts:

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

PS. Nicht getestet

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.