微軟BI 之SSIS 系列 - Execute SQL Task 中的 Single Row 與 Full Result Set 的處理技巧


開篇介紹

Execute SQL Task 這個控件在微軟BI ETL 項目中使用的頻率還是非常高的,也是大部分入門 SSIS 初學者最早接觸到的幾個控制流控件。
我們通常使用 Execute SQL Task 的場景包含但不止於以下幾類:
  1. 在從源端加載數據到 Staging 表之前使用 Execute SQL Task 執行一些 Truncate 操作。
  2. 執行一些 Log 的插入,更新操作。
  3. ETL 過程中的 Merge 語句操作。
  4. XML 的輸出處理。
關於如何使用 Execute SQL Task 就不在本文講解了,包括參數傳遞 Input,存儲過程返回值 Output,包括 Return Value ,還有 SQL Source Type 等不同方式以及 OLD DB,ADO.NET 區別等基礎概念文本也不會涉及到。
 
本文要講解的內容是針對 ResultSet 的幾種類型:None, Single row, Full result set, XML。其中 None 不用講解,關於 XML 的內容大家可以參考我的另一篇文章 兩種將 SQL Server 數據庫數據輸出成 XML 文件的方法,因此在這里只講解 Single Row 和 Full Result Set 的處理方法。

Single Row  

首先,Single Row 指的就是在 Execute SQL Task 中返回的就是一個單行的記錄,允許多列。
通常在 Execute SQL Task 中使用 Single Row 的時候是為了在控制流中控制流程的流轉,滿足一定的條件走一個分支流程,不滿足則走另外一個流程。
比如,在包執行之初查詢數據庫檢查一下包的某些狀態,或者數據的某些狀態,只有在滿足達到一定條件下才能執行剩下的流程,否則則不執行包或者報錯。這類設計在各種不同的BI項目中可能都存在,那么其原型就是利用 Execute SQL Task 中的 Single Row返回的值來控制流程。
有以下幾個步驟:
  1. SQL Statement 中的語句返回單行記錄,可以多列。
  2. 需要在包中實現定義變量用來接收 Single Row 的某列上的值。
  3. 使用 Precedence Constraint 和變量來控制流程。

Single Row 案例

一條簡單的查詢語句返回一個單行雙列的記錄。
設置好連接並放好SQL語句,選擇 Single Row。
添加三個變量
  1. EXISTING_COUNT - 用來接收查詢中的返回的COUNT數。
  2. MAX_DATE - 用來接收查詢中返回的最大時間。
  3. TEST_DATE - 測試時間,比如說 2014-01-01。

0 表示 Single Row 中的第一列,1 表示 Single Row 中的第二列。
下面放兩個空的 Data Flow Task 並關聯到 Execute SQL Task,點擊連接線設置條件控制。
DFT_TEST_AFTER_MAX 上的連接線條件判斷為:DATEDIFF( "dd", (DT_DBDATE) (@[User::TEST_DATE]) , (DT_DBDATE) (@[User::MAX_DATE])) >=0
DFT_TEST_BEFORE_MAX上的判斷表達式為:DATEDIFF( "dd", (DT_DBDATE) (@[User::TEST_DATE]) , (DT_DBDATE) (@[User::MAX_DATE])) <0
左右兩個數據流下各加入一個 Script Task 顯示一下這幾個變量的值,代碼如下:
public void Main()
  {
   // TODO: Add your code here
            //User::EXISTING_COUNT,User::MAX_DATE,User::TEST_DATE
            string existing_count = Dts.Variables["User::EXISTING_COUNT"].Value.ToString();
            string max_date = Dts.Variables["User::MAX_DATE"].Value.ToString();
            string test_date = Dts.Variables["User::TEST_DATE"].Value.ToString();

            MessageBox.Show("Existing Count - " + existing_count + ", test date - " + test_date + ", max date - " + max_date);
   Dts.TaskResult = (int)ScriptResults.Success;
  }

保存並執行包,流程走了右邊,因為 MAX DATE 要比 TEST DATE 大。

修改 TEST_DATE,那么 TEST DATE 要比 MAX DATE 大。
這就是 Execute SQL Task 中 Single Row 的使用方法。

FULL Result Set

通常在 Execute SQL Task 中使用到 FULL Result Set 就一定會結合 Foreach Loop 來使用,所適用的場景是循環便利查詢結果集中的每一行數據,將每一行的數據其中某幾列取出來放入到變量中,然后同樣在 Foreach Loop 中的其它控制流控件使用這些變量做一些操作。

疑問

像這種遍歷為什么不放到數據庫中直接遍歷不是更好嗎?問題就在於,有時操作的對象並不僅僅是數據庫中的表對象,而是涉及到不同的文件處理,這時就需要使用這種方式了。比如,我的目標數據源中有幾百個文件,且文件的命名包括產品的名稱,現在表中有產品名稱的記錄。需要循環遍歷表中的產品,並對比哪些文件命名包含有這些產品名稱,包含產品名稱的文件則抽取數據,不包含產品名稱的文件則移到其它目錄。
下面使用這個查詢作為一個示例結果集 -
 
使用 FULL RESULT SET  
定義如下幾個變量 - ORDER_SET 用來保存 Execute SQL Task 查詢中的結果集,其它變量用來在循環每一行的時候保存每一列的值。注意:返回的結果集要使用 OBJECT 類型的變量來保存,這個 OBJECT 類型在內部以集合的形式存在並保存整個查詢結果集。
EXECUTE SQL TASK 的 Mapping。

Foreach 下的設置

我們可以通過 Foreach 的方式循環遍歷這個返回的集合。添加一個 Foreach 控件,並設置遍歷方式 - Foreach ADO Enumerator 和要遍歷的集合對象。
遍歷這個集合的時候,每次返回一行,這一行也是一個集合,通過設置索引0,1,2 將這個集合的元素賦值給各個變量。
在 Foreach 控件中添加一個 Script Task 用來顯示每一行中各列的內容。
Script 中的腳本代碼如下:
public void Main()
  {
   // TODO: Add your code here
            //User::PRODUCT_ID,User::SALES_ORDER_DETAIL_ID,User::UNIT_PRICE
            string detailID = Dts.Variables["User::SALES_ORDER_DETAIL_ID"].Value.ToString();
            string productID = Dts.Variables["User::PRODUCT_ID"].Value.ToString();
            string unitPrice = Dts.Variables["User::UNIT_PRICE"].Value.ToString();

            MessageBox.Show("Detail ID - " + detailID+", Product ID - "+productID +", Unite Price - "+ unitPrice);
   Dts.TaskResult = (int)ScriptResults.Success;
  }

保存並執行包,第一次循環的結果是失敗的,但是可以看出來我們的結果集返回是沒有問題的。

出錯的原因如下:
Error: The type of the value (String) being assigned to variable "User::SALES_ORDER_DETAIL_ID" differs from the current variable type (Int32). Variables may not change type during execution. Variable types are strict, except for variables of type Object.
Error: The type of the value (String) being assigned to variable "User::PRODUCT_ID" differs from the current variable type (Int32). Variables may not change type during execution. Variable types are strict, except for variables of type Object.
Error: The type of the value (String) being assigned to variable "User::UNIT_PRICE" differs from the current variable type (Decimal). Variables may not change type during execution. Variable types are strict, except for variables of type Object.
這是由於在集合中的數據默認都是 String 類型的,是不能夠直接轉換成我們定義的變量所指定的類型,因此需要修改我們的變量類型。
再次執行就可以看到遍歷的效果,第一次 -

最后一次 - 

Script Task 中的循環遍歷

同樣的 Object 對象,不僅僅可以在 Foreach 中循環遍歷,也可以直接在 Script Task 中循環遍歷。
添加一個 Script Task,把結果集對象放入變量列表中。
腳本代碼如下 - 
using System.Data.OleDb;

public void Main()
  {
   // TODO: Add your code here 
            OleDbDataAdapter adapter = new OleDbDataAdapter();
            DataTable dataTable = new DataTable(); 

            adapter.Fill(dataTable, Dts.Variables["User::ORDER_SET"].Value);

            foreach (DataRow row in dataTable.Rows)
            {
                MessageBox.Show(row[0] + "," + row[1] + "," + row[0]); 
            } 

   Dts.TaskResult = (int)ScriptResults.Success;
  }

一樣可以實現遍歷的效果 -

最后一條記錄-

總結

通過講解 Execute SQL Task 的兩種查詢結果返回方式 Single Row 和 Full Result Set,實際上由此引申出了何時使用 Single Row 以及 Full Result Set 的場景。熟悉和掌握這些場景可以幫助我們在復雜的 ETL 項目中找出各種不同的解決方案,可以非常靈活的解決一些實際問題。
再次,特別通過 Foreach Loop Container 以及 Script Task 學習到了兩種解析 Full Result Set 的方式,其中涉及到的知識點以及細節還是比較多的,特意總結下來希望可以幫助到大家。

更多 BI 文章請參看 BI 系列隨筆列表 (SSIS, SSRS, SSAS, MDX, SQL Server)  如果覺得這篇文章看了對您有幫助,請幫助推薦,以方便他人在 BIWORK 博客推薦欄中快速看到這些文章。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM