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;

试一试,告诉我你的进展情况。

相关问题请教:

  1. 在分布式处理系统中,是否所有输入数据(可能是 TB)都由 1 Extractor 实例处理?
    • 绝对不是!如需确认,请参阅 EXTRACT 文档 (msdn.microsoft.com/en-us/library/azure mt621320.aspx).
  2. 给定多个提取器实例,数据可以在哪里拆分?换句话说,一般来说,是什么决定了 U-Sql 中数据的原子性单位?具体针对您的情况,您有什么保证整个 START...END 序列将由一个实例处理而不是在中间分开?
    • Data Lake Tools documentation 建议 数据原子性的通用单位是 "line"(行结构文件)——这是一个 属性数据上传本身。
    • Per USQL Programmatibility guide[SqlUserDefinedExtractor(AtomicFileProcessing = true)] 确保整个输入由 1 个实例按顺序处理,这就足够了,并且对于这种情况可能是可行的,具体取决于输入大小。
  3. 行集有顺序吗?

    • 不! 行集是无序的概念 - 将它们视为非去重哈希集。

      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 作为逻辑。