【问题标题】:How to load grouped data with SSIS如何使用 SSIS 加载分组数据
【发布时间】:2016-04-13 02:42:23
【问题描述】:

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

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

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

【问题讨论】:

  • 如果您没有像 id 这样的其他列来帮助确定这些城市属于哪个国家/地区,那么 DBMS 就不可能得到您需要的东西。因为他们没有按顺序进行。

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


【解决方案1】:

cha 的答案会起作用,但如果您需要在没有临时/临时表的 SSIS 中执行此操作,这里还有一个:

您可以通过使用数据流级别变量的脚本转换来运行数据流。脚本中的每一行都会检查 Country 列的值。

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

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

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

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

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

【讨论】:

  • 谢谢哥们。我尝试使用光标,我认为这与您的想法相似。但是我实际上有 20k+ 行,所以逐行运行太慢了......
  • 好的,我尝试了你的方法,但我总是收到错误消息“锁定以进行读写访问的变量集合在 PostExecute 之外不可用”。但是我将变量赋值放在 PostExecute.......sign,不知道出了什么问题。谢谢
  • 也许您也不允许读取脚本中的 ReadWrite 变量。我用我学到的一些新信息编辑了我的答案。
  • 谢谢Tab Alleman,我采用了你的想法,效果很好!在将数据流中的数据加载到 SQL Server 之前对其进行处理要快得多。
  • 很高兴听到它,也很高兴知道我自己的未来参考!
【解决方案2】:

是的,这是可能的。首先,您需要将数据加载到具有 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

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

【讨论】:

  • 谢谢你。我试过你的想法,但在我的情况下它不起作用。因为我的表中实际上有 20k+ 行。所以我收到一个错误,说“CTE 重复超过 100 次”
  • 您可以使用自联接代替递归 CTE 来避免递归限制。
  • 感谢@TabAlleman。我已经更新了我的答案以包含一个没有递归的版本
猜你喜欢
  • 2014-08-31
  • 1970-01-01
  • 2021-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-21
相关资源
最近更新 更多