2017-01-03 32 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)基於條件臨時禁用觸發器。是的,正如我在答案中所顯示的那樣,這是可能的,它確實有效:-)。 –

相關問題