Trong SQL Server, có một cách để xác định các giá trị của các tham số được truyền cho một thủ tục được lưu trữ đang thực thi


13

Một cách để xác định thủ tục lưu trữ thực thi là sử dụng các phương thức "quản lý động", như vậy:

SELECT 
    sqlText.Text, req.* 
FROM 
    sys.dm_exec_requests req
OUTER APPLY 
    sys.dm_exec_sql_text(req.sql_handle) AS sqltext

Tuy nhiên, điều này chỉ hiển thị văn bản của câu lệnh tạo của thủ tục được lưu trữ. ví dụ:

CREATE PROCEDURE IMaProcedure @id int AS SELECT * FROM AllTheThings Where id = @id

Lý tưởng nhất là tôi muốn xem các tham số nào cho quy trình chạy đang khiến nó chạy quá lâu đối với các tham số vi phạm cụ thể.

Có cách nào làm được việc này không? (Trong câu hỏi này Aaron Bertrand có đề cập đến DBCC InputBuffer , nhưng tôi không nghĩ rằng điều đó phù hợp cho vấn đề này.)


Thực sự cách duy nhất để nắm bắt các tham số đầu vào hoặc xem những gì đã được truyền vào trong thời gian chạy là ghi nhật ký các giá trị và cuộc gọi đến một tệp nhật ký. Bạn có thể thực hiện việc này một cách dễ dàng với RAISEERROR nếu bạn muốn thấy nó trong nhật ký lỗi hoặc với một chút nỗ lực hơn, hãy viết nó ra một tệp bên ngoài ở đâu đó.
Steve Mangiameli

Câu trả lời:


16

Thông tin này - các giá trị tham số thời gian chạy được chuyển vào Thủ tục lưu trữ (tức là cuộc gọi RPC) hoặc truy vấn được tham số hóa - chỉ khả dụng qua Dấu vết SQL (và tôi giả sử Sự kiện mở rộng tương đương trong các phiên bản SQL Server mới hơn). Bạn có thể thấy điều này bằng cách chạy SQL Server Profiler (nó đi kèm với SQL Server) và chọn khác nhau "Hoàn thành" sự kiện, chẳng hạn như: RPC:Completed, SP:Completed, và SQL:BatchCompleted. Bạn cũng cần chọn trường "TextData" vì các giá trị sẽ ở đó.

Sự khác biệt giữa câu trả lời của tôi và @ Kin của câu trả lời về câu hỏi này là câu trả lời @ Kin (trừ khi tôi nhầm, trong trường hợp này tôi sẽ loại bỏ này) tập trung vào việc một trong hai:

  • kế hoạch truy vấn của riêng bạn (trong trường hợp đó có thể có thông tin tham số thời gian chạy trong đó, nhưng không dành cho các Phiên / SPID khác), hoặc
  • các kế hoạch từ DMV (trong trường hợp đó chúng chỉ nên có các giá trị tham số được biên dịch, không phải là giá trị thời gian chạy).

Câu trả lời của tôi tập trung vào việc nhận các giá trị tham số cho các phiên khác hiện đang chạy. Khi dựa vào DMV, không có cách nào để biết liệu giá trị tham số thời gian chạy có giống với giá trị tham số đã biên dịch hay không. Và bối cảnh của câu hỏi này là theo dõi giá trị thời gian chạy của các truy vấn được gửi qua các Phiên / SPID khác (và trong SQL Server 2005, trong khi Sự kiện mở rộng đã được giới thiệu trong SQL Server 2008).


13

Bạn có thể bật kế hoạch thực hiện thực tế và sau đó xem xét kế hoạch thực hiện XML.

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

Hoặc bạn có thể sử dụng công cụ thám hiểm kế hoạch của sql sentry và xem parameterstab sẽ liệt kê compiled valuerun time valuecho kế hoạch thực hiện thực tế.

Nếu bạn không thể bật kế hoạch thực tế thì bạn có thể xem xét bộ nhớ cache của kế hoạch như được mô tả dưới đây.

-- borrowed from  Erland Sommarskog
-- Link : http://www.sommarskog.se/query-plan-mysteries.html#dmvgettingplans
-- Remember that you are looking at the estimated plan so the actual no. of rows and actual executions wont be there ! <-- Important why a particular plan is bad.

DECLARE @dbname    nvarchar(256),
        @procname  nvarchar(256)
SELECT @dbname = 'Northwind',  -- Your DB name
       @procname = 'dbo.List_orders_11' -- The SP that you want to get parameters for !

; WITH basedata AS (
   SELECT qs.statement_start_offset/2 AS stmt_start,
          qs.statement_end_offset/2 AS stmt_end,
          est.encrypted AS isencrypted, est.text AS sqltext,
          epa.value AS set_options, qp.query_plan,
          charindex('<ParameterList>', qp.query_plan) + len('<ParameterList>')
             AS paramstart,
          charindex('</ParameterList>', qp.query_plan) AS paramend
   FROM   sys.dm_exec_query_stats qs
   CROSS  APPLY sys.dm_exec_sql_text(qs.sql_handle) est
   CROSS  APPLY sys.dm_exec_text_query_plan(qs.plan_handle,
                                            qs.statement_start_offset,
                                            qs.statement_end_offset) qp
   CROSS  APPLY sys.dm_exec_plan_attributes(qs.plan_handle) epa
   WHERE  est.objectid  = object_id (@procname)
     AND  est.dbid      = db_id(@dbname)
     AND  epa.attribute = 'set_options'
), next_level AS (
   SELECT stmt_start, set_options, query_plan,
          CASE WHEN isencrypted = 1 THEN '-- ENCRYPTED'
               WHEN stmt_start >= 0
               THEN substring(sqltext, stmt_start + 1,
                              CASE stmt_end
                                   WHEN 0 THEN datalength(sqltext)
                                   ELSE stmt_end - stmt_start + 1
                              END)
          END AS Statement,
          CASE WHEN paramend > paramstart
               THEN CAST (substring(query_plan, paramstart,
                                   paramend - paramstart) AS xml)
          END AS params
   FROM   basedata
)
SELECT set_options AS [SET], n.stmt_start AS Pos, n.Statement,
       CR.c.value('@Column', 'nvarchar(128)') AS Parameter,
       CR.c.value('@ParameterCompiledValue', 'nvarchar(128)') AS [Sniffed Value],
       CAST (query_plan AS xml) AS [Query plan]
FROM   next_level n
CROSS  APPLY   n.params.nodes('ColumnReference') AS CR(c)
ORDER  BY n.set_options, n.stmt_start, Parameter

5
Bộ đệm kế hoạch chỉ có các giá trị được biên dịch thay vì các giá trị cho một lần chạy cụ thể sau này. Cũng có thể sử dụng Showplan XML Statistics Profilesự kiện trong Profiler để có được kế hoạch thực tế mặc dù nếu loại bỏ Profiler thì sẽ có những cách ít chuyên sâu hơn để có được điều này.
Martin Smith

1

@SolomonRutzky nói đúng.
SQL Profiler Trace là cách duy nhất ( không cần chỉnh sửa Sproc ).

Chỉnh sửa Sproc của bạn:

Tuy nhiên , điều tốt nhất tiếp theo là chỉnh sửa một chút Sproc trong câu hỏi.
Khai báo một biến DateTime khi bắt đầu với Thời gian hiện tại.
Khi kết thúc Sproc, Đăng nhập Sproc_StartTime, Sproc_EndTime và Giá trị tham số vào bảng.

Bạn thậm chí có thể thêm một số logic có điều kiện để sử dụng DateDiff () để chỉ ghi nhật ký khi một lượng thời gian kéo dài được sử dụng để xử lý Sproc.
Điều này có thể tăng tốc Sproc của bạn và giảm mức tiêu thụ không gian của Bảng nhật ký của bạn khi Sproc đang chạy top-top.

Sau đó, bạn có Tệp nhật ký mà bạn có thể truy vấn và phân tích qua nhiều tháng (không có Dấu vết chạy trong Prod).
Khi bạn hoàn thành điều chỉnh Sproc của mình, chỉ cần xóa một vài dòng logic Timer và Logger mà bạn đã thêm.

Các giá trị tham số được lưu trong bộ nhớ cache:

Tôi nên đề cập rằng bao gồm các Giá trị tham số kế hoạch lưu trữ hiện tại trong Bảng nhật ký của bạn có thể giúp bạn xác định xem chúng có phải là vấn đề hiệu năng hay không .
Tôi sử dụng OPTIMIZE FORđể đặt cách xử lý Thông số trong Sproc của mình khi tôi biết nó sẽ được sử dụng để cắt và cắt dữ liệu.
Tôi thấy rằng việc sử dụng OPTIMIZE FORmang lại kết quả nhất quán và nhanh chóng khi sử dụng cùng một Sproc với Tham số như Bộ lọc tùy chọn .
Đó chắc chắn là một biến ít hơn để xem xét nếu bạn chỉ định cách xử lý chúng.

Dưới đây là một ví dụ về những gì bạn có thể thêm vào dưới cùng của Tuyên bố chọn:

OPTION(OPTIMIZE FOR (@SiteID = 'ABC',
                     @LocationID = NULL, @DepartmentID = NULL,
                     @EmployeeID = NULL, @CustomerID = NULL,
                     @ProductID = NULL, @OrderID = NULL, @OrderStatusID = NULL,
                     @IncludedCancelledOrders = 1,
                     @StartDate UNKNOWN, @EndDate UNKNOWN))

0

Tôi nhận thấy khi sử dụng truy vấn của Erland Sommarskog để hủy kế hoạch XML và rút ra ParameterCompiledValue rằng CTE "dựa trên" đầu tiên không tính đến các kế hoạch có CẢNH BÁO (ví dụ: chuyển đổi ngầm định) vì CHARINDEX (hàm dựng sẵn) tìm chuỗi biểu thức phù hợp đầu tiên đầu vào (tức là) và các cảnh báo như vậy sử dụng các cụm từ / nút tương tự.

Do đó, tôi đề xuất thay thế phần này bằng phần sửa đổi dưới đây:

      CHARINDEX('<ParameterList>', qp.query_plan) + LEN('<ParameterList>') AS paramstart,
      CHARINDEX('</ParameterList>', qp.query_plan) AS paramend

Phần sửa đổi:

       CHARINDEX('<ParameterList><ColumnReference', qp.query_plan) + LEN('<ParameterList>') AS paramstart,
       CHARINDEX('</ParameterList></QueryPlan>', qp.query_plan) AS paramend

Disallowed implicit conversion from data type xml to data type varchar, table 'sys.dm_exec_query_plan', column 'query_plan'. Use the CONVERT function to run this query.
Matt

-1
SELECT DB_NAME(req.database_id),
sqltext.TEXT,
req.session_id,
req.status,
req.start_time,
req.command,
req.cpu_time,
req.total_elapsed_time ,   REPLACE(REPLACE(REPLACE(REPLACE(
CONVERT(VARCHAR(MAX), CONVERT(XML, REPLACE( query_plan, 'xmlns="','xmlns1="')).query('//        ParameterList/ColumnReference')),
'<ColumnReference Column="','declare '),
'" ParameterDataType="',' '),
'" ParameterCompiledValue="(',' = '),
')"/>', CONCAT(';', CHAR(10) , CHAR(13))) ParameterList
FROM sys.dm_exec_requests req
CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext 
 CROSS  APPLY sys.dm_exec_text_query_plan(plan_handle, statement_start_offset, statement_end_offset) qp
order by req.total_elapsed_time desc 

2
Mã chỉ trả lời được khuyến khích. Xem xét thêm một lời giải thích tại sao mã này giải quyết vấn đề. Xem cách trả lời
Peter Vandivier
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.