เอาชนะข้อจำกัดความยาวอักขระ 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ตัวอักษรยาวเกิน 4,000 ตัวอักษรฉันจะได้ผลลัพธ์ 0 ถึงแม้ว่าแบบสอบถามของฉันจะอยู่ในแผนแคช (ฉันคาดหวังอย่างน้อย erorr)

มีวิธีแก้ไขปัญหานี้หรือจะทำแตกต่างกันอย่างไร ผมมีข้อสงสัยซึ่งสามารถ> ตัวอักษรที่ยาวนานและมีลักษณะเหมือนที่ฉันไม่สามารถหาพวกเขาด้วย10000LIKE


2
อาจทำให้ข้อความwhere st.text like '%MY_QUERY%CHARS%' ESCAPE '?'
แตกสลาย

4
คุณมีข้อความค้นหาที่เหมือนกันสำหรับอักขระ 4,000 ตัวและแตกต่างกันจริงหรือไม่?
Martin Smith

@MartinSmith ใช่ฉันมีคำถามเช่นนั้น
Dan Dinu

คำตอบ:


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

ข้อความค้นหาทั้งสองรายการส่งคืนข้อผิดพลาดต่อไปนี้:

ข่าวสารเกี่ยวกับ 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ใช้การเปรียบเทียบทุกอย่างที่ละเอียดอ่อน (ตัวพิมพ์เล็ก - ใหญ่, สำเนียง, คะนะและความกว้าง)


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ค่าการค้นหาคือการวางแบบสอบถามที่เป็นปัญหาลงในหน้าต่าง Query แล้วแสดงแผนการดำเนินการโดยประมาณ อ่านเอาต์พุต XML และมองหาQueryHashแอ็ตทริบิวต์ในStmtSimpleอิลิเมนต์และสิ่งนี้ควรให้สิ่งที่คุณต้องการ เสียบค่า QueryHash ลงในแบบสอบถามด้านบนและหวังว่าคุณควรมีสิ่งที่คุณต้องการ

นี่คือภาพหน้าจอบางส่วนที่แสดงวิธีรับQueryHashค่าอย่างรวดเร็วในกรณีที่ฉันอธิบายได้ไม่ดี

แสดงแผนการดำเนินการโดยประมาณ

ป้อนคำอธิบายรูปภาพที่นี่

แสดงแผนปฏิบัติการ XM ...

ป้อนคำอธิบายรูปภาพที่นี่

ค้นหา QueryHash Value

ป้อนคำอธิบายรูปภาพที่นี่

เห็นได้ชัดว่าเคล็ดลับจะไม่ทำงานหากแบบสอบถามที่คุณกำลังมองหาแตกต่างจากแบบสอบถามที่คุณแสดงแผนการดำเนินการโดยประมาณ แต่อาจเร็วกว่าความแตกต่างนิดหน่อยทั้งหมดที่มาพร้อมกับรูทีน CLR และทำให้การทำงานนั้นถูกต้อง


0

หากคุณสามารถเข้าถึงข้อความค้นหา (หมายถึงคุณสามารถแก้ไขได้) คุณสามารถเพิ่มความคิดเห็นที่ไม่ซ้ำกับผู้ที่คุณสนใจ:

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

จากนั้นค้นหาmyUniqueQuery123ในแคชแผนแทนข้อความค้นหาทั้งหมด:

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

PS ไม่ผ่านการทดสอบ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.