THE SQL Server Blog Spot on the Web
Welcome to SQLblog.com - The SQL Server blog spot on the web Sign in | Join | Help
in Search

Andy Leonard

SSIS and ETL
Thoughts about Database and Software Development, and the tools of the trade.

SSIS Design Pattern - Read a DataSet From Variable In a Script Task

Introduction

One of the things I appreciate about SSIS is the script elements. There is a lot of functionality built into the engine out of the box but I like the idea of having a Script Task on the Control Flow and a Script Component on the Data Flow just in case.

Last month I used a Script Task to build a more flexible FTP client class. Last week I re-used that code to perform a bunch of uploads. I had a list of files to upload in a dataset stored inside an SSIS Object variable. I could have pulled my FTP client Script Task into a ForEach loop and simply iterated the Object variable with the ADO Enumerator, but I decided against it for a couple reasons:

1. What fun would that be?
2. Seriously, I wanted to avoid the overhead of opening and closing the FTP connection once for each file. I wanted to open the connection, log in, send all the files in a list, then close the connection.

A Note About The ActiveX Script Task

Don't use it.

Why? It has "I'm not going to be around much longer" written all over it. The only time I use this task is during DTS conversion.

Use the Script Task. .Net is fun. You'll like it, I promise.

On To The Project!

Poking around the blogosphere, I found some good information and nifty code written by Jamie Thomson to get me started. Note: There are two types of SSIS developers, those who read Jamie Thomson's blog and those who will.

Follow these steps to build an SSIS project that demonstrates how to read (or shred) a dataset in an SSIS Object variable from a Script Task.

First, create a new SSIS project. I called mine "DatasetVariableInScriptDemo". When the project opens, drag an Execute SQL Task and Script Task onto the Control Flow canvas.  

Double-click the Execute SQL Task to open the editor. Click the Connection property and define a new connection (<New connection...>) to your local AdventureWorks database. (Note: If you do not have the AdventureWorks sample databases installed, you can download them here.)

Set the SQLStatement property by clicking the ellipsis in the SQL Statement textbox and adding the following code to the "Enter SQL Query popup:

SELECT Top 10
  ContactID
 ,Title
 ,FirstName
 ,LastName
FROM Person.Contact

This query returns ten rows from the Person.Contact table.

Set the ResultSet property to "Full result set".

 

Click "Result Set" from the list on the left to map the result set properties to variables. Click the Add button and change the NewResultName text to "0". Under Variable Name, click "<New variable...>" and define a new package-scoped, Object type variable named dsVar. Click the OK button to close the Add Variable dialog and create the variable.

Click the OK button to close the Execute SQL Task editor.

Connect the Execute SQL Task to the Script Task by dragging an Execute SQL Task precedence constraint (the green arrow visible when you click the Execute SQL Task) to the Script Task.

Double-click the Script Task to open the editor. Click Script from the list on the left to open the Script property page. Add dsVar to the ReadOnlyVariables property. 

 

Click the Design Script button to open the Visual Studio for Applications editor.

Open the Project Explorer. Right-click the References logical folder and click Add Reference. Select System.XML and click the Add button, then click the OK button to add a System.XML reference.

 

Return to the script editor and replace the supplied code with the following:

Imports System
Imports System.Data
Imports System.Math
Imports Microsoft.SqlServer.Dts.Runtime
Imports System.Xml
Imports System.Data.OleDb

Public Class ScriptMain

 
Public Sub Main()

 
Dim oleDA As New OleDbDataAdapter
  Dim dt As New DataTable
  Dim col As DataColumn
  Dim row As DataRow
  Dim sMsg As String

  oleDA.Fill(dt, Dts.Variables("dsVar").Value)

  For Each row In dt.Rows
   For Each col In dt.Columns
    sMsg = sMsg & col.ColumnName &
": " & _
           
row(col.Ordinal).ToString & vbCrLf
   Next
   MsgBox(sMsg)
   sMsg = ""
  Next

  Dts.TaskResult = Dts.Results.Success

 End Sub

End
Class

This script uses an OLEDbDataAdapter (oleDA) to fill a DataTable (dt) with the contents of the dsVar SSIS package variable, then iterates each row and column to build a string containing the data in the row. It then pops up a messagebox for each row displaying the row's contents before moving to the next row.

Your code would replace the lines that build and display the message.

Conclusion

I like the flexibility offered here. Because of this approach I was able to accomplish my goal of building and re-using a more flexible FTP client in an SSIS Script Task.

Get the code!

:{> Andy

Technorati Tags:


Published Sunday, October 14, 2007 6:29 AM by andyleonard
Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Loosely Coupled Human Code Factory said:

While ramping back up for heavy duty OLAP &amp; BI work there where a few names that kept popping up. ...

October 22, 2007 3:41 PM
 

Darren Harvey said:

An absolute gem of a snippet of code

many thanks

October 24, 2007 7:09 AM
 

Hery said:

Why we have to add System.XML when we want to use DataSet ? I found no explanation on this article. Thanks.

October 25, 2007 1:20 AM
 

andyleonard said:

Hi Hery,

  Apologies for neglecting to explain the need for a reference to System.XML. The DataTable object is part of the System.XML namespace. You can see a screenshot of the script without the reference at http://vsteamsystemcentral.com/images/ext/SystemXML.png.

:{> Andy

October 29, 2007 6:30 AM
 

Alex said:

Damn Andy, you're on the cover my SSIS book.

November 9, 2007 4:20 PM
 

andyleonard said:

Alex,

  That picture adds functionality to the book. You can use the cover to keep the crows out of the corn!

:{> Andy

November 10, 2007 4:55 AM
 

Loosely Coupled Human Code Factory said:

While ramping back up for heavy duty OLAP & BI work there where a few names that kept popping up. These...

November 21, 2007 6:25 PM
 

sandeep said:

thanks andy.

really useful

:)

cheers

November 30, 2007 12:52 PM
 

design patterns said:

December 7, 2007 8:55 AM
 

RP said:

Really good andy...  I've just created an e-mail notification SSIS using the above... Thanks.

January 22, 2008 8:10 PM
 

andyleonard said:

Thanks RP!

  That's a good implementation idea! There's so many cool uses for this functionality. Kudos to Jamie Thomson for doing the heavy lifting on this!

:{> Andy

January 23, 2008 9:34 AM
 

Kenan said:

Hi ,

I made some changes below to the code that it will be more useful if we are talking about DataSet.

Public Sub Main()

       Dim dt As New DataTable

       Dim col As DataColumn

       Dim row As DataRow

       Dim results As DataSet

       Dim sMsg As String

       Try

           results = CType(Dts.Variables("dsVar").Value, DataSet)

           For Each tbl As DataTable In results.Tables

               For Each row In tbl.Rows

                   For Each col In tbl.Columns

                       sMsg = sMsg & col.ColumnName & ": " & _

                              row(col.Ordinal).ToString & vbCrLf

                   Next

                   MsgBox(sMsg)

                   sMsg = ""

               Next

           Next

       Catch ex As Exception

           Dts.Events.FireError(-1, "N/A", ex.ToString(), "", 0)

       End Try      

       Dts.TaskResult = Dts.Results.Success

   End Sub

February 6, 2008 7:07 AM
 

andyleonard said:

Hi Kenan,

  Very elegant coding - thanks!

:{> Andy

February 6, 2008 8:04 AM
 

Dave said:

Hi Andy,

Very good stuff here!  I had the issue of wanting to dynamically build a text file from a list of SQL tables...so i have a ForEach loop that passes in the table name to another SQL Task that creates a full result set into a variable.  I then use part of your code with a streamwriter to build out the text file.  

Again, great snippit to overcome a big issue in my mind with SSIS...that being the lack of ability to change metadata being pumped to a txt file at runtime.

Dave

February 8, 2008 7:59 AM
 

Ashish Sinha said:

Your code was a great help to dynamically display the rows, which didn't adhere to the condition.

February 15, 2008 11:26 AM
 

Slava said:

Hi Andy,

When I run your code I get :

" Object is not an ADODB.RecordSet or an ADODB.Record.

Parameter name: adodb "

  at System.Data.OleDb.OleDbDataAdapter.FillFromADODB(Object data, Object adodb, String srcTable, Boolean multipleResults)

  at System.Data.OleDb.OleDbDataAdapter.Fill(DataTable dataTable, Object ADODBRecordSet)

  at ScriptTask_d195cb0e8fc747399ad752205c10a68b.ScriptMain.Main()

What could be the problem?

April 25, 2008 10:03 AM
 

andyleonard said:

Hi Slava,

  I'm not sure. I haven't seen that one before. Could you zip up your SSIS package and send it to me? andy dot leonard at gmail dot com. (Replace "dot" with "." and "at" with "@").

Andy

April 25, 2008 10:46 AM
 

slava said:

Andy, I used your download:), however is it possible the problem is that I need sql2005 Enterprise, I have 2005 Standart Server?

April 25, 2008 12:08 PM
 

andyleonard said:

Hi Slava,

  That could be it. I rarely use Standard these days - I like Developer!

:{> Andy

April 25, 2008 12:13 PM
 

deloveh said:

Kenan,

Based on a past experience your code would work if an Execute Sql task used an ADO.NET connection manager, but when you use OLEDB the type of object in the variable is not a DataSet.  It is instead an ADODB Recordset which is why we need the OleDbDataAdapter to do the fill on it.

May 15, 2008 1:38 PM
 

Scott Marchione said:

Hello Andy,

I'm sort of a rookie with this stuff, but I found your code helpful to the point that I know I'm getting the data I want, I'm pulling email addresses from my SQL task, and I want to send a email to those addresses, but it seems that no matter what I try, I can not use the results from the sql query to populate the Send to or BCC fields, I always end up getting an error that reads conversion from data type Object to String is not permitted, and if I attempt to not convert the results from an object to a string I get a different error, AND if I change my variable from object to string, I don't get any results! Have you ever tried something like this before?

July 29, 2008 11:48 AM
 

andyleonard said:

Hi Scott,

  I have done similar things before. I would not use this SSIS pattern to accomplish it.

  The approach I would take with SSIS is as follows:

1. Collect the email dataset into an SSIS Object variable as described above.

2. Add a ForEach Loop Container configured to use the ADO Enumerator to pump values from the dataset into variables. This will loop through the rows in the dataset and set variables (maybe EmailSendTo and EmailBCC variables, for example) for each iteration of the loop - and it will iterate once for each row in the dataset.

3. You could then place a SendMail Task inside the ForEach Loop Container.

  I wrote an article about ForEach Loops for SQL Server Central recently. The editor tells me it should be up in early August.

Hope this helps,

Andy

July 29, 2008 12:21 PM
 

Rafael Haroutunian said:

Aha, this is exactly what I was looking for! OleDbDataAdapter, that was the answer...  Darn, I spent so much time trying to unsuccessfully coerce that Object thing into one of the Collection types because I saw a code snippet using CType(Object, Hashtable).

Superb!  Thank you so much, Andy!!!

August 10, 2008 6:07 AM
 

andyleonard said:

Hi Rafael,

  I had the same experience - Jamie Thomson deserves the credit for blogging about that first (http://blogs.conchango.com/jamiethomson/archive/2006/01/04/2540.aspx). Thanks Jamie!

:{> Andy

August 10, 2008 10:05 AM
 

Mark FInk said:

I received the same error that Slava did what was the resolution to it.

November 7, 2008 9:25 AM

Leave a Comment

(required) 
(optional)
(required) 
Submit

About andyleonard

Andy Leonard is a SQL Server MVP, database developer, engineer, husband, and father. Andy lives in Farmville Virginia with his lovely bride Christy Lynn and their three children: Stevie Ray, Emma Grace, and Riley Cooper. Andy's two older children - Manda and Penny - are grown and married. When he's not working (and Andy works a lot), he enjoys spending time with his family, working on their old farmhouse, fishing, and reading older sci-fi series.
Powered by Community Server (Commercial Edition), by Telligent Systems
  Privacy Statement