نظرة عامة على دليل قابلية البرمجة 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"
يوضح هذا المثال سيناريو حالة استخدام أكثر تعقيدا نستخدم فيه متغيرا عاما داخل مقطع التعليمات البرمجية الخلفية الذي يتم تطبيقه على مجموعة صفوف الذاكرة بأكملها.