SSIS - 转换为DataTable的对象变量变为空

时间:2014-12-03 05:03:01

标签: c# ssis etl

我正在尝试创建一个对象变量,该变量将保存来自 执行SQL任务 的集合。此集合将在整个ETL包中用于多个 脚本任务

问题是,在第一个 脚本任务 的第一个Fill之后,对象变量变为空。这是关于我如何将变量用于DataTable的代码:

try
            {
                DataTable dt = new DataTable();

                OleDbDataAdapter da = new OleDbDataAdapter();

                da.Fill(dt, Dts.Variables["reportMetrics"].Value);

                Dts.TaskResult = (int)ScriptResults.Success;
            }
            catch (Exception Ex)
            {
                MessageBox.Show(Ex.Message);
                Dts.TaskResult = (int)ScriptResults.Failure;
            }

在整个ETL包中, 脚本任务 组件将拥有这段代码。由于变量在第一个Fill之后变空,我无法重用对象变量。

我猜测Fill方法与此有关。

谢谢!

3 个答案:

答案 0 :(得分:1)

您的Dts.Variables["reportMetrics"].Value对象看起来像DataReader个对象。此对象允许对数据进行只进,只读访问。您无法使用DataReader填充DataTable两次。要完成您的任务,您需要创建另一个执行此处描述的脚本任务:它将Reader读取到DataTable对象,并将此DataTable对象存储在另一个类型为Object的Dts.Variable中。

Dts.Variables["reportMetricsTable"].Value = dt

之后,如果修改数据,所有子结果脚本任务都应创建此表的副本,如果不修改数据,则直接使用它。

DataTable dtCopy = (Dts.Variables["reportMetricsTable"].Value as DataTable).Copy()

答案 1 :(得分:0)

我有类似的情况。虽然我认为您可以使用SELECT COUNT(*)查询执行SQL任务并将结果分配给SSIS变量,但我所做的是创建一个名为totalCount的int SSIS变量,其原始值为0.我希望总计数为是> 0(否则,我没有任何东西可以迭代)所以我在我的脚本任务中创建了一个if语句。如果值为零,我假设totalCount尚未初始化,因此我使用您使用的相同代码(使用Fill方法)。否则(即,在进一步的迭代中),我跳过该部分并继续使用totalCount变量。这是代码块。希望它有所帮助:

if ((int)Dts.Variables["User::totalCount"].Value == 0)    // if the total count variable has not been initialized...
        {
            System.Data.OleDb.OleDbDataAdapter da = new System.Data.OleDb.OleDbDataAdapter();
            DataTable stagingTablesQryResult = new DataTable();
            da.Fill(stagingTablesQryResult, Dts.Variables["User::stagingTablesQryResultSet"].Value);    // to be used for logging how many files are we iterating. It may be more efficient to do a count(*) outside this script and save the total number of rows for the query but I made this as proof of concept for future developments.

            Dts.Variables["User::totalCount"].Value = stagingTablesQryResult.Rows.Count;
        }

Console.WriteLine("{0}. Looking for data file {0} of {1} using search string '{2}'.", counter, Dts.Variables["User::totalCount"].Value, fileNameSearchString);

答案 2 :(得分:0)

优秀

这帮助我解决了构建myt ETL平台的问题。

本质上,我执行SQL任务以构建任务数据集,其中存在一些内联转换和规则,这些规则和规则将相关任务放在了首位,出于明显的原因,我只希望每次执行一次。

然后我需要从数据集中获取唯一的ProcessID(以在For Each循环中使用)

在FEL中,我想从原始数据集中获取相关记录,然后进行进一步的FEL流程。

对于数据集的第二次执行,我面临着相同的“空数据集”。

我认为我会尝试分享自己的解决方案以帮助他人

您需要添加命名空间

  

使用System.Data.OleDb;

进入脚本

Screen shot of solution

获取数据集

执行SQL-获取数据并传递给变量对象

拉动Ds Declare the Variable Objects

 public void Main()
        {
            DataTable dt            = new DataTable();
            OleDbDataAdapter da     = new OleDbDataAdapter();
            //Read the original table
            da.Fill(dt, Dts.Variables["Tbl"].Value);
            //Push to a replica
            Dts.Variables["TblClone"].Value = dt;

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

构建过程列表 通过过滤数据集中的Rank字段来获取ProcessID(和名称)的列表

Declare the Variable Objects

public void Main()
        {  //Take a copy of the Cloned Dataset
            DataTable dtRead = (Dts.Variables["TblClone"].Value as DataTable).Copy();

            //Lock the output object variable
            Dts.VariableDispenser.LockForWrite("User::ProcTbl");

            //Create a data table to place the results into which we can write to the output object once finished
            DataTable dtWrite = new DataTable();

            //Create elements to the Datatable programtically
            //dtWrite.Clear();

            dtWrite.Columns.Add("ID", typeof(Int64));
            dtWrite.Columns.Add("Nm");

            //Start reading input rows
            foreach (DataRow dr in dtRead.Rows)
            {
                //If 1st col from Read object = ID var 
                if (Int64.Parse(dr[9].ToString()) == 1) //P_Rnk = 1 
                {
                    DataRow newDR = dtWrite.NewRow();

                        newDR[0] = Int64.Parse(dr[0].ToString());
                        newDR[1] = dr[4].ToString();

                    //Write the row
                    dtWrite.Rows.Add(newDR);
                }
            }

            //Write the dataset back to the object variable
            Dts.Variables["User::ProcTbl"].Value = dtWrite;
            Dts.Variables.Unlock();

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

从ProcList构建任务列表

在For Each循环中循环处理ProcessID

Build TL Collection

..并映射Vars

Build TL Var Mappings

构建TL脚本 这将为您动态生成输出(请注意,尽管还没有对其进行广泛的测试,但是这对我来说仍然有效,因此,如果它不起作用...。 您会看到我已经注释掉了一些Debug内容

public void Main()
    {   
        //Clone the copied table
        DataTable dtRead = (Dts.Variables["TblClone"].Value as DataTable).Copy();

        //Read the var to filter the records by
        var ID = Int64.Parse(Dts.Variables["User::ProcID"].Value.ToString());

        //Lock the output object variable
        Dts.VariableDispenser.LockForWrite("User::SubTbl");

        //Debug Test the ProcID being passed
        //MessageBox.Show(@"Start ProcID =  " + ID.ToString());
        //MessageBox.Show(@"TblCols =  " + dtRead.Columns.Count);

        //Create a data table to place the results into which we can write to the output object once finished
        DataTable dtWrite   = new DataTable();

        //Create elements to the Datatable programtically
        //dtWrite.Clear();
        foreach (DataColumn dc in dtRead.Columns)
        {
            dtWrite.Columns.Add(dc.ColumnName, dc.DataType);
        }

        MessageBox.Show(@"TblRows =  " + dtRead.Rows.Count); 
        //Start reading input rows
        foreach (DataRow dr in dtRead.Rows)
        {
            //If 1st col from Read object = ID var 
            if (ID == Int64.Parse(dr[0].ToString()))
            {
                DataRow newDR = dtWrite.NewRow();

                //Dynamically create data for each column
                foreach (DataColumn dc in dtRead.Columns)
                {
                    newDR[dc.ColumnName] = dr[dc.ColumnName];
                }
                //Write the row
                dtWrite.Rows.Add(newDR);

                //Debug
                //MessageBox.Show(@"ProcID =  " + newDR[0].ToString() + @"TaskID =  " + newDR[1].ToString() + @"Name = " + newDR[4].ToString());
            }

        }
        //Write the dataset back to the object variable
        Dts.Variables["User::SubTbl"].Value = dtWrite;
        Dts.Variables.Unlock();

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

对于每个循环容器

FEL Cont Collection N.B.不要忘记在变量映射中映射项目

现在您可以使用记录并对该数据进行处理 我将Msg Loop脚本包含为一个简单的数据检查...实际上,它会关闭并触发其他进程,但是尽管Id包含了它,但只是为了帮助您进行数据检查

消息循环

消息循环脚本

public void Main()
        {
            // TODO: Add your code here
                MessageBox.Show("ID = " + Dts.Variables["User::ProcID"].Value + ", and val = " + Dts.Variables["User::TaskID"].Value, "Name = Result");

                Dts.TaskResult = (int)ScriptResults.Success;

        }

希望可以帮助某人解决问题(希望在一个工作日左右解决这个问题:/