跟踪或记录对SQL Server中用户定义函数的调用

时间:2017-01-17 16:47:16

标签: sql-server user-defined-functions udf

了解在用户定义的函数中不允许使用副作用运算符(例如" insert"),如何记录(或以其他方式跟踪)对特定用户定义函数的调用?我还想捕获传递给UDF的参数。

理想情况下,日志将是一个表,其中插入了有关每次UDF调用的信息(时间戳和参数值)。然后可以从该表中导出报告和使用指标。

我不能将UDF重写为存储过程,即使是同名,也不会破坏许多期望UDF并且无法控制的下游系统。

我也不愿意在我们的服务器上启用任何类型的命令shell功能,这会降低SQL Server的最佳实践安全默认值。

2 个答案:

答案 0 :(得分:11)

我找到了问题的解决方案。它有点棘手,看起来像黑客,但似乎不可能以另一种方式解决。

我们的想法是创建一个.NET SQL函数,在需要的地方记录数据(文件,Windows EventLog,db等),然后创建调用此.NET函数的SQL UDF,最后从函数中调用此SQL函数传递所有需要记录的参数。 SQL Server没有检查.net函数内部是什么,你可以在那里写下你需要的所有逻辑。

如何在没有任何安全限制的情况下创建.net SQL函数的想法取自post

因此,使用这个文件

创建一个.net库项目
using System;

namespace SqlTest
{
 public class LogEvent
 {
    [Microsoft.SqlServer.Server.SqlFunction]
    public static int Log(string data)
    {
        System.IO.File.AppendAllText(@"C:\Log\LogUDF.txt", data);
        return 0;
    }
 }
}

使用某些pfx证书(项目属性 - >签名标签)进行签名。

接下来,调用此查询

USE [master]
CREATE ASYMMETRIC KEY LogKey FROM EXECUTABLE FILE =
'C:\Work\ConsoleApplication1\SqlTest\bin\Debug\SqlTest.dll'

CREATE LOGIN LogLogin FROM ASYMMETRIC KEY LogKey

GRANT UNSAFE ASSEMBLY TO LogLogin 
GO

USE [MyDB]
CREATE ASSEMBLY SqlTest FROM
'C:\Work\ConsoleApplication1\SqlTest\bin\Debug\SqlTest.dll' 
WITH PERMISSION_SET = unsafe
GO
CREATE FUNCTION dbo.Log( @data as nvarchar(200) )
RETURNS int
AS EXTERNAL NAME SqlTest.[SqlTest.LogEvent].Log

在这里,您需要更改已编译库的路径,MyDB - 您的数据库名称。 并且您将创建dbo.Log SQL函数。接下来,您可以在需要的地方拨打电话。例如,来自这个TestFunction

CREATE FUNCTION TestFunction
(
    @p1 int
)
RETURNS int
AS
BEGIN
    DECLARE @temp int
    SELECT @temp = [dbo].[Log] ('fff')
    RETURN 1
END

因此,调用SELECT TestFunction(1)会将'fff'文本写入C:\Log\LogUDF.txt文件。

就是这样。一些重要的注意事项:

  1. SQL Server应具有写入文件C:\Log\LogUDF.txt
  2. 的权限(登录/用户)
  3. 您应该是SQL server admin

答案 1 :(得分:1)

您可以尝试以下操作:

1)使用SQL事件探查器检查每个不同场景的捕获数据

检查SP:StmtCompleted以确保捕获在存储过程中执行的语句或使用已定义的函数。还要确保包含所有必需的列(TextData,LoginName,ApplicationName等)。 TextData对此解决方案至关重要。

2)检查每个方案以查看您在探查器中收到的内容。 E.g:

-- a mock function that is similar to what I have understood your function does
alter FUNCTION dbo.GetLoginResult(@username VARCHAR(64))
    RETURNS INT
AS
BEGIN
    DECLARE @l INT = LEN(@username)

    IF (@l < 10)
        RETURN 0

    RETURN 1

    -- DECLARE @Result INT
    -- SELECT @Result = DATEPART(s, GETDATE()) % 3
    -- RETURN @Result
END
go

select dbo.GetLoginResult('SomeGuy')  --> `IF (@l < 10)` and `RETURN 0`
GO

select dbo.GetLoginResult('Some girl with a long name')  --> `IF (@l < 10)` and `RETURN 1`
GO

因此,如果您可以调整您的函数以便在将要返回特定输出时执行特定指令的方式,您可以根据配置文件信息识别函数的结果是什么(因为输入和输出值似乎没有在分析器中捕获)

3)服务器端跟踪

如前所述,SQL事件探查器会产生很大的开销,因此您应该使用服务器端跟踪。幸运的是,您可以导出刚刚创建的分析信息here

i)SQL Profiler - &gt;档案 - &gt;出口 - &gt;脚本跟踪定义 - &gt;对于SQL Server ..

ii)替换生成的脚本中的路径并运行它 - &gt;记住生成的id(它是跟踪id)

iii)您可以在分析器中打开文件并在停止跟踪后将其数据导出到表中(它由sqlserver进程锁定)。