Delen via


U-SQL-programmeerhandleiding - UDT en UDAGG

Door de gebruiker gedefinieerde typen gebruiken: UDT

Door de gebruiker gedefinieerde typen of UDT is een andere programmeerfunctie van U-SQL. U-SQL UDT fungeert als een normaal door de gebruiker gedefinieerd C#-type. C# is een sterk getypte taal die het gebruik van ingebouwde en aangepaste door de gebruiker gedefinieerde typen toestaat.

U-SQL kan willekeurige UDT's niet impliciet serialiseren of deserialiseren wanneer de UDT wordt doorgegeven tussen hoekpunten in rijensets. Dit betekent dat de gebruiker een expliciete notatie moet opgeven met behulp van de IFormatter-interface. Dit voorziet U-SQL van serialisatie- en deserialisatiemethoden voor de UDT.

Notitie

De ingebouwde extractors en outputters van U-SQL kunnen momenteel geen UDT-gegevens serialiseren of deserialiseren van of naar bestanden, zelfs niet met de IFormatter-set. Dus wanneer u UDT-gegevens naar een bestand schrijft met de UITVOER-instructie, of wanneer u het leest met een extractor, moet u deze doorgeven als een tekenreeks of bytearray. Vervolgens roept u de serialisatie- en deserialisatiecode (dat wil gezegd, de ToString()-methode van UDT expliciet aan. Door de gebruiker gedefinieerde extractoren en uitvoerters kunnen daarentegen UDT's lezen en schrijven.

Als we UDT proberen te gebruiken in EXTRACTOR of OUTPUTTER (uit de vorige SELECT), zoals hier wordt weergegeven:

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

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

De volgende fout wordt weergegeven:

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

Als u met UDT in outputter wilt werken, moeten we deze serialiseren naar een tekenreeks met de methode ToString() of een aangepaste uitvoerter maken.

UDT's kunnen momenteel niet worden gebruikt in GROUP BY. Als UDT wordt gebruikt in GROUP BY, wordt de volgende fout gegenereerd:

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

Als u een UDT wilt definiëren, moet u het volgende doen:

  1. Voeg de volgende naamruimten toe:
using Microsoft.Analytics.Interfaces
using System.IO;
  1. Voeg Microsoft.Analytics.Interfacestoe. Dit is vereist voor de UDT-interfaces. Daarnaast is System.IO mogelijk nodig om de IFormatter-interface te definiëren.

  2. Definieer een gebruikt gedefinieerd type met het kenmerk SqlUserDefinedType.

SqlUserDefinedType wordt gebruikt om een typedefinitie in een assembly te markeren als een door de gebruiker gedefinieerd type (UDT) in U-SQL. De eigenschappen op het kenmerk weerspiegelen de fysieke kenmerken van de UDT. Deze klasse kan niet worden overgenomen.

SqlUserDefinedType is een vereist kenmerk voor de UDT-definitie.

De constructor van de klasse:

  • SqlUserDefinedTypeAttribute (typeformatter)

  • Type formatter: Vereiste parameter voor het definiëren van een UDT formatter- specifiek, het type van de IFormatter interface moet hier worden doorgegeven.

[SqlUserDefinedType(typeof(MyTypeFormatter))]
public class MyType
{ … }
  • Typische UDT vereist ook definitie van de IFormatter-interface, zoals wordt weergegeven in het volgende voorbeeld:
public class MyTypeFormatter : IFormatter<MyType>
{
    public void Serialize(MyType instance, IColumnWriter writer, ISerializationContext context)
    { … }

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

De IFormatter interface serialiseert en ontserialiseerd een objectgrafiek met het hoofdtype van <typeparamref name="T">.

<typeparam name="T">Het hoofdtype voor de objectgrafiek dat moet worden geserialiseerd en gedeserialiseerd.

  • deserialiseren: de-serialiseert de gegevens in de opgegeven stroom en reconstitueert de grafiek van objecten.

  • Serialiseer: Serialiseert een object of objectgrafiek, met de gegeven root naar de opgegeven stroom.

MyType instantie: Instantie van het type. IColumnWriter schrijver/IColumnReader lezer: de onderliggende kolomstroom. ISerializationContext context: Enum die een set vlaggen definieert die de bron- of doelcontext voor de stream specificeert tijdens serialisatie.

  • intermediair: Hiermee geeft u aan dat de bron- of doelcontext geen permanente opslag is.

  • Persistentie: Hiermee geeft u aan dat de bron- of doelcontext een gepersisteerde opslag is.

Als standaard C#-type kan een U-SQL UDT-definitie overschrijvingen voor operators bevatten zoals +/==/!=. Het kan ook statische methoden bevatten. Als we deze UDT bijvoorbeeld gaan gebruiken als parameter voor een U-SQL MIN-aggregeerfunctie, moeten we de <-operator overriden.

Eerder in deze handleiding hebben we een voorbeeld gedemonstreerd voor de identificatie van fiscale perioden vanaf de specifieke datum in de notatie Qn:Pn (Q1:P10). In het volgende voorbeeld ziet u hoe u een aangepast type definieert voor fiscale periodewaarden.

Hieronder volgt een voorbeeld van een code-behind-sectie met aangepaste UDT- en IFormatter-interface:

[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;
        }
    }
}

Het gedefinieerde type bevat twee getallen: kwartaal en maand. Operators ==/!=/>/< en statische methode ToString() worden hier gedefinieerd.

Zoals eerder vermeld, kan UDT worden gebruikt in SELECT-expressies, maar kan niet worden gebruikt in OUTPUTTER/EXTRACTOR zonder aangepaste serialisatie. Het moet worden geserialiseerd als een string met ToString() of worden gebruikt met een aangepaste outputter/extractor.

Laten we nu het gebruik van UDT bespreken. In een code-behind-sectie hebben we de functie GetFiscalPeriod gewijzigd in het volgende:

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);
}

Zoals u ziet, wordt de waarde van het type FiscalPeriod geretourneerd.

Hier vindt u een voorbeeld van hoe u dit verder kunt gebruiken in het U-SQL-basisscript. In dit voorbeeld ziet u verschillende vormen van UDT-aanroep vanuit U-SQL-script.

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();

Hier volgt een voorbeeld van een volledige code-behind-sectie:

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;
                }
            }
        }
    }
}

Door de gebruiker gedefinieerde aggregaties gebruiken: UDAGG

Door de gebruiker gedefinieerde aggregaties zijn aggregatiefuncties die niet standaard worden verzonden met U-SQL. Het voorbeeld kan een aggregatie zijn voor het uitvoeren van aangepaste wiskundige berekeningen, tekenreekssamenvoegingen, bewerkingen met tekenreeksen, enzovoort.

De door de gebruiker gedefinieerde basisklassedefinitie is als volgt:

    [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 geeft aan dat het type moet worden geregistreerd als een door de gebruiker gedefinieerd aggregaat. Deze klasse kan niet worden overgenomen.

Het kenmerk SqlUserDefinedType is optionele voor UDAGG-definitie.

Met de basisklasse kunt u drie abstracte parameters doorgeven: twee als invoerparameters en één als resultaat. De gegevenstypen zijn variabel en moeten worden gedefinieerd tijdens de overname van klassen.

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 wordt één keer aangeroepen voor elke groep tijdens de berekening. Het biedt een initialisatieroutine voor elke aggregatiegroep.
  • verzamelen wordt eenmaal voor elke waarde uitgevoerd. Het biedt de belangrijkste functionaliteit voor het aggregatie-algoritme. Het kan worden gebruikt om waarden samen te voegen met verschillende gegevenstypen die tijdens de overname van klassen worden gedefinieerd. Er kunnen twee parameters van variabele gegevenstypen worden geaccepteerd.
  • Beëindig wordt eenmaal per aggregatiegroep aan het einde van de verwerking uitgevoerd om het resultaat voor elke groep weer te geven.

Als u de juiste invoer- en uitvoergegevenstypen wilt declareren, gebruikt u de klassedefinitie als volgt:

public abstract class IAggregate<T1, T2, TResult> : IAggregate
  • T1: Eerste parameter die moet worden verzameld
  • T2: Tweede parameter die moet worden verzameld
  • TResult: Retourtype beëindiging

Voorbeeld:

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

of

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

UDAGG gebruiken in U-SQL

Als u UDAGG wilt gebruiken, definieert u deze eerst in code-behind of verwijst u ernaar vanuit de bestaande DLL voor programmeerbaarheid, zoals eerder is besproken.

Gebruik vervolgens de volgende syntaxis:

AGG<UDAGG_functionname>(param1,param2)

Hier volgt een voorbeeld van 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;
    }

}

En een basis U-SQL-script:

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();

In dit use-casescenario voegt u klasse-GUID's samen voor de specifieke gebruikers.

Volgende stappen