2017-01-03 31 views
5

我创建调用这样的组件的触发器:如何将参数传递给外部(SQLCLR)SQL服务器触发

CREATE TRIGGER Testrigger ON STATION 
FOR INSERT 
AS EXTERNAL NAME assemblytest.[WriteTimeInfile.Program].Testrigger 

在这集的.NET代码,不会是这样的:

namespace WriteTimeInfile 
{ 
    public class Program 
    { 
     [SqlTrigger(Name = @"Testrigger", Target = "[dbo].[STATION]", Event = "FOR INSERT, UPDATE, DELETE")] 
     public static void Testrigger() 
     { 
      File.AppendAllText(@"C:\Users\Vivien\date.txt", 
      DateTime.Now.ToString() + Environment.NewLine); 
     } 
    } 
} 

我希望能够通过,作为参数,创建行或更新的行是这样的:

CREATE TRIGGER Testrigger ON STATION 
AFTER INSERT 
AS 
EXTERNAL NAME assemblytest.[WriteTimeInfile.Program].Testrigger (STATION.ID) 

我在StackOverflow上发现了一个7岁的topic,它告诉我们无法将参数传递给CLR程序集。
我问是否现在可以在最近的SQL Server版本中使用。

你知道有没有办法,如果是的话,请问怎么办?

+0

您总是可以从SQLCLR触发器中访问INSERTED和DELETED表,[如此旧版本的文档所示](https://msdn.microsoft.com/zh-cn/library/938d9dz2(v = VS.90)的.aspx)。您不需要将任何参数传递给触发器,就像无需将任何参数传递给正常触发器一样 –

+0

*为什么要将参数传递给触发器?什么样的参数?为什么不使用存储过程呢?如果要使用更改检测实现某种ETL功能,可以使用更改跟踪(而不是CDC)来查找密钥并更改修改行的类型并处理它们。 –

回答

3

不,您不能直接将参数传递给SQLCLR触发器。你可以,但是,间接地在几个方面传递值(等同于与普通T-SQL触发器):

  1. 本地临时表
  2. SET CONTEXT_INFO/CONTEXT_INFO
  3. 在SQL服务器2016或更高版本:sp_set_session_context/SESSION_CONTEXT

在所有情况下,你会通过执行SqlCommand具有输出SqlParameter的值拉入.NET代码获取值。 (请参阅最后关于使用情况的注释)。

但是,如果您只是想要inserted和/或deleted表的值,那些将不会是参数或参数。只是SELECT那些使用SqlCommand使用Context Connection = true作为连接字符串,而SqlDataReader。您可以在的MSDN页面的Sample CLR Trigger部分中看到此示例。


关于不属于DML操作的一部分将值传递给触发器注:

虽然它不是共同做的,当然也有有效的用例通过一块从主要上下文到事件链中的一个或多个触发器的信息。我遇到的两种最常见的情况是:1)将基于应用程序的登录或用户ID(不是SQL Server的一部分)传递给审计触发器以删除行(由于该信息无法添加到DELETE中的ModifiedBy列操作),以及2)基于条件临时禁用触发器。是的,这是可能的,它确实有效。请在DBA上查看我的以下答案。StackExchange:

+0

试图将触发器用作存储过程,可能意味着它们不应该首先触发 –

+0

@PanagiotisKanavos如果这是一个试图使用触发器的情况,就好像它们是存储过程那么我会同意你的:-)。但是肯定有使用触发器的情况,因为它们应该被使用,但仍然有时会遇到这种需求。 –

+1

@srutzky谢谢你,你正在成为我的官方问题解决者 –

1

inserted和deleted伪表总是可用于SQLCLR触发器中直接查询,如在文档的this 9+ years old version。你可以随时查询它们,例如:

using (SqlConnection conn = new SqlConnection("context connection=true")) 
{ 
    conn.Open(); 
    SqlCommand sqlComm = new SqlCommand(); 
    SqlPipe sqlP = SqlContext.Pipe; 

    sqlComm.Connection = conn; 
    sqlComm.CommandText = "SELECT UserName from INSERTED"; 

    userName.Value = sqlComm.ExecuteScalar().ToString(); 

    if (IsEMailAddress(userName.Value.ToString())) 
    { 
     sqlComm.Parameters.Add(userName); 
     sqlComm.CommandText = "INSERT UsersAudit(UserName) VALUES(@username)"; 
     sqlP.Send(sqlComm.CommandText); 
     sqlP.ExecuteAndSend(sqlComm); 
    } 
} 

最新的样品是一样的。

您不需要将它们作为参数传递,就像您不需要将它们作为参数传递给正常触发器一样。这些表总是可用于查询。

我怀疑这些表没有公开,例如上下文对象上的集合,因为这需要从SQL Server的缓冲区中复制它们(浪费CPU和内存)。另一个原因是收集不能被有效查询。你可能不得不使用LINQ(使用更多的CPU)或者只是迭代整个内容(内存和CPU浪费)。内存浪费将是更大的问题,因为这个内存可以用来缓冲更多的数据和索引,从而加速访问。

我怀疑你链接到的问题想问同样的问题,但OP认为伪表必须作为参数传递。所以他问了一些参数而不是实际的问题。

+0

谢谢,这是很好的知道,但我希望能够通过更新行或任何参数的参数,以及 –

+0

你不能这样做与任何无论如何都会触发,因为它没有任何意义。触发器由服务器执行,因此您无法操作它们之外的值。至于**行**(复数),这些已经可用。 *复数*是重要部分。它们可以是INSERTED或DELETED表中的一行或一百万行。语法会发生变化,但访问表的方式仍然相同 –

+0

@VivienPipo和Panagiotis:虽然这不是很常见,但确实有用例将一条信息从主要上下文传递到一个或多个触发事件链。我遇到的两个最常见的情况是:1)传递基于应用程序的Login或UserID(不是SQL Server的一部分)以删除删除行的人(因为该信息无法添加到DELETE中的ModifiedBy列)操作),以及2)基于条件临时禁用触发器。是的,正如我在答案中所显示的那样,这是可能的,它确实有效:-)。 –

相关问题