【问题标题】:Why does SSIS TOKEN function fail to count adjacent column delimiters?为什么 SSIS TOKEN 函数无法计算相邻的列分隔符?
【发布时间】:2012-10-16 15:07:05
【问题描述】:

我在表达式编辑器中遇到了 SQL Server Integration Services 2012 的新字符串函数 TOKEN() 的问题。

这应该可以帮助您解析分隔记录。如果记录来自平面文件,您可以使用平面文件源执行此操作。在这种情况下,我正在处理以字符串形式存储在数据库 VARCHAR 字段中的旧分隔导入记录。现在需要将它们提取、处理并重新导出为分隔字符串。例如:

1^Apple^0001^01/01/2010^食蚁兽^A1
2^香蕉^0002^03/15/2010^熊^B2
3^蔓越莓^0003^4/15/2010^乌鸦^C3

如果这些字符串位于名为 OldImportRecord 的列中,分隔符是插入符号(如图所示),并且我们希望将第五个字段放入派生列中,我们将使用如下表达式:

TOKEN(OldImportRecord,"^",5)

这会返回Anteater、Bear、Crow等。其实我们可以为这条记录中的每个字段创建Derived Columns(注意索引是从一开始的),根据需要更改它们,然后再构建另一个分隔出口记录。

这就是问题所在。如果我们的某些数据包含一些空字符串(或呈现为空字符串的 Null)怎么办?

4^^0004^6/15/2010^鸭^D4

TOKEN() 无法计算相邻的列分隔符,这会导致列计数失效。现在它只看到五列而不是六列。我们的 TOKEN(OldImportRecord,"^",5) 返回“D4”而不是预期的“Duck”。当我们提取第四列时,我们最终尝试将“Duck”放入日期列中,随之而来的是各种有趣的事情。

以下是部分解决方法:

TOKEN(REPLACE(OldImportRecord,"^^","^ ^"),"^",5)

请注意,这会错过每隔一个分隔符对,因此对于像“5^^^^Emu^E5”这样的字符串会失败,它在 REPLACE() 之后看起来像“5^ ^^ ^Emu^E5”。列数仍然错误。

所以这是我的完整解决方法。这包括两个嵌套的 REPLACE statements(),一个用于删除多余空格的 RTRIM(),以及一个 DT_STR 强制转换,因为我想将结果保留在 VARCHAR 中:

(DT_STR,255,1252)RTRIM(TOKEN(REPLACE(REPLACE(OldImportRecord,"^^","^ ^"),"^^","^ ^"),"^",5))

我发布此信息以供参考,因为其他人也可能遇到此问题。

有没有人有更好的解决方法,甚至是真正的解决方案?

【问题讨论】:

    标签: ssis tokenize sql-server-2012


    【解决方案1】:

    问题原因

    SSIS 中的

    TOKEN 方法使用C++strtok 函数的实现。我在阅读Microsoft® SQL Server® 2012 Integration Services 这本书时收集了这些信息。第 113 页上的注释提到了它(我喜欢这本书!很多不错的信息。)。

    我搜索了strtok函数的实现,找到了以下链接。

    INFO: strtok(): C Function -- Documentation Supplement - 此链接中的代码示例显示该函数确实忽略了连续的分隔符。

    以下 SO 问题的答案指出 strtok 函数旨在忽略连续分隔符。

    Need to know when no data appears between two token separators using strtok()

    strtok_s behaviour with consecutive delimiters

    我认为 TOKENTOKENCOUNT 函数按照设计工作,但 SSIS 是否应该这样运行可能是 Microsoft SSIS 团队的问题。

    原始帖子 - 以上部分是更新:

    我根据您的数据输入在 SSIS 2012 中创建了一个简单的包。正如您在问题中所描述的,TOKEN 函数的行为不符合预期。我同意你的观点,该功能似乎不起作用。这篇文章不是对您原来问题的回答。

    下面是另一种以相对简单的方式编写表达式的方法。这仅在您的输入记录中的最后一段总是有一个值时才有效(例如 A1B2C3 等)。

    表达式可以改写为

    该语句将输入记录作为参数,分隔符插入符号(^)作为第二个参数。第三个参数计算被分隔符分割时记录中的段总数。如果您在最后一个段中有数据,则保证有两个段。然后,您可以减去 1 来获取倒数第二段。

    (DT_STR,50,1252)TOKEN(OldImportRecord,"^",TOKENCOUNT(OldImportRecord,"^") - 1)
    

    我创建了一个带有数据流任务的简单包。 OLE DB 源检索数据,派生的转换按照下面的屏幕截图解析和拆分数据。然后将输出插入到目标表中。您可以在最后一个屏幕截图中看到源表和目标表。目标表有两列。第一列存储倒数第二个段数据和基于分隔符的段计数(这也是不正确的)。您可以注意到最后一条记录没有获取正确的结果。如果最后一条记录的值不是8,则上述表达式将失败,因为该表达式将计算为零索引。

    希望这有助于简化您的表达方式。

    如果您没有收到任何其他人的消息,我建议您将此问题记录在 Microsoft Connect website

    创建表格并填充脚本

    CREATE TABLE [dbo].[SourceTable](
        [OldImportRecord] [varchar](50) NOT NULL
    ) ON [PRIMARY]
    GO
    
    CREATE TABLE [dbo].[DestinationTable](
        [NewImportRecord] [varchar](50) NOT NULL,
        [CaretCount] [int] NOT NULL
    ) ON [PRIMARY]
    GO
    
    INSERT INTO dbo.SourceTable (OldImportRecord) VALUES 
        ('1^Apple^0001^01/01/2010^Anteater^A1'),
        ('2^Banana^0002^03/15/2010^Bear^B2'),
        ('3^Cranberry^0003^4/15/2010^Crow^C3'),
        ('4^^0004^6/15/2010^Duck^D4'),
        ('5^^^^Emu^E5'),
        ('6^^^^Geese^F6'),
        ('^^^^Pheasant^G7'),
        ('8^^^^Sparrow^');
    GO
    

    数据流任务中的派生列转换

    源表和目标表中的数据

    【讨论】:

    • 另一个警告是,这也不能处理倒数第二段本身是空字符串的情况。但是,您的解决方法比我的更能说明问题(并使用了 TOKENCOUNT),所以我想将此标记为答案,至少在其他人想出意外之前。
    • 好的,我应您的要求取消了它的标记。如果这是对您的贡献的正确评价方式,我给了它一个赞成票。
    • 这不起作用的一种情况是字符串中的第一个字符是分隔符。我有一个字符串,其中有 2 个分隔符(管道)。如果您考虑以下情况:“|Dog|”如果条目 1 和 3 为 null,则上面采用 TokenCount - 1 的代码将返回 0 并出错。
    • 我将其重新标记为答案,因为它包含了为什么会发生这种情况的基本解释。
    【解决方案2】:

    TOKEN 不仅会跳过相邻的定界符,还会跳过前导和尾随定界符。因此,使用您的示例,如果您有一个看起来像这样的“好”字段:

    1^Apple^0001^01/01/2010^食蚁兽^A1

    后跟一个带有相邻和前导分隔符的分隔符,如下所示:

    ^^^0004^6/15/2010^Duck^
    

    TOKENCOUNT 只会找到两个分隔符,最终您将 0004 分配给 Token1,将 6/15/2010 分配给 Token2,将 Duck 分配给 Token3。

    我使用了另一种替换。我没有在相邻的分隔符之间放置空格,这对引导或训练没有帮助,而是使用替换来用我在文本中绝对找不到的字符包围分隔符。以下表达式对我很有效。它是罗嗦的,但它就是这样。

    (DT_STR,255,1252)REPLACE(TOKEN(REPLACE(OldImportRecord,"^","~^~"),"^",1),"~","")
    

    当然,您可以将数字 1 替换为您想要的任何 Token,并根据您的需要调整演员阵容。希望有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-03
      • 2016-01-08
      • 2013-06-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多