如何使用SSIS加载分组数据

时间:2016-04-13 02:42:23

标签: sql sql-server tsql ssis ssis-2012

我有一个棘手的平面文件数据源。数据分组,如下所示:

Country    City
U.S.       New York
           Washington
           Baltimore
Canada     Toronto
           Vancouver

但我希望它在加载到数据库时成为这种格式:

Country    City
U.S.       New York
U.S.       Washington
U.S.       Baltimore
Canada     Toronto
Canada     Vancouver

之前有人遇到过这样的问题吗?有想法处理它吗? 我现在唯一的想法是使用光标,但它太慢了 谢谢!

2 个答案:

答案 0 :(得分:3)

是的,有可能。首先,您需要将数据加载到具有IDENTITY列的表中:

-- drop table #t
CREATE TABLE #t (id INTEGER IDENTITY PRIMARY KEY,
Country VARCHAR(20),
City VARCHAR(20))

INSERT INTO #t(Country, City)
SELECT a.Country, a.City
 FROM OPENROWSET( BULK 'c:\import.txt', 
     FORMATFILE = 'c:\format.fmt',
     FIRSTROW = 2) AS a;

select * from #t

结果将是:

id          Country              City
----------- -------------------- --------------------
1           U.S.                 New York
2                                Washington
3                                Baltimore
4           Canada               Toronto
5                                Vancouver

现在有了一些递归的CTE魔法,你可以填充缺失的细节:

;WITH a as(
    SELECT Country
          ,City
          ,ID
    FROM #t WHERE ID = 1
    UNION ALL
    SELECT COALESCE(NULLIF(LTrim(#t.Country), ''),a.Country)
          ,#t.City
          ,#t.ID
    FROM a INNER JOIN #t ON a.ID+1 = #t.ID
    )
SELECT * FROM a
 OPTION (MAXRECURSION 0)

结果:

Country              City                 ID
-------------------- -------------------- -----------
U.S.                 New York             1
U.S.                 Washington           2
U.S.                 Baltimore            3
Canada               Toronto              4
Canada               Vancouver            5

<强>更新

正如Tab Alleman在下面建议的那样,在没有递归查询的情况下可以实现相同的结果:

SELECT ID
     , COALESCE(NULLIF(LTrim(a.Country), ''), (SELECT TOP 1 Country FROM #t t WHERE t.ID < a.ID AND LTrim(t.Country) <> '' ORDER BY t.ID DESC))
     , City
FROM #t a

BTW,输入数据的格式文件是这样的(如果你想尝试脚本将输入数据保存为c:\ import.txt,下面的格式文件保存为c:\ format.fmt):

9.0
  2
  1       SQLCHAR       0       11      ""       1     Country      SQL_Latin1_General_CP1_CI_AS
  2       SQLCHAR       0       100     "\r\n"   2     City         SQL_Latin1_General_CP1_CI_AS

答案 1 :(得分:3)

cha的答案是可行的,但如果你需要在没有临时/临时表的情况下在SSIS中这样做,这是另一个:

您可以通过使用DataFlow级变量的脚本转换来运行数据流。当每行进入脚本时,检查Country列的值。

如果它具有非空值,则使用该值填充变量,并在数据流中传递它。

如果Country有空白值,则用变量值覆盖它,这将是您获得的最后一个非空白国家/地区值。

编辑:我查找了您的错误消息并学习了一些关于脚本组件的新内容(数据流工具,而不是脚本任务,控制流工具):

  

ReadWriteVariables集合仅在   PostExecute方法可以最大限度地提高性能并将风险降至最低   锁定冲突。因此,您无法直接递增该值   处理每行数据时的包变量。递增   取而代之的是局部变量的值,并设置包的值   变量为PostExecute方法中局部变量的值   处理完所有数据后。你也可以使用   VariableDispenser属性可以解决此限制问题   本主题后面会介绍。但是,直接写入包   处理每一行的变量将对性能产生负面影响   并增加锁定冲突的风险。

来自this MSDN article,如果你想走那条路,它还有更多关于变量分配器解决方法的信息,但是当我说你可以设置包的价值时,显然我误解了你脚本中的变量。您必须使用脚本本地的变量,然后在Post-Execute事件处理程序中更改它。我无法从文章中看出这是否意味着您将无法读取脚本中的变量,如果是这种情况,那么变量分配器将是唯一的选择。或者我想你可以创建另一个脚本具有只读访问权限的变量,并将其值设置为表达式,以便它始终具有读写变量的值。这可能有用。