Panduan kemampuan pemrograman U-SQL - UDT dan UDAGG

Gunakan jenis yang ditentukan pengguna: UDT

Jenis yang ditentukan pengguna, atau UDT, adalah fitur programabilitas lain dari U-SQL. U-SQL UDT berfungsi seperti jenis yang ditentukan pengguna C# biasa. C# adalah bahasa pemrogram yang diketik dengan kuat yang memungkinkan penggunaan jenis bawaan dan kustom yang ditentukan pengguna.

U-SQL tidak dapat secara implisit menserialisasikan atau mendeserialisasi UDT arbitrer ketika UDT diteruskan di antara simpul dalam set baris. Hal ini berarti bahwa pengguna harus memberikan pemformat yang eksplisit dengan menggunakan antarmuka IFormatter. Ini menyediakan U-SQL dengan metode serialisasi dan deserialisasi untuk UDT.

Catatan

Ekstraktor dan outputter bawaan U-SQL saat ini tidak dapat menserialisasikan atau mendeserialisasi data UDT ke atau dari file bahkan dengan set IFormatter. Jadi, ketika Anda menulis data UDT ke file dengan pernyataan OUTPUT, atau membacanya dengan ekstraktor, Anda harus meneruskannya sebagai string atau array byte. Kemudian Anda memanggil kode serialisasi dan deserialisasi (yaitu, metode ToString() UDT) secara eksplisit. Ekstraktor dan perangkat output yang ditentukan pengguna, di sisi lain, dapat membaca dan menulis UDT.

Jika kita mencoba menggunakan UDT dalam EXTRACTOR atau OUTPUTTER (di luar SELECT sebelumnya), seperti yang ditunjukkan di sini:

@rs1 =
    SELECT
        MyNameSpace.Myfunction_Returning_UDT(filed1) AS myfield
    FROM @rs0;

OUTPUT @rs1
    TO @output_file
    USING Outputters.Text();

Kami menerima kesalahan berikut:

Error	1	E_CSC_USER_INVALIDTYPEINOUTPUTTER: Outputters.Text was used to output column myfield of type
MyNameSpace.Myfunction_Returning_UDT.

Description:

Outputters.Text only supports built-in types.

Resolution:

Implement a custom outputter that knows how to serialize this type, or call a serialization method on the type in
the preceding SELECT.	C:\Users\sergeypu\Documents\Visual Studio 2013\Projects\USQL-Programmability\
USQL-Programmability\Types.usql	52	1	USQL-Programmability

Untuk menjalankan UDT di perangkat output, kita harus melakukan serialisasi untuk string dengan metode ToString() atau membuat perangkat output kustom.

UDT saat ini tidak dapat digunakan di GROUP BY. Jika UDT digunakan dalam GROUP BY, kesalahan berikut ditampilkan:

Error	1	E_CSC_USER_INVALIDTYPEINCLAUSE: GROUP BY doesn't support type MyNameSpace.Myfunction_Returning_UDT
for column myfield

Description:

GROUP BY doesn't support UDT or Complex types.

Resolution:

Add a SELECT statement where you can project a scalar column that you want to use with GROUP BY.
C:\Users\sergeypu\Documents\Visual Studio 2013\Projects\USQL-Programmability\USQL-Programmability\Types.usql
62	5	USQL-Programmability

Untuk menentukan UDT, kita harus:

  1. Menambahkan namespace berikut:
using Microsoft.Analytics.Interfaces
using System.IO;
  1. Tambahkan Microsoft.Analytics.Interfaces, yang diperlukan untuk antarmuka UDT. Selain itu, System.IO mungkin diperlukan untuk menentukan antarmuka IFormatter.

  2. Tentukan jenis yang ditentukan pengguna dengan atribut SqlUserDefinedType.

SqlUserDefinedType digunakan untuk menandai definisi jenis dalam rakitan sebagai jenis yang ditentukan pengguna (UDT) di U-SQL. Properti pada atribut mencerminkan karakteristik fisik UDT. Kelas ini tidak dapat diwariskan.

SqlUserDefinedType adalah atribut yang diperlukan untuk definisi UDT.

Konstruktor kelas:

  • SqlUserDefinedTypeAttribute (pemformat jenis)

  • Pemformat jenis: Parameter yang diperlukan untuk menentukan pemformat UDT--khususnya, jenis antarmuka IFormatter harus diteruskan di sini.

[SqlUserDefinedType(typeof(MyTypeFormatter))]
public class MyType
{ … }
  • UDT khas juga memerlukan definisi antarmuka IFormatter, seperti yang ditunjukkan dalam contoh berikut:
public class MyTypeFormatter : IFormatter<MyType>
{
    public void Serialize(MyType instance, IColumnWriter writer, ISerializationContext context)
    { … }

    public MyType Deserialize(IColumnReader reader, ISerializationContext context)
    { … }
}

Antarmuka IFormatter menserialisasikan dan mendeserialisasi grafik objek dengan jenis akar typeparamref <name="T">.

<typeparam name="T">Jenis akar untuk grafik objek untuk membuat serialisasi dan deserialisasi.

  • Deserialisasi: Mendeserialisasi data pada aliran yang diberikan dan menyusun kembali grafik objek.

  • Serialisasi: Melakukan serialisasi objek, atau grafik objek, dengan akar yang diberikan ke aliran yang disediakan.

MyType instans: Instans jenisnya. IColumnWriter penulis / IColumnReader pembaca: Aliran kolom yang mendasarinya. ISerializationContext konteks: Enum yang menentukan set bendera yang menentukan konteks sumber atau tujuan untuk aliran selama serialisasi.

  • Perantara: Menentukan bahwa konteks sumber atau tujuan bukan penyimpanan yang bertahan.

  • Persistensi: Menentukan bahwa konteks sumber atau tujuan bukan penyimpanan tersimpan.

Sebagai jenis C# biasa, definisi U-SQL UDT dapat mencakup penggantian untuk operator seperti +/==/!=. U-SQL UDT juga dapat mencakup metode statik. Misalnya, jika kita akan menggunakan UDT ini sebagai parameter untuk fungsi agregat U-SQL MIN, kita harus menentukan < penimpaan operator.

Sebelumnya dalam panduan ini, kami menunjukkan contoh untuk identifikasi periode fiskal dari tanggal tertentu dalam format Qn:Pn (Q1:P10). Contoh berikut menunjukkan cara menentukan jenis kustom untuk nilai periode fiskal.

Berikut ini adalah contoh bagian kode-belakang dengan antarmuka UDT dan IFormatter kustom:

[SqlUserDefinedType(typeof(FiscalPeriodFormatter))]
public struct FiscalPeriod
{
    public int Quarter { get; private set; }

    public int Month { get; private set; }

    public FiscalPeriod(int quarter, int month):this()
    {
        this.Quarter = quarter;
        this.Month = month;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }

        return obj is FiscalPeriod && Equals((FiscalPeriod)obj);
    }

    public bool Equals(FiscalPeriod other)
    {
return this.Quarter.Equals(other.Quarter) && this.Month.Equals(other.Month);
    }

    public bool GreaterThan(FiscalPeriod other)
    {
return this.Quarter.CompareTo(other.Quarter) > 0 || this.Month.CompareTo(other.Month) > 0;
    }

    public bool LessThan(FiscalPeriod other)
    {
return this.Quarter.CompareTo(other.Quarter) < 0 || this.Month.CompareTo(other.Month) < 0;
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return (this.Quarter.GetHashCode() * 397) ^ this.Month.GetHashCode();
        }
    }

    public static FiscalPeriod operator +(FiscalPeriod c1, FiscalPeriod c2)
    {
return new FiscalPeriod((c1.Quarter + c2.Quarter) > 4 ? (c1.Quarter + c2.Quarter)-4 : (c1.Quarter + c2.Quarter), (c1.Month + c2.Month) > 12 ? (c1.Month + c2.Month) - 12 : (c1.Month + c2.Month));
    }

    public static bool operator ==(FiscalPeriod c1, FiscalPeriod c2)
    {
        return c1.Equals(c2);
    }

    public static bool operator !=(FiscalPeriod c1, FiscalPeriod c2)
    {
        return !c1.Equals(c2);
    }
    public static bool operator >(FiscalPeriod c1, FiscalPeriod c2)
    {
        return c1.GreaterThan(c2);
    }
    public static bool operator <(FiscalPeriod c1, FiscalPeriod c2)
    {
        return c1.LessThan(c2);
    }
    public override string ToString()
    {
        return (String.Format("Q{0}:P{1}", this.Quarter, this.Month));
    }

}

public class FiscalPeriodFormatter : IFormatter<FiscalPeriod>
{
    public void Serialize(FiscalPeriod instance, IColumnWriter writer, ISerializationContext context)
    {
        using (var binaryWriter = new BinaryWriter(writer.BaseStream))
        {
            binaryWriter.Write(instance.Quarter);
            binaryWriter.Write(instance.Month);
            binaryWriter.Flush();
        }
    }

    public FiscalPeriod Deserialize(IColumnReader reader, ISerializationContext context)
    {
        using (var binaryReader = new BinaryReader(reader.BaseStream))
        {
var result = new FiscalPeriod(binaryReader.ReadInt16(), binaryReader.ReadInt16());
            return result;
        }
    }
}

Jenis yang ditentukan mencakup dua angka: kuartal dan bulan. Operator ==/!=/>/< dan metode statik ToString() ditentukan di sini.

Seperti disebutkan sebelumnya, UDT dapat digunakan dalam ekspresi SELECT, tetapi tidak dapat digunakan dalam OUTPUTTER/EXTRACTOR tanpa serialisasi kustom. UDT harus diserialisasikan sebagai untai (karakter) dengan ToString() atau digunakan dengan OUTPUTTER / EXTRACTOR kustom.

Sekarang mari kita diskusikan penggunaan UDT. Di bagian kode-belakang, kami mengubah fungsi GetFiscalPeriod menjadi berikut:

public static FiscalPeriod GetFiscalPeriodWithCustomType(DateTime dt)
{
    int FiscalMonth = 0;
    if (dt.Month < 7)
    {
        FiscalMonth = dt.Month + 6;
    }
    else
    {
        FiscalMonth = dt.Month - 6;
    }

    int FiscalQuarter = 0;
    if (FiscalMonth >= 1 && FiscalMonth <= 3)
    {
        FiscalQuarter = 1;
    }
    if (FiscalMonth >= 4 && FiscalMonth <= 6)
    {
        FiscalQuarter = 2;
    }
    if (FiscalMonth >= 7 && FiscalMonth <= 9)
    {
        FiscalQuarter = 3;
    }
    if (FiscalMonth >= 10 && FiscalMonth <= 12)
    {
        FiscalQuarter = 4;
    }

    return new FiscalPeriod(FiscalQuarter, FiscalMonth);
}

Seperti yang Anda lihat, fungsi menghasilkan nilai dari jenis FiscalPeriod kami.

Di sini kami memberikan contoh cara menggunakan fungsi lebih lanjut dalam skrip dasar U-SQL. Contoh ini menunjukkan berbagai bentuk invokasi UDT dari skrip U-SQL.

DECLARE @input_file string = @"c:\work\cosmos\usql-programmability\input_file.tsv";
DECLARE @output_file string = @"c:\work\cosmos\usql-programmability\output_file.tsv";

@rs0 =
    EXTRACT
        guid string,
        dt DateTime,
        user String,
        des String
    FROM @input_file USING Extractors.Tsv();

@rs1 =
    SELECT
        guid AS start_id,
        dt,
        DateTime.Now.ToString("M/d/yyyy") AS Nowdate,
        USQL_Programmability.CustomFunctions.GetFiscalPeriodWithCustomType(dt).Quarter AS fiscalquarter,
        USQL_Programmability.CustomFunctions.GetFiscalPeriodWithCustomType(dt).Month AS fiscalmonth,
        USQL_Programmability.CustomFunctions.GetFiscalPeriodWithCustomType(dt) + new USQL_Programmability.CustomFunctions.FiscalPeriod(1,7) AS fiscalperiod_adjusted,
        user,
        des
    FROM @rs0;

@rs2 =
    SELECT
        start_id,
        dt,
        DateTime.Now.ToString("M/d/yyyy") AS Nowdate,
        fiscalquarter,
        fiscalmonth,
        USQL_Programmability.CustomFunctions.GetFiscalPeriodWithCustomType(dt).ToString() AS fiscalperiod,

           // This user-defined type was created in the prior SELECT.  Passing the UDT to this subsequent SELECT would have failed if the UDT was not annotated with an IFormatter.
           fiscalperiod_adjusted.ToString() AS fiscalperiod_adjusted,
           user,
           des
    FROM @rs1;

OUTPUT @rs2
    TO @output_file
    USING Outputters.Text();

Berikut ini contoh bagian kode-belakang lengkap:

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

namespace USQL_Programmability
{
    public class CustomFunctions
    {
        static public DateTime? ToDateTime(string dt)
        {
            DateTime dtValue;

            if (!DateTime.TryParse(dt, out dtValue))
                return Convert.ToDateTime(dt);
            else
                return null;
        }

        public static FiscalPeriod GetFiscalPeriodWithCustomType(DateTime dt)
        {
            int FiscalMonth = 0;
            if (dt.Month < 7)
            {
                FiscalMonth = dt.Month + 6;
            }
            else
            {
                FiscalMonth = dt.Month - 6;
            }

            int FiscalQuarter = 0;
            if (FiscalMonth >= 1 && FiscalMonth <= 3)
            {
                FiscalQuarter = 1;
            }
            if (FiscalMonth >= 4 && FiscalMonth <= 6)
            {
                FiscalQuarter = 2;
            }
            if (FiscalMonth >= 7 && FiscalMonth <= 9)
            {
                FiscalQuarter = 3;
            }
            if (FiscalMonth >= 10 && FiscalMonth <= 12)
            {
                FiscalQuarter = 4;
            }

            return new FiscalPeriod(FiscalQuarter, FiscalMonth);
        }        [SqlUserDefinedType(typeof(FiscalPeriodFormatter))]
        public struct FiscalPeriod
        {
            public int Quarter { get; private set; }

            public int Month { get; private set; }

            public FiscalPeriod(int quarter, int month):this()
            {
                this.Quarter = quarter;
                this.Month = month;
            }

            public override bool Equals(object obj)
            {
                if (ReferenceEquals(null, obj))
                {
                    return false;
                }

                return obj is FiscalPeriod && Equals((FiscalPeriod)obj);
            }

            public bool Equals(FiscalPeriod other)
            {
return this.Quarter.Equals(other.Quarter) &&    this.Month.Equals(other.Month);
            }

            public bool GreaterThan(FiscalPeriod other)
            {
return this.Quarter.CompareTo(other.Quarter) > 0 || this.Month.CompareTo(other.Month) > 0;
            }

            public bool LessThan(FiscalPeriod other)
            {
return this.Quarter.CompareTo(other.Quarter) < 0 || this.Month.CompareTo(other.Month) < 0;
            }

            public override int GetHashCode()
            {
                unchecked
                {
                    return (this.Quarter.GetHashCode() * 397) ^ this.Month.GetHashCode();
                }
            }

            public static FiscalPeriod operator +(FiscalPeriod c1, FiscalPeriod c2)
            {
return new FiscalPeriod((c1.Quarter + c2.Quarter) > 4 ? (c1.Quarter + c2.Quarter)-4 : (c1.Quarter + c2.Quarter), (c1.Month + c2.Month) > 12 ? (c1.Month + c2.Month) - 12 : (c1.Month + c2.Month));
            }

            public static bool operator ==(FiscalPeriod c1, FiscalPeriod c2)
            {
                return c1.Equals(c2);
            }

            public static bool operator !=(FiscalPeriod c1, FiscalPeriod c2)
            {
                return !c1.Equals(c2);
            }
            public static bool operator >(FiscalPeriod c1, FiscalPeriod c2)
            {
                return c1.GreaterThan(c2);
            }
            public static bool operator <(FiscalPeriod c1, FiscalPeriod c2)
            {
                return c1.LessThan(c2);
            }
            public override string ToString()
            {
                return (String.Format("Q{0}:P{1}", this.Quarter, this.Month));
            }

        }

        public class FiscalPeriodFormatter : IFormatter<FiscalPeriod>
        {
public void Serialize(FiscalPeriod instance, IColumnWriter writer, ISerializationContext context)
            {
                using (var binaryWriter = new BinaryWriter(writer.BaseStream))
                {
                    binaryWriter.Write(instance.Quarter);
                    binaryWriter.Write(instance.Month);
                    binaryWriter.Flush();
                }
            }

public FiscalPeriod Deserialize(IColumnReader reader, ISerializationContext context)
            {
                using (var binaryReader = new BinaryReader(reader.BaseStream))
                {
var result = new FiscalPeriod(binaryReader.ReadInt16(), binaryReader.ReadInt16());
                    return result;
                }
            }
        }
    }
}

Menggunakan agregat yang ditentukan pengguna: UDAGG

Agregat yang ditentukan pengguna adalah fungsi terkait agregasi apa pun yang tidak dikirim secara langsung dengan U-SQL. Contohnya dapat berupa agregat untuk melakukan perhitungan matematika kustom, perangkaian untai (karakter), manipulasi untai (karakter), dan sebagainya.

Definisi kelas dasar agregat yang ditentukan pengguna adalah sebagai berikut:

    [SqlUserDefinedAggregate]
    public abstract class IAggregate<T1, T2, TResult> : IAggregate
    {
        protected IAggregate();

        public abstract void Accumulate(T1 t1, T2 t2);
        public abstract void Init();
        public abstract TResult Terminate();
    }

SqlUserDefinedAggregate menunjukkan bahwa jenis harus terdaftar sebagai agregat yang ditentukan pengguna. Kelas ini tidak dapat diwariskan.

Atribut SqlUserDefinedType bersifat opsional untuk definisi UDAGG.

Kelas dasar memungkinkan Anda untuk meneruskan tiga parameter abstrak: dua sebagai parameter input dan satu sebagai hasilnya. Jenis data adalah variabel dan harus ditentukan selama pewarisan kelas.

public class GuidAggregate : IAggregate<string, string, string>
{
    string guid_agg;

    public override void Init()
    { … }

    public override void Accumulate(string guid, string user)
    { … }

    public override string Terminate()
    { … }
}
  • Init digunakan sekali untuk masing-masing grup selama komputasi. Init memberikan rutinitas inisialisasi untuk masing-masing grup agregasi.
  • Akumulasi dijalankan satu kali untuk masing-masing nilai. Akumulasi memberikan fungsionalitas utama untuk algoritma agregasi. Akumulasi dapat digunakan untuk mengagregasi nilai dengan berbagai jenis data yang ditentukan selama pewarisan kelas. Akumulasi dapat menerima dua parameter jenis data variabel.
  • Penghentian dijalankan sekali per grup agregasi di akhir pemrosesan agar menghasilkan hasil untuk masing-masing grup.

Untuk mendeklarasikan jenis data input dan output yang benar, gunakan definisi kelas sebagai berikut:

public abstract class IAggregate<T1, T2, TResult> : IAggregate
  • T1: Parameter pertama yang terakumulasi
  • T2: Parameter kedua yang terakumulasi
  • TResult: Jenis hasil yang dihentikan

Contohnya:

public class GuidAggregate : IAggregate<string, int, int>

atau

public class GuidAggregate : IAggregate<string, string, string>

Menggunakan UDAGG di U-SQL

Untuk menggunakan UDAGG, pertama-tama tentukan dalam kode-belakang atau referensikan dari DLL programabilitas yang ada seperti yang dibahas sebelumnya.

Kemudian gunakan sintaks berikut:

AGG<UDAGG_functionname>(param1,param2)

Berikut adalah contoh UDAGG:

public class GuidAggregate : IAggregate<string, string, string>
{
    string guid_agg;

    public override void Init()
    {
        guid_agg = "";
    }

    public override void Accumulate(string guid, string user)
    {
        if (user.ToUpper()== "USER1")
        {
            guid_agg += "{" + guid + "}";
        }
    }

    public override string Terminate()
    {
        return guid_agg;
    }

}

Dan skrip U-SQL dasar:

DECLARE @input_file string = @"\usql-programmability\input_file.tsv";
DECLARE @output_file string = @" \usql-programmability\output_file.tsv";

@rs0 =
    EXTRACT
            guid string,
            dt DateTime,
            user String,
            des String
    FROM @input_file
    USING Extractors.Tsv();

@rs1 =
    SELECT
        user,
        AGG<USQL_Programmability.GuidAggregate>(guid,user) AS guid_list
    FROM @rs0
    GROUP BY user;

OUTPUT @rs1 TO @output_file USING Outputters.Text();

Dalam skenario penggunaan ini, kami menggabungkan GUID kelas untuk pengguna tertentu.

Langkah berikutnya