为什么SSIS令牌函数无法计数相邻的列分隔符?

为什么SSIS令牌函数无法计数相邻的列分隔符?,ssis,tokenize,sql-server-2012,Ssis,Tokenize,Sql Server 2012,在名为TOKEN()的表达式编辑器中,SQL Server Integration Services 2012的新字符串函数遇到问题 这有助于解析分隔记录。如果记录来自平面文件,则可以使用平面文件源执行此操作。在本例中,我处理的是作为字符串存储在数据库VARCHAR字段中的旧分隔导入记录。现在,它们需要被提取、处理,并作为分隔字符串重新导出。例如: 1^苹果^0001^01/01/2010^食蚁兽^A1 2^香蕉^0002^03/15/2010^熊^B2 3^蔓越莓^0003^4/15/2010

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

这有助于解析分隔记录。如果记录来自平面文件,则可以使用平面文件源执行此操作。在本例中,我处理的是作为字符串存储在数据库VARCHAR字段中的旧分隔导入记录。现在,它们需要被提取、处理,并作为分隔字符串重新导出。例如:

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

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

TOKEN(OldImportRecord,"^",5)
这将返回Anteater、Bear、Crow等。事实上,我们可以为此记录中的每个字段创建派生列(请注意,索引是基于一个字段的),根据需要更改它们,然后构建另一个分隔的记录以供导出

问题出在这里。如果我们的某些数据包含一些空字符串(或呈现为空字符串的null),该怎么办

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

TOKEN()无法计数相邻的列分隔符,这会导致列计数失败。现在它只看到五列而不是六列。我们的令牌(OldImportRecord,“^”,5)返回“D4”,而不是预期的“Duck”。当我们提取第四列时,我们试图将“Duck”放在日期列中,各种各样的乐趣随之而来

这里有一个局部解决方法:

TOKEN(REPLACE(OldImportRecord,"^^","^ ^"),"^",5)
请注意,这会丢失每秒一次的分隔符对,因此对于类似“5^^^^^ Emu^E5”的字符串,它将失败,该字符串在REPLACE()之后看起来像“5^^^^ Emu^E5”。列计数仍然错误

这是我的全部解决方法。这包括两个嵌套的REPLACE语句()、一个用于删除多余空格的RTRIM()和一个DT_STR cast,因为我希望将结果保留在VARCHAR中:

(DT_STR,255,1252)RTRIM(TOKEN(REPLACE(REPLACE(OldImportRecord,"^^","^ ^"),"^^","^ ^"),"^",5))
我发布这篇文章是为了提供信息,因为其他人可能也会遇到这个问题

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

问题原因: SSIS中的
TOKEN
方法使用C++中的
strtok
函数实现。我在读这本书的时候收集了这些信息。这是在第113页的注释中提到的(我喜欢这本书!有很多很好的信息。)

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

-此链接中的代码示例显示函数忽略连续分隔符字符

以下SO问题的答案指出,
strtok
函数被设计为忽略连续分隔符

我认为,
TOKEN
TOKENCOUNT
函数按照设计工作,但这是否是SSIS的行为方式可能是Microsoft SSIS团队的一个问题

以上部分的原始帖子是一个更新: 我在SSIS 2012中根据您的数据输入创建了一个简单的包。正如您在问题中所述,
令牌
函数的行为不符合预期。我同意你的观点,这个功能似乎不起作用。这篇文章不是对你原来问题的回答

下面是另一种以相对简单的方式编写表达式的方法。只有当输入记录中的最后一段始终有一个值(比如A1、B2、C3等)时,这才有效

表达式可以重写为

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

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

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

如果没有其他人的消息,我建议您登录此问题

创建表格并填充脚本

数据流任务内部的派生列转换

源表和目标表中的数据


令牌不仅跳过相邻的分隔符,还跳过前导和尾随分隔符。因此,使用您的示例,如果您有一个字段“good”字段,如下所示:

^^^0004^6/15/2010^Duck^
1^苹果^0001^01/01/2010^食蚁兽^A1

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

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

我用了一种不同的替代品。我没有在相邻的分隔符之间放置空格(这对前导或训练没有帮助),而是使用replace将分隔符用我在文本中绝对找不到的字符包围起来。下面的表达对我很有用。虽然罗嗦,但事实就是这样

(DT_STR,255,1252)REPLACE(TOKEN(REPLACE(OldImportRecord,"^","~^~"),"^",1),"~","")
当然,你会用你想要的任何标记替换数字1,并根据你的需要调整演员阵容。希望有帮助

另一个警告是