نظرة عامة على دليل قابلية البرمجة U-SQL

U-SQL هي لغة استعلام مصممة لنوع البيانات الضخمة من أحمال العمل. واحدة من الميزات الفريدة ل U-SQL هي الجمع بين اللغة التعريفية الشبيهة SQL مع قابلية التوسعة والبرمجة التي توفرها C #. في هذا الدليل ، نركز على قابلية التوسع والبرمجة للغة U-SQL التي تم تمكينها بواسطة C #.

المتطلبات

قم بتنزيل أدوات بحيرة بيانات Azure وتثبيتها Visual Studio.

بدء العمل مع U-SQL

انظر إلى البرنامج النصي U-SQL التالي:

@a  = 
  SELECT * FROM 
    (VALUES
       ("Contoso",   1500.0, "2017-03-39"),
       ("Woodgrove", 2700.0, "2017-04-10")
    ) AS D( customer, amount, date );

@results =
  SELECT
    customer,
    amount,
    date
  FROM @a;    

يحدد هذا البرنامج النصي مجموعتين من RowSets: @a و @results. يتم تعريف RowSet @results من @a.

أنواع وتعبيرات C# في البرنامج النصي U-SQL

تعبير U-SQL هو تعبير C # مقترن بالعمليات المنطقية U-SQL مثل AND، ORو NOT. يمكن استخدام تعبيرات U-SQL مع SELECT ، EXTRACT ، WHERE ، HAVE ، GROUP BY و CLARE. على سبيل المثال، يقوم البرنامج النصي التالي بتحليل سلسلة كقيمة DateTime.

@results =
  SELECT
    customer,
    amount,
    DateTime.Parse(date) AS date
  FROM @a;    

يقوم المقتطف التالي بتحليل سلسلة كقيمة DateTime في عبارة DECLARE .

DECLARE @d = DateTime.Parse("2016/01/01");

استخدام تعبيرات C# لإحالات تحويل نوع البيانات

يوضح المثال التالي كيفية إجراء تحويل بيانات datetime باستخدام تعبيرات C #. في هذا السيناريو بالذات، يتم تحويل بيانات datetime السلسلة إلى datetime قياسي مع تدوين وقت منتصف الليل 00:00:00.

DECLARE @dt = "2016-07-06 10:23:15";

@rs1 =
  SELECT 
    Convert.ToDateTime(Convert.ToDateTime(@dt).ToString("yyyy-MM-dd")) AS dt,
    dt AS olddt
  FROM @rs0;

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

استخدام تعبيرات C# لتاريخ اليوم

لسحب تاريخ اليوم ، يمكننا استخدام تعبير C # التالي: DateTime.Now.ToString("M/d/yyyy")

فيما يلي مثال على كيفية استخدام هذا التعبير في برنامج نصي:

@rs1 =
  SELECT
    MAX(guid) AS start_id,
    MIN(dt) AS start_time,
    MIN(Convert.ToDateTime(Convert.ToDateTime(dt<@default_dt?@default_dt:dt).ToString("yyyy-MM-dd"))) AS start_zero_time,
    MIN(USQL_Programmability.CustomFunctions.GetFiscalPeriod(dt)) AS start_fiscalperiod,
    DateTime.Now.ToString("M/d/yyyy") AS Nowdate,
    user,
    des
  FROM @rs0
  GROUP BY user, des;

استخدام تجميعات .NET

يعتمد نموذج قابلية التوسعة في U-SQL بشكل كبير على القدرة على إضافة تعليمات برمجية مخصصة من تجميعات .NET.

تسجيل تجميع .NET

استخدم العبارة CREATE ASSEMBLY لوضع تجميع .NET في قاعدة بيانات U-SQL. بعد ذلك ، يمكن للنصوص النصية U-SQL استخدام هذه التجميعات باستخدام العبارةREFERENCE ASSEMBLY.

توضح التعليمة البرمجية التالية كيفية تسجيل تجميع:

CREATE ASSEMBLY MyDB.[MyAssembly]
   FROM "/myassembly.dll";

توضح التعليمة البرمجية التالية كيفية الرجوع إلى تجميع:

REFERENCE ASSEMBLY MyDB.[MyAssembly];

راجع تعليمات تسجيل التجميع التي تغطي هذا الموضوع بمزيد من التفصيل.

استخدام تعيين إصدار التجميع

حاليا، يستخدم U-SQL الإصدار .NET Framework 4.7.2. لذا تأكد من أن التجميعات الخاصة بك متوافقة مع هذا الإصدار من وقت التشغيل.

كما ذكرنا سابقا ، يقوم U-SQL بتشغيل التعليمات البرمجية بتنسيق 64 بت (x64). لذا تأكد من تجميع التعليمات البرمجية الخاصة بك لتشغيلها على x64. وإلا فستحصل على خطأ التنسيق غير الصحيح الذي تم عرضه مسبقا.

يمكن أن يكون كل ملف DLL وملف مورد تم تحميله، مثل وقت تشغيل مختلف أو تجميع أصلي أو ملف تكوين، 400 ميغابايت على الأكثر. لا يمكن أن يتجاوز الحجم الإجمالي للموارد المنشورة، إما عبر DEPLOY RESOURCE أو عبر مراجع إلى التجميعات وملفاتها الإضافية، 3 غيغابايت.

أخيرا ، لاحظ أن كل قاعدة بيانات U-SQL يمكن أن تحتوي فقط على إصدار واحد من أي تجميع معين. على سبيل المثال ، إذا كنت بحاجة إلى كل من الإصدار 7 والإصدار 8 من مكتبة NewtonSoft Json.NET ، فأنت بحاجة إلى تسجيلهما في قاعدتي بيانات مختلفتين. علاوة على ذلك ، يمكن أن يشير كل برنامج نصي فقط إلى إصدار واحد من DLL تجميع معين. في هذا الصدد ، يتبع U-SQL إدارة تجميع C # ودلالات الإصدار.

استخدام الوظائف المعرفة من قبل المستخدم: UDF

الدالات المعرفة من قبل المستخدم SQL U- ، أو UDF ، هي إجراءات برمجة تقبل المعلمات ، وتنفذ إجراء (مثل عملية حسابية معقدة) ، وترجع نتيجة هذا الإجراء كقيمة. يمكن أن تكون قيمة إرجاع UDF رقما قياسيا واحدا فقط. يمكن استدعاء U-SQL UDF في البرنامج النصي الأساسي U-SQL مثل أي دالة رقمية C # أخرى.

نوصي بتهيئة وظائف U-SQL المعرفة من قبل المستخدم كوظائف عامةوثابتة.

public static string MyFunction(string param1)
{
    return "my result";
}

أولا دعونا نلقي نظرة على المثال البسيط لإنشاء UDF.

في سيناريو حالة الاستخدام هذا، نحتاج إلى تحديد الفترة المالية، بما في ذلك الربع المالي والشهر المالي لأول تسجيل دخول للمستخدم المحدد. الشهر المالي الأول من السنة في السيناريو الخاص بنا هو يونيو.

لحساب الفترة المالية ، نقدم وظيفة C # التالية:

public static string GetFiscalPeriod(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 "Q" + FiscalQuarter.ToString() + ":P" + FiscalMonth.ToString();
}

إنه ببساطة يحسب الشهر المالي والربع المالي وإرجاع قيمة السلسلة. بالنسبة لشهر يونيو، وهو الشهر الأول من الربع المالي الأول، نستخدم "Q1:P1". بالنسبة لشهر يوليو ، نستخدم "Q1: P2" ، وما إلى ذلك.

هذه هي وظيفة C # العادية التي سنستخدمها في مشروع U-SQL الخاص بنا.

فيما يلي كيفية ظهور قسم التعليمات البرمجية الخلفية في هذا السيناريو:

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

namespace USQL_Programmability
{
    public class CustomFunctions
    {
        public static string GetFiscalPeriod(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 "Q" + FiscalQuarter.ToString() + ":" + FiscalMonth.ToString();
        }
    }
}

الآن سنسمي هذه الوظيفة من البرنامج النصي U-SQL الأساسي. للقيام بذلك ، يتعين علينا توفير اسم مؤهل بالكامل للدالة ، بما في ذلك مساحة الاسم ، والتي في هذه الحالة هي NameSpace.Class.Function (معلمة).

USQL_Programmability.CustomFunctions.GetFiscalPeriod(dt)

فيما يلي البرنامج النصي الأساسي الفعلي ل U-SQL:

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

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

DECLARE @default_dt DateTime = Convert.ToDateTime("06/01/2016");

@rs1 =
    SELECT
        MAX(guid) AS start_id,
	MIN(dt) AS start_time,
        MIN(Convert.ToDateTime(Convert.ToDateTime(dt<@default_dt?@default_dt:dt).ToString("yyyy-MM-dd"))) AS start_zero_time,
        MIN(USQL_Programmability.CustomFunctions.GetFiscalPeriod(dt)) AS start_fiscalperiod,
        user,
        des
    FROM @rs0
    GROUP BY user, des;

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

فيما يلي ملف الإخراج لتنفيذ البرنامج النصي:

0d8b9630-d5ca-11e5-8329-251efa3a2941,2016-02-11T07:04:17.2630000-08:00,2016-06-01T00:00:00.0000000,"Q3:8","User1",""

20843640-d771-11e5-b87b-8b7265c75a44,2016-02-11T07:04:17.2630000-08:00,2016-06-01T00:00:00.0000000,"Q3:8","User2",""

301f23d2-d690-11e5-9a98-4b4f60a1836f,2016-02-11T09:01:33.9720000-08:00,2016-06-01T00:00:00.0000000,"Q3:8","User3",""

يوضح هذا المثال استخداما بسيطا ل UDF المضمن في U-SQL.

الحفاظ على الحالة بين استدعاءات UDF

يمكن أن تكون كائنات قابلية البرمجة U-SQL C # أكثر تعقيدا ، باستخدام التفاعل من خلال المتغيرات العالمية وراء التعليمات البرمجية. دعونا نلقي نظرة على سيناريو حالة استخدام الأعمال التالي.

في المؤسسات الكبيرة، يمكن للمستخدمين التبديل بين أنواع مختلفة من التطبيقات الداخلية. يمكن أن تتضمن هذه Microsoft Dynamics CRM و Power BI وما إلى ذلك. قد يرغب العملاء في تطبيق تحليل القياس عن بعد لكيفية تبديل المستخدمين بين التطبيقات المختلفة ، وما هي اتجاهات الاستخدام ، وما إلى ذلك. الهدف من العمل هو تحسين استخدام التطبيق. قد يرغبون أيضا في الجمع بين تطبيقات مختلفة أو إجراءات تسجيل دخول محددة.

لتحقيق هذا الهدف ، يتعين علينا تحديد معرفات الجلسة ووقت التأخير بين آخر جلسة حدثت.

نحتاج إلى العثور على تسجيل دخول سابق ثم تعيين تسجيل الدخول هذا لجميع الجلسات التي يتم إنشاؤها لنفس التطبيق. التحدي الأول هو أن البرنامج النصي الأساسي U-SQL لا يسمح لنا بتطبيق الحسابات على الأعمدة المحسوبة بالفعل باستخدام دالة LAG. والتحدي الثاني هو أنه يتعين علينا أن نبقي على الدورة المحددة لجميع الدورات في غضون نفس الفترة الزمنية.

لحل هذه المشكلة ، نستخدم متغيرا عاما داخل قسم التعليمات البرمجية الخلفية: static public string globalSession;.

يتم تطبيق هذا المتغير العمومي على مجموعة الصفوف بأكملها أثناء تنفيذ البرنامج النصي.

فيما يلي قسم التعليمات البرمجية الخلفية لبرنامج U-SQL الخاص بنا:

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

namespace USQLApplication21
{
    public class UserSession
    {
        static public string globalSession;
        static public string StampUserSession(string eventTime, string PreviousRow, string Session)
        {

            if (!string.IsNullOrEmpty(PreviousRow))
            {
                double timeGap = Convert.ToDateTime(eventTime).Subtract(Convert.ToDateTime(PreviousRow)).TotalMinutes;
                if (timeGap <= 60) {return Session;}
                else {return Guid.NewGuid().ToString();}
            }
            else {return Guid.NewGuid().ToString();}

        }

        static public string getStampUserSession(string Session)
        {
            if (Session != globalSession && !string.IsNullOrEmpty(Session)) { globalSession = Session; }
            return globalSession;
        }

    }
}

يوضح هذا المثال المتغير العمومي static public string globalSession; المستخدم داخل الدالة getStampUserSession وإعادة تهيئته في كل مرة يتم فيها تغيير معلمة جلسة العمل.

البرنامج النصي الأساسي U-SQL هو كما يلي:

DECLARE @in string = @"\UserSession\test1.tsv";
DECLARE @out1 string = @"\UserSession\Out1.csv";
DECLARE @out2 string = @"\UserSession\Out2.csv";
DECLARE @out3 string = @"\UserSession\Out3.csv";

@records =
    EXTRACT DataId string,
            EventDateTime string,           
            UserName string,
            UserSessionTimestamp string

    FROM @in
    USING Extractors.Tsv();

@rs1 =
    SELECT 
        EventDateTime,
        UserName,
	LAG(EventDateTime, 1) 
		OVER(PARTITION BY UserName ORDER BY EventDateTime ASC) AS prevDateTime,          
        string.IsNullOrEmpty(LAG(EventDateTime, 1) 
		OVER(PARTITION BY UserName ORDER BY EventDateTime ASC)) AS Flag,           
        USQLApplication21.UserSession.StampUserSession
           (
           	EventDateTime,
           	LAG(EventDateTime, 1) OVER(PARTITION BY UserName ORDER BY EventDateTime ASC),
           	LAG(UserSessionTimestamp, 1) OVER(PARTITION BY UserName ORDER BY EventDateTime ASC)
           ) AS UserSessionTimestamp
    FROM @records;

@rs2 =
    SELECT 
    	EventDateTime,
        UserName,
        LAG(EventDateTime, 1) 
		OVER(PARTITION BY UserName ORDER BY EventDateTime ASC) AS prevDateTime,
        string.IsNullOrEmpty( LAG(EventDateTime, 1) OVER(PARTITION BY UserName ORDER BY EventDateTime ASC)) AS Flag,
        USQLApplication21.UserSession.getStampUserSession(UserSessionTimestamp) AS UserSessionTimestamp
    FROM @rs1
    WHERE UserName != "UserName";

OUTPUT @rs2
    TO @out2
    ORDER BY UserName, EventDateTime ASC
    USING Outputters.Csv();

يتم استدعاء الدالة USQLApplication21.UserSession.getStampUserSession(UserSessionTimestamp) هنا أثناء حساب مجموعة صفوف الذاكرة الثانية. يمر UserSessionTimestamp العمود وإرجاع القيمة حتى UserSessionTimestamp يتغير.

ملف الإخراج هو كما يلي:

"2016-02-19T07:32:36.8420000-08:00","User1",,True,"72a0660e-22df-428e-b672-e0977007177f"
"2016-02-17T11:52:43.6350000-08:00","User2",,True,"4a0cd19a-6e67-4d95-a119-4eda590226ba"
"2016-02-17T11:59:08.8320000-08:00","User2","2016-02-17T11:52:43.6350000-08:00",False,"4a0cd19a-6e67-4d95-a119-4eda590226ba"
"2016-02-11T07:04:17.2630000-08:00","User3",,True,"51860a7a-1610-4f74-a9ea-69d5eef7cd9c"
"2016-02-11T07:10:33.9720000-08:00","User3","2016-02-11T07:04:17.2630000-08:00",False,"51860a7a-1610-4f74-a9ea-69d5eef7cd9c"
"2016-02-15T21:27:41.8210000-08:00","User3","2016-02-11T07:10:33.9720000-08:00",False,"4d2bc48d-bdf3-4591-a9c1-7b15ceb8e074"
"2016-02-16T05:48:49.6360000-08:00","User3","2016-02-15T21:27:41.8210000-08:00",False,"dd3006d0-2dcd-42d0-b3a2-bc03dd77c8b9"
"2016-02-16T06:22:43.6390000-08:00","User3","2016-02-16T05:48:49.6360000-08:00",False,"dd3006d0-2dcd-42d0-b3a2-bc03dd77c8b9"
"2016-02-17T16:29:53.2280000-08:00","User3","2016-02-16T06:22:43.6390000-08:00",False,"2fa899c7-eecf-4b1b-a8cd-30c5357b4f3a"
"2016-02-17T16:39:07.2430000-08:00","User3","2016-02-17T16:29:53.2280000-08:00",False,"2fa899c7-eecf-4b1b-a8cd-30c5357b4f3a"
"2016-02-17T17:20:39.3220000-08:00","User3","2016-02-17T16:39:07.2430000-08:00",False,"2fa899c7-eecf-4b1b-a8cd-30c5357b4f3a"
"2016-02-19T05:23:54.5710000-08:00","User3","2016-02-17T17:20:39.3220000-08:00",False,"6ca7ed80-c149-4c22-b24b-94ff5b0d824d"
"2016-02-19T05:48:37.7510000-08:00","User3","2016-02-19T05:23:54.5710000-08:00",False,"6ca7ed80-c149-4c22-b24b-94ff5b0d824d"
"2016-02-19T06:40:27.4830000-08:00","User3","2016-02-19T05:48:37.7510000-08:00",False,"6ca7ed80-c149-4c22-b24b-94ff5b0d824d"
"2016-02-19T07:27:37.7550000-08:00","User3","2016-02-19T06:40:27.4830000-08:00",False,"6ca7ed80-c149-4c22-b24b-94ff5b0d824d"
"2016-02-19T19:35:40.9450000-08:00","User3","2016-02-19T07:27:37.7550000-08:00",False,"3f385f0b-3e68-4456-ac74-ff6cef093674"
"2016-02-20T00:07:37.8250000-08:00","User3","2016-02-19T19:35:40.9450000-08:00",False,"685f76d5-ca48-4c58-b77d-bd3a9ddb33da"
"2016-02-11T09:01:33.9720000-08:00","User4",,True,"9f0cf696-c8ba-449a-8d5f-1ca6ed8f2ee8"
"2016-02-17T06:30:38.6210000-08:00","User4","2016-02-11T09:01:33.9720000-08:00",False,"8b11fd2a-01bf-4a5e-a9af-3c92c4e4382a"
"2016-02-17T22:15:26.4020000-08:00","User4","2016-02-17T06:30:38.6210000-08:00",False,"4e1cb707-3b5f-49c1-90c7-9b33b86ca1f4"
"2016-02-18T14:37:27.6560000-08:00","User4","2016-02-17T22:15:26.4020000-08:00",False,"f4e44400-e837-40ed-8dfd-2ea264d4e338"
"2016-02-19T01:20:31.4800000-08:00","User4","2016-02-18T14:37:27.6560000-08:00",False,"2136f4cf-7c7d-43c1-8ae2-08f4ad6a6e08"

يوضح هذا المثال سيناريو حالة استخدام أكثر تعقيدا نستخدم فيه متغيرا عاما داخل مقطع التعليمات البرمجية الخلفية الذي يتم تطبيقه على مجموعة صفوف الذاكرة بأكملها.

الخطوات التالية