这是我完整的任务说明:
我必须使用u-sql从多个文件中提取数据并将其输出到csv文件中。每个输入文件都包含多个基于某些字符串行的报告(“START OF ...”和“END OF ...”作为报告分隔符)。以下是单个源(输入)文件的示例(数据格式):
START OF DAILY ACCOUNT
some data 1
some data 2
some data 3
some data n
END OF DAILY ACCOUNT
START OF LEDGER BALANCE
some data 1
some data 2
some data 3
some data 4
some data 5
some data n
END OF LEDGER BALANCE
START OF DAILY SUMMARY REPORT
some data 1
some data 2
some data 3
some data n
END OF DAILY SUMMARY REPORT
所以现在我的问题是如何在所有文件的“START OF ...”和“END OF ...”行之间获取记录?
我最终想要这样的事情:
@dailyAccountResult = [select all rows between "START OF DAILY ACCOUNT" and "END OF DAILY ACCOUNT" rows]
@ledgerBalanceResult = [select all rows between "START OF LEDGER BALANCE" and "END OF LEDGER BALANCE" rows]
@dailySummaryReportResult = [select all rows between "START OF DAILY SUMMARY REPORT" and "END OF DAILY SUMMARY REPORT" rows]
我需要为此编写自定义提取器吗?如果是,那么请建议我如何。
答案 0 :(得分:4)
我认为使用普通的U-SQL而不使用自定义提取器是可行的。我已根据您的sample data:
创建了一个简单示例// Get raw input
@input =
EXTRACT rawData string
FROM "/input/input36.txt"
USING Extractors.Tsv();
// Add a row number and break out the section;
// Get all [START OF ...] and [END OF ...] blocks and pair them.
// !!WARNING code assumes there are no duplicate sections, ie can not be more than one DAILY ACCOUNT section for example
@working =
SELECT ROW_NUMBER() OVER() AS rn,
System.Text.RegularExpressions.Regex.Match(rawData, "(START OF|END OF) (?<sectionName>.+)").Groups["sectionName"].ToString() AS sectionName,
*
FROM @input;
// Work out the section boundaries
@sections =
SELECT sectionName,
MIN(rn) AS startRn,
MAX(rn) AS endRn,
COUNT( * ) AS records
FROM @working
WHERE sectionName != ""
GROUP BY sectionName;
// Create the output
@output =
SELECT s.sectionName,
i.rn == s.startRn ? 1 : 0 AS isStartSection,
i.rn == s.endRn ? 1 : 0 AS isEndSection,
i.rawData
FROM @sections AS s
CROSS JOIN
@working AS i
WHERE i.rn BETWEEN s.startRn AND s.endRn;
// Output the file
OUTPUT @output
TO "/output/output.txt"
USING Outputters.Tsv(quoting : false);
现在每个部分都标有部分名称,您可以轻松地将数据分配给不同的变量,并可选择包括页眉/页脚行,例如
@dailyAccount =
SELECT rawData
FROM @output
WHERE sectionName == "DAILY ACCOUNT"
AND isStartSection == 0
AND isEndSection == 0;
尝试一下,让我知道你是怎么过的。
答案 1 :(得分:1)
要问的相关问题:
[SqlUserDefinedExtractor(AtomicFileProcessing = true)]
确保整个输入按1个实例顺序处理,这已足够,并且根据输入大小可能适用于此情况。行集有订单吗?
不!行集是无序概念 - 将它们视为非重复数据删除哈希集。
var input = new HashSet<string>(File.ReadLines(@In_Data));
File.WriteAllLines(@Out_NewData, input)
不希望保留原始的行顺序(即使它适用于某些输入,该实现细节,不保证语义行为)。
同样的行集 - 输入顺序当数据被转换为行集时,行丢失(无保证)。因此,尝试使用ROW_NUMBER()是徒劳的 - 在调用ROW_NUMBER()时无法保留命令。使用ROW_NUMBER()的唯一方法是,如果行集有一些Key,其排序顺序可以重新创建行的原始顺序。
因为行集没有顺序,所以无论如何都需要自定义提取器 - 它是脚本中唯一能够观察文件中行的顺序的部分,给定
您可以选择在自定义提取器中包含所有逻辑,或者只是添加一个编号列来模仿ROW_NUMBER并使用本机U-Sql作为逻辑。