USQL - 如何在USQL中选择两个字符串行之间的所有行

时间:2016-12-13 09:44:08

标签: c# azure-sql-database azure-data-lake 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]

我需要为此编写自定义提取器吗?如果是,那么请建议我如何。

2 个答案:

答案 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);

我的结果: My results

现在每个部分都标有部分名称,您可以轻松地将数据分配给不同的变量,并可选择包括页眉/页脚行,例如

@dailyAccount =
    SELECT rawData
    FROM @output
    WHERE sectionName == "DAILY ACCOUNT"
          AND isStartSection == 0
          AND isEndSection == 0;

尝试一下,让我知道你是怎么过的。

答案 1 :(得分:1)

要问的相关问题:

  1. 在分布式处理系统中,所有输入数据(可能是TB)都会由 1 Extractor实例处理吗?
    • 绝对不是!有关确认,请参阅EXTRACT文档(msdn.microsoft.com/en-us/library/azure mt621320.aspx)。
  2. 给定多个提取器实例,数据可以在哪里分割?换句话说,一般来说,什么决定了U-Sql中数据的原子性单位?特别针对您的情况,您有什么保证整个START ... END序列将由一个实例处理而不是在中间分开?
    • Data Lake Tools documentation 建议数据原子性的通用单位是&#34; line&#34; (行结构文件) - 这是数据上传本身的属性。
    • 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()的唯一方法是,如果行集有一些Key,其排序顺序可以重新创建行的原始顺序。

  4. 因为行集没有顺序,所以无论如何都需要自定义提取器 - 它是脚本中唯一能够观察文件中行的顺序的部分,给定

    • 它使用AtomicFileProcessing或
    • 您找到了一种方法来保证START ... END序列之间不会发生数据分割。 AFAIK没有办法做到这一点(没有预先处理整个序列到预先上传的行)。

    您可以选择在自定义提取器中包含所有逻辑,或者只是添加一个编号列来模仿ROW_NUMBER并使用本机U-Sql作为逻辑。