我有一个关于如何解析列的问题,希望 SSIS/数据库专家可以提供帮助:)
这是问题所在。我在 SQL Server 中有一个以字符串/varchar 格式保存的列,需要从中解析出数据。我有商业智能开发工作室(BIDS)和 SSIS 支持。我熟悉使用该工具使用派生列并像使用 BIDS 那样拆分数据。
问题是数据,这里有一个例子。
Easy to parse with derived columns
24 Year
2 Month
3 Month
2 Month
8 Year
7 Year
No clue how to approach to parse
1x Month
3x per Month
1-2 x per month
6 days per month
Run Code Online (Sandbox Code Playgroud)
所以我想要做的是,如果数据是标准的,我想将列分成两列,Number(int) 和 DayType(string),如果它属于第二类(不稳定的数据),把它放在一个第三栏,其他。关于如何解决这个问题的任何建议?
补充说明:我想将非标准数据放入另一个第三列,以便我可以查看数据,并在该特定列上运行命令,以编辑掉一些绒毛,然后使数据在应用程序中可用。
例如,每月 3x,取出“x per”,然后将其解析为 Number 和 Daytype 列。
同样在有 1-2 的情况下,我想取较高的数字,而放弃较低的数字,例如每周 4-6 天,保留 6,去掉“一周”并保留天数
对于这样的事情,最佳解决方案当然是控制您的输入。也就是说,现实是您必须解析提供的输入。
对于像您的解析这样复杂的事情,我会跳过Derived Column transformation
并直接转到Script transformation
. 我选择我的源列,Input
并创建三个输出列:数字、垃圾和间隔。number 和 Interval 将保存解析的值,而垃圾只会在脚本无法从输入中产生正面或反面时填充。
我使用了两个成员变量, numbersRegex 和 periodDomain。periodDomain只是一个具有可接受值的列表。对于字符串比较,我强制所有内容都小写并希望使用英语。numbersRegex是一个正则表达式,用于识别字符串中的数字。
对于输入的每一行,脚本将根据空格拆分输入值。对于这些令牌中的每一个,我测试令牌中是否有数字。如果是,我们将调用该GetBiggestNumber
方法。否则,我们将调用ValidatePeriodDomain
一旦所有标记都被处理,那么确保两个值都已设置是很重要的。
GetBiggestNumber
尝试查看数字的所有分组并找到最大的集合。
ValidatePeriodDomain
尝试将当前值与已知的可接受值列表进行比较。
using System.Text.RegularExpressions;
using System.Collections.Generic;
/// <summary>
/// The amazing script transformation
/// </summary>
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
Regex numbersRegex;
List<string> periodDomain;
/// <summary>
/// Initialize member variables
/// </summary>
public override void PreExecute()
{
base.PreExecute();
// match consecutive digits
this.numbersRegex = new Regex(@"\d+", RegexOptions.Compiled );
this.periodDomain = new List<string>(){ "year", "month" };
}
/// <summary>
/// Parse the incoming data
/// </summary>
/// <param name="Row">The row that is currently passing through the component</param>
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
string[] parts = Row.Input.Split();
string period = string.Empty;
int? foo = null;
foreach (string token in parts)
{
// try to do something with it
// If the token has a digit in it, then we'll extract the largest value
// if no digits, then the first token matching our domain is preserved
if (this.numbersRegex.IsMatch(token))
{
foo = GetBiggestNumber(token);
}
else
{
if (ValidatePeriodDomain(token))
{
period = token;
}
}
}
// at this point, we've processed the input data
// If the local variables are in their initial states, then we didn't find
// anything of note and need to populate the Row.Junk column
// Why local variables, because can't read from Row.column
if (period == string.Empty || (foo == null))
{
Row.trash = Row.Input;
}
else
{
Row.number = foo.Value;
Row.Interval = period;
}
}
private bool ValidatePeriodDomain(string token)
{
return (this.periodDomain.Contains(token.ToLower()));
}
private int? GetBiggestNumber(string token)
{
int? bigOne = null;
int? current = null;
// Get all the groups of numbers and compare them
foreach (Match item in this.numbersRegex.Matches(token))
{
current = int.Parse(item.Value);
if (!bigOne.HasValue)
{
bigOne = current;
}
if (current.Value > bigOne.Value)
{
bigOne = current;
}
}
return bigOne;
}
}
Run Code Online (Sandbox Code Playgroud)
使用上面的脚本,您可以看到它如何对输入数据进行切片和切块。我在生成以下屏幕截图的代码和发布的内容之间做了一个小改动。我观察到输入值 9000 被分配给 Row.number 但由于该行从未分配过间隔,我将实际的行填充推迟到脚本的末尾(它在