如何使用SSIS加载分组数据

How to load grouped data with SSIS

我有一个棘手的平面文件数据源。数据分组,像这样:

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

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

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

-- 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

顺便说一句,你输入数据的格式文件是这个(如果你想尝试脚本将输入数据保存为 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

cha 的答案会奏效,但如果您需要在没有 temporary/staging 表的 SSIS 中执行此操作,这里还有另一个答案:

您可以 运行 通过使用数据流级变量的脚本转换来处理您的数据流。随着每一行进入脚本,检查 Country 列的值。

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

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

编辑:我查看了您的错误消息并了解了有关脚本组件(数据流工具,而不是脚本任务,控制流工具)的新知识:

The collection of ReadWriteVariables is only available in the PostExecute method to maximize performance and minimize the risk of locking conflicts. Therefore you cannot directly increment the value of a package variable as you process each row of data. Increment the value of a local variable instead, and set the value of the package variable to the value of the local variable in the PostExecute method after all data has been processed. You can also use the VariableDispenser property to work around this limitation, as described later in this topic. However, writing directly to a package variable as each row is processed will negatively impact performance and increase the risk of locking conflicts.

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