SSIS - 将存储过程中的结果集插入另一个DB服务器上的表中

时间:2016-07-18 13:47:06

标签: sql-server sql-server-2008 ssis

我想我偶然发现了一个边缘情况,ADO.NET数据源和OLDEB数据源都无法完全满足我的需求:

  1. 控制流程 - >执行SQL任务 - > ADO.NET数据源允许使用用户定义的表(表值参数)作为参数处理存储过程。

    • 但是,我没有办法/知道如何将数据插入到另一台服务器上的表中。
  2. 数据流 - > OLEDB数据源允许将一台服务器上的数据源结果直接传送到另一台服务器上的数据源。

    • 但是,所有数据流源(OLEDB数据源甚至ADO.NET数据源)似乎都不允许传入参数映射,因此无法传入复杂的用户定义类型。
    • 我也不能使用变量表达式,因为似乎没有办法将Object作为表达式值插入。
  3. Server1具有以下存储过程:

    CREATE TYPE [dbo].[OrderKeyList] AS TABLE(
      [OrderKey] [varchar](50) NULL
    )
    GO
    
    CREATE PROCEDURE [dbo].[GetAllOrdersInOrderList] (
      @OrderList dbo.OrderKeyList not null
    )
    AS
    BEGIN
      SELECT o.*
      FROM dbo.Orders o
      WHERE o.OrderKey in (SELECT o.OrderKey FROM @OrderList);
    END
    GO
    

    SSIS包如下:

    .-[Sequence Container]-----------------------------------------------.
    |                                                                    |
    | .-[Data Flow Task - Populate User Variable User::OrderList]-.      |
    | |                                                           |      |
    | '-----------------------------------------------------------'      |
    |                               |                                    |
    |                              \|/                                   |
    | .-[Execute SQL Task - call dbo.GetOrdersByOrderList]--------.      |
    | |                                                           |      |
    | '-----------------------------------------------------------'      |
    |                               |                                    |
    |                              \|/                                   |
    | .-[ ?????????????????????????????????????? ]----------------.      |
    | |                                                           |      |
    | '-----------------------------------------------------------'      |
    '--------------------------------------------------------------------'
    

    我能想到的唯一解决方案是在Source DB上添加第二个存储过程,该过程采用@OrderList varchar(max)而不是dbo.OrderKeyList表,并调用dbo.Split(',', @OrderList)并将其传递给真正的存储过程:

    CREATE PROCEDURE [dbo].[GetAllOrdersInOrderListWrapper] (
       @OrderList varchar(max)
    )
    AS
    BEGIN
      DECLARE @tmpOrderList dbo.OrderKeyList
      SELECT
        DISTINCT CAST(o.Data as varchar(50))
      INTO @tmpOrderList
      FROM dbo.Split(',', @OrderList) o;
    
      EXEC dbo.GetAllOrdersInOrderList @tmpOrderList
    END;
    GO
    

    但我真的不喜欢这种方法,因为:

    • 它将如何扩展到数千行?
    • 它需要存储过程源添加另一个存储过程,仅用于SSIS。

3 个答案:

答案 0 :(得分:1)

如果您使用控制流/执行SQL任务方法,则可以通过链接服务器将数据移动到另一台服务器上的表中。

如果你使用你提到的包装器存储过程方法,它将无法在ETL过程中进行可怕的扩展,即使在数千行中也是如此。如果是我,这是我会选择的选项。

答案 1 :(得分:0)

您的问题可能与尝试将对象作为用户定义的类型传递有关,我从未尝试过传递'表格'作为参数。

我会这样做: 而不是将数据推送到对象变量而是将其推送到源上的表(可能是temp)。可以使用表的名称作为参数运行存储过程,然后执行动态sql

CREATE PROCEDURE [dbo].[GetAllOrdersInOrderList] (
  @OrderList varchar(500) not null
)
AS
BEGIN    
Set @Sql = '  SELECT o.*
      FROM dbo.Orders o
      WHERE o.OrderKey in (SELECT o.OrderKey FROM ' + @OrderList +')';
    Exec @Sql
END

也许其他人有办法完全按照你的描述去做,我自己会非常有兴趣看到这样的解决方案。

答案 2 :(得分:0)

在研究了一些之后,我在Stack Overflow上发现了一个suggestion,它通过@Jon Seigel - Using table-valued parameters in SSIS向我指出了这篇文章。

它的网络是将User :: OrderList作为参数传递给脚本任务,并编写一个C#程序直接使用ADO.NET,从而绕过SSIS GUI的限制。

你为什么要这样做呢?为什么不用C#编写所有内容?好吧,通过这样做,我个人仍然看到SSIS的好处,因为ETL过程的整体编排仍然是图形化的,因此应该非常容易阅读,即使写入更痛苦(哈!写SSIS并不痛苦?)。

我现在正在尝试这种方法,因为我可以看到多种好处,包括将来使用C#SqlBulkCopy类批量插入数据以加快加载的选项。