使用 U-SQL 在日期范围内生成日期

Generate dates in a date range using U-SQL

我需要用定义的开始日期和结束日期之间的所有日期填充一个行集。如果我的开始日期是 19/7/2017,结束日期是 21/7/2017,那么行集应该包含 19/7/2017、20/7/2017 和 21/7/2017。

我想知道是否有一种简单的方法可以使用 U-SQL

第 1 步:您需要对行集中的行进行确定性排序,这样在逻辑上才有意义。因此,找出您要按

对行进行排序的列

第 2 步:获取分配给每一行的行号。这是 https://msdn.microsoft.com/en-us/library/azure/mt763822.aspx

的示例

第 3 步:您可以使用分配给每行的行号与 C# 表达式相结合来生成每行应有的日期。

最简单的方法是从您最喜欢的仓库中导出您最喜欢的日期维度并将其导入到 U-SQL table.

您也可以使用自定义 U-SQL 代码来执行此操作,如下所示:

DECLARE @outputFilepath string = "output/output74.csv";

//DECLARE @startDate DateTime = DateTime.Parse("19/7/2017");
//DECLARE @endDate DateTime = DateTime.Parse("21/7/2017");

DECLARE @startDate DateTime = DateTime.Parse("1/1/2000");
DECLARE @endDate DateTime = DateTime.Parse("31/12/2017");


// User-defined appliers
// Take one row and produce 0 to n rows
// Used with OUTER/CROSS APPLY
@output =
    SELECT outputDate
    FROM(
        VALUES ( 1 ) 
        ) AS dummy(x)
        CROSS APPLY new USQLtpch.makeDateRange (@startDate, @endDate) AS properties(outputDate DateTime);


OUTPUT @output
TO @outputFilepath
USING Outputters.Tsv();

代码隐藏文件:

using Microsoft.Analytics.Interfaces;
using Microsoft.Analytics.Types.Sql;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace USQLtpch
{

    [SqlUserDefinedApplier]
    public class makeDateRange : IApplier
    {
        private DateTime startDate;
        private DateTime endDate;

        public makeDateRange(DateTime startDate, DateTime endDate)
        {
            this.startDate = startDate;
            this.endDate = endDate;
        }

        public override IEnumerable<IRow> Apply(IRow input, IUpdatableRow output)
        {

            // Initialise
            DateTime outputDate = this.startDate;


            // Loop until date range has been filled out
            while (outputDate <= endDate)
            {
                output.Set<DateTime>("outputDate", outputDate);

                // Increment date
                outputDate = outputDate.AddDays(1);

                yield return output.AsReadOnly();

            }
        }
    }
}

我使用自定义 Applier 完成了此操作,该 Applier 获取 1 行并将其转换为 0 或 n。

我们始终建议开发人员首先研究使用纯 U-SQL 方法而不是使用 C# UDO,这是完成此任务的另一种方法。

首先,考虑如何在 U-SQL

中获取数字列表
@numbers_10 = 
    SELECT
        *
    FROM 
    (VALUES
        (0),
        (1),
        (2),
        (3),
        (4),
        (5),
        (6),
        (7),
        (8),
        (9)
    ) AS T(Value);

这只是 10 个数字 - 0 到 9。我们可以使用 CROSS JOIN 来扩展列表。

@numbers_100 = 
    SELECT (a.Value*10 + b.Value) AS Value
    FROM @numbers_10 AS a 
        CROSS JOIN @numbers_10 AS b;

现在我们有 0 到 99。我们可以使用 CROSS JOIN 生成更多数字。

@numbers_10000 = 
    SELECT (a.Value*100 + b.Value) AS Value
    FROM @numbers_100 AS a CROSS JOIN @numbers_100 AS b;

然后从中生成一个日期列表。

DECLARE @StartDate = DateTime.Parse("1979-03-31");

...

@result = 
    SELECT 
        Value,
        @StartDate.AddDays( Value ) AS Date
    FROM @numbers_10000;

完整的脚本如下所示:

DECLARE @StartDate = DateTime.Parse("1979-03-31");

@numbers_10 = 
    SELECT
        *
    FROM 
    (VALUES
        (0),
        (1),
        (2),
        (3),
        (4),
        (5),
        (6),
        (7),
        (8),
        (9)
    ) AS T(Value);

@numbers_100 = 
    SELECT (a.Value*10 + b.Value) AS Value
    FROM @numbers_10 AS a CROSS JOIN @numbers_10 AS b;

@numbers_10000 = 
    SELECT (a.Value*100 + b.Value) AS Value
    FROM @numbers_100 AS a CROSS JOIN @numbers_100 AS b;

@result = 
    SELECT 
        Value,
        @StartDate.AddDays( Value ) AS Date
    FROM @numbers_10000;

OUTPUT @result TO "/res.csv" USING Outputters.Csv(outputHeader:true);

获得数字或日期列表后,将其保存到 U-SQL table 中可能会很方便,这样您以后可以轻松检索列表。

这是 U-SQL 语言的 .Net 元素可以发挥巨大作用的主要示例。在这种情况下,您可以 explodeEnumerable.Range 来获取可应用于数据的递增值列表:

DECLARE @startDate DateTime = DateTime.Parse("2000/01/01");
DECLARE @endDate DateTime = DateTime.Parse("2017/12/31");

@dates =
    SELECT d.DateValue
    FROM (VALUES(@startDate)) AS sd(s)
         CROSS APPLY    // EXPLODE creates a rowset from all the values in the given list
             EXPLODE(Enumerable.Range(0
                                     ,(@endDate - @startDate).Days
                                     )
                                     .Select(offset => sd.s.AddDays(offset))
                    ) AS d(DateValue)
    ;