Khắc phục giới hạn độ dài nhân vật THÍCH


13

Bằng cách đọc giới hạn độ dài ký tự THÍCH này ở đây, có vẻ như tôi không thể gửi văn bản dài hơn ~ 4000 ký tự trong mệnh đề THÍCH.

Tôi đang cố gắng tìm nạp gói truy vấn từ bộ đệm của kế hoạch truy vấn cho một truy vấn cụ thể.

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

nếu truy vấn bên trong LIKEdài hơn 4000 ký tự thì tôi nhận được 0 kết quả ngay cả khi truy vấn của tôi nằm trong kế hoạch bộ đệm. (Tôi đã mong đợi ít nhất là một erorr).

Có cách nào để khắc phục vấn đề này hoặc làm nó khác đi không? Tôi có các truy vấn có thể 10000dài > ký tự và có vẻ như tôi không thể tìm thấy chúng với LIKE.


2
Chia nhỏ văn bản có lẽ ... vì bạn không nên có nhiều truy vấn giống hệt nhau:where st.text like '%MY_QUERY%CHARS%' ESCAPE '?'
scsimon

4
Bạn có thực sự có các văn bản truy vấn giống hệt nhau cho 4.000 ký tự và sau đó khác nhau không?
Martin Smith

@MartinSmith vâng, tôi có truy vấn như thế.
Dan Dinu

Câu trả lời:


9

Dường như điều này không thể giải quyết được bằng T-SQL thuần túy vì CHARINDEXcũng không PATINDEXcho phép sử dụng hơn 8000 byte trong chuỗi "để tìm kiếm" (tức là tối đa 8000 VARCHARhoặc 4000 NVARCHARký tự). Điều này có thể được nhìn thấy trong các thử nghiệm sau:

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

Cả hai truy vấn đó đều trả về lỗi sau:

Msg 8152, Cấp 16, Trạng thái 10,
Chuỗi xxxxx hoặc dữ liệu nhị phân sẽ bị cắt ngắn.

Và, giảm một 7000trong hai truy vấn đó để 3999thoát khỏi lỗi. Giá trị 4000trong cả hai trường hợp cũng sẽ bị lỗi (do N'Z'ký tự phụ ở đầu).

TUY NHIÊN, điều này có thể được thực hiện bằng SQLCLR. Nó khá đơn giản để tạo một hàm vô hướng chấp nhận hai tham số đầu vào loại NVARCHAR(MAX).

Ví dụ sau minh họa khả năng này bằng phiên bản Miễn phí của thư viện SQL # SQLCLR (mà tôi đã tạo, nhưng String_Contains lại có sẵn trong phiên bản Miễn phí :-).

Các String_Contains vô hướng UDF hiện có @SearchValueparam đầu vào như NVARCHAR(4000)thay vì NVARCHAR(MAX)(tôi phải không có suy nghĩ mọi người sẽ được tìm kiếm cho chuỗi hơn 4000 ký tự ;-) nhưng đó là rất dễ dàng để thay đổi bằng cách thay đổi một lần sau (sau khi SQL # đã được cài đặt, tất nhiên):

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

THIẾT LẬP

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

KIỂM TRA

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

Xin lưu ý rằng String_Contains đang sử dụng so sánh mọi thứ nhạy cảm (trường hợp, dấu, Kana và chiều rộng).


2

Bởi vì bạn cũng đã yêu cầu các phương pháp thay thế, một cách khác để tìm một kế hoạch cụ thể là tìm kiếm kế hoạch đó plan_hash, bằng cách thay đổi truy vấn của bạn như sau:

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

Cách nhanh nhất tôi tìm thấy để lấy QueryHashgiá trị cần tìm là dán truy vấn được đề cập vào Cửa sổ truy vấn và sau đó Hiển thị Kế hoạch thực hiện ước tính. Đọc đầu ra XML và tìm QueryHashthuộc tính trong StmtSimplephần tử và điều này sẽ cung cấp cho bạn những gì bạn cần. Cắm giá trị QueryHash vào truy vấn ở trên và hy vọng bạn sẽ có thứ bạn đang tìm kiếm.

Dưới đây là một số ảnh chụp màn hình cho thấy cách nhanh chóng nhận được QueryHashgiá trị trong trường hợp tôi giải thích nó kém.

Hiển thị kế hoạch thực hiện dự kiến

nhập mô tả hình ảnh ở đây

Hiển thị kế hoạch thực hiện XM ...

nhập mô tả hình ảnh ở đây

Tìm kiếm Giá trị QueryHash

nhập mô tả hình ảnh ở đây

Rõ ràng là mẹo sẽ không hoạt động nếu truy vấn bạn đang tìm khác với truy vấn bạn đang hiển thị Kế hoạch thực hiện ước tính, nhưng điều này có thể nhanh hơn tất cả các sắc thái đi kèm với thói quen CLR và khiến chúng hoạt động bình thường.


0

Nếu bạn có quyền truy cập vào các văn bản truy vấn (có nghĩa là bạn có thể sửa đổi chúng), bạn có thể thêm nhận xét duy nhất cho những người bạn quan tâm:

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

sau đó tìm kiếm myUniqueQuery123trong bộ đệm kế hoạch thay vì toàn bộ văn bản truy vấn:

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

Tái bút Không được kiểm tra

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.