SQL - 如何 Select SQL 中两个字符串行之间的所有行
USQL - How To Select All Rows Between Two String Rows in USQL
这是我的完整任务描述:
我必须使用 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]
我需要为此编写自定义提取器吗?如果是,请告诉我怎么做。
我认为这可以使用普通的 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);
我的结果:
现在每个部分都标有部分名称,您可以轻松地将数据分配给不同的变量,并可选择包含 header/footer 行,例如
@dailyAccount =
SELECT rawData
FROM @output
WHERE sectionName == "DAILY ACCOUNT"
AND isStartSection == 0
AND isEndSection == 0;
试一试,告诉我你的进展情况。
相关问题请教:
- 在分布式处理系统中,是否所有输入数据(可能是 TB)都由 1 Extractor 实例处理?
- 绝对不是!如需确认,请参阅 EXTRACT 文档 (msdn.microsoft.com/en-us/library/azure mt621320.aspx).
- 给定多个提取器实例,数据可以在哪里拆分?换句话说,一般来说,是什么决定了 U-Sql 中数据的原子性单位?具体针对您的情况,您有什么保证整个 START...END 序列将由一个实例处理而不是在中间分开?
- Data Lake Tools documentation 建议 数据原子性的通用单位是 "line"(行结构文件)——这是一个 属性数据上传本身。
- Per USQL Programmatibility guide,
[SqlUserDefinedExtractor(AtomicFileProcessing = true)]
确保整个输入由 1 个实例按顺序处理,这就足够了,并且对于这种情况可能是可行的,具体取决于输入大小。
行集有顺序吗?
不! 行集是无序的概念 - 将它们视为非去重哈希集。
var input = new HashSet<string>(File.ReadLines(@In_Data));
File.WriteAllLines(@Out_NewData, input)
预计不会保留原始行顺序(即使它对某些输入有效,那是实现细节,不能保证语义行为)。
行集也是如此 - 行的输入顺序丢失(无保证)数据被翻译成行集的那一刻。因此,尝试使用 ROW_NUMBER() 是徒劳的——在可以调用 ROW_NUMBER() 之前没有任何顺序可以保留。使用 ROW_NUMBER() 的唯一方法是如果行集有一些键,其排序顺序可以重新创建行的原始顺序。
因为行集没有顺序,无论如何您都需要一个自定义提取器 - 它是脚本中唯一能够观察文件中行顺序的部分,给定
- 它使用 AtomicFileProcessing,或者
- 您找到了一种方法来保证数据拆分不会发生在 START...END 序列之间。 AFAIK 没有办法做到这一点(缺少将整个序列预处理成行预上传)。
您可以选择将所有逻辑包含在自定义提取器中,或者只需添加一个带编号的列来模仿 ROW_NUMBER 并使用本机 U-Sql 作为逻辑。
这是我的完整任务描述:
我必须使用 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]
我需要为此编写自定义提取器吗?如果是,请告诉我怎么做。
我认为这可以使用普通的 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);
我的结果:
现在每个部分都标有部分名称,您可以轻松地将数据分配给不同的变量,并可选择包含 header/footer 行,例如
@dailyAccount =
SELECT rawData
FROM @output
WHERE sectionName == "DAILY ACCOUNT"
AND isStartSection == 0
AND isEndSection == 0;
试一试,告诉我你的进展情况。
相关问题请教:
- 在分布式处理系统中,是否所有输入数据(可能是 TB)都由 1 Extractor 实例处理?
- 绝对不是!如需确认,请参阅 EXTRACT 文档 (msdn.microsoft.com/en-us/library/azure mt621320.aspx).
- 给定多个提取器实例,数据可以在哪里拆分?换句话说,一般来说,是什么决定了 U-Sql 中数据的原子性单位?具体针对您的情况,您有什么保证整个 START...END 序列将由一个实例处理而不是在中间分开?
- Data Lake Tools documentation 建议 数据原子性的通用单位是 "line"(行结构文件)——这是一个 属性数据上传本身。
- Per USQL Programmatibility guide,
[SqlUserDefinedExtractor(AtomicFileProcessing = true)]
确保整个输入由 1 个实例按顺序处理,这就足够了,并且对于这种情况可能是可行的,具体取决于输入大小。
行集有顺序吗?
不! 行集是无序的概念 - 将它们视为非去重哈希集。
var input = new HashSet<string>(File.ReadLines(@In_Data)); File.WriteAllLines(@Out_NewData, input)
预计不会保留原始行顺序(即使它对某些输入有效,那是实现细节,不能保证语义行为)。
行集也是如此 - 行的输入顺序丢失(无保证)数据被翻译成行集的那一刻。因此,尝试使用 ROW_NUMBER() 是徒劳的——在可以调用 ROW_NUMBER() 之前没有任何顺序可以保留。使用 ROW_NUMBER() 的唯一方法是如果行集有一些键,其排序顺序可以重新创建行的原始顺序。
因为行集没有顺序,无论如何您都需要一个自定义提取器 - 它是脚本中唯一能够观察文件中行顺序的部分,给定
- 它使用 AtomicFileProcessing,或者
- 您找到了一种方法来保证数据拆分不会发生在 START...END 序列之间。 AFAIK 没有办法做到这一点(缺少将整个序列预处理成行预上传)。
您可以选择将所有逻辑包含在自定义提取器中,或者只需添加一个带编号的列来模仿 ROW_NUMBER 并使用本机 U-Sql 作为逻辑。