4

我从源代码转换的每一行都需要唯一的 guid。
以下是示例脚本;代码 Guid.NewGuid() 总是为所有行返回相同的

@Person =
    EXTRACT SourceId          int,
            AreaCode          string,
            AreaDetail         string,
            City        string
    FROM "/Staging/Person"
    USING Extractors.Tsv(nullEscape:"#NULL#");

@rs1 =
    SELECT 
    Guid.NewGuid() AS PersonId,
    AreaCode,
    AreaDetail,
    City    
    FROM @Person;

OUTPUT @rs1   
    TO "/Datamart/DimUser.tsv"
      USING Outputters.Tsv(quoting:false, dateTimeFormat:null);
4

3 回答 3

7

请注意,U-SQL 是一种声明性语言,因此将快照已知的非确定性函数,例如每个脚本的一个值Guid.NewGuid()DateTime.Now

虽然您可以通过将此类函数包装到 C# 函数中来解决此问题,但非常不鼓励这种做法,因为您使脚本具有不确定性,如果必须重试执行中的节点并且不执行,则可能导致脚本失败产生可重复的结果!

那么如何提供唯一编号呢?

选项包括:

  1. 如果可以更改数据生成,请添加外部数据中已有的值。
  2. Skolemization:编写一个确定性表达式,将关键属性组合成一个唯一值。
  3. 用于ROW_NUMBER() OVER ()您阅读的数据。如果您已经有需要保证唯一性的数据,请添加作业运行时间的时间刻度,或者获取最高的现有值,或者获得足够大的间隔凸点,具体取决于您的要求。

这是一个示例,它使用时间刻度加上ROW_NUBER()确保每次运行脚本时每行的 id 都是唯一的,因为如上所述,U-SQL 将DateTime.Now在每次脚本调用时评估一次:

@data =
SELECT *
FROM (VALUES
      ( "John", "Doe" ),
      ( "Paul", "Miller" ),
      ( "Tracy", "Smith" ),
      ( "Jane", "Doe")
     ) AS T(firstname, lastname);

@res = 
SELECT DateTime.Now.Ticks+ROW_NUMBER() OVER () AS id, 
       firstname, lastname
FROM @data;

OUTPUT @res
TO "/output/data.csv"
USING Outputters.Csv();
于 2016-08-15T23:24:14.277 回答
5

对该问题的快速总结是,您不应尝试通过依赖于生成新 Guid 的技术或任何其他“基于时间”的方法来分配唯一值。这样做的原因是,由于顶点重试、性能优化等原因,U-SQL 中的行可能会被重新计算。

在这些情况下,这些值将重新分配一个新值,并最终在运行 U-SQL 脚本时导致错误 - 因为 U-SQL 要求行对于输入数据是确定性的。

与其分配新的 Guid,不如使用 ROW_NUMBER 窗口函数,它可以安全地向行添加新的唯一编号。我

@result =
    SELECT 
        *,
        ROW_NUMBER() OVER () AS UID
    FROM @querylog;
于 2016-08-15T23:21:37.317 回答
2

在代码隐藏中创建一个 udf:

namespace USQL_Namespace
{
    public static class Udfs
    {
        public static string newGuidString()
        {
            return Guid.NewGuid().ToString();
        }
    }

并内联引用它:

@o = 
    SELECT USQL_Namespace.Udfs.newGuidString() AS newId;
于 2017-05-20T15:54:30.380 回答