1. مقدمة في تحليل البيانات الكلوية
عملية الكلويّة هي مفهوم مهم جدًا في استعلامات قواعد البيانات. وعادةً ما تُستخدم للتحليل الإحصائي مثل الجمع، العد، الوسط الحسابي، الحد الأقصى، والحد الأدنى. تُساعد هذه العمليات المستخدمين في استخراج معلومات ذات دلالة من كميات كبيرة من البيانات، مما يوفر الدعم لتحليل البيانات واتخاذ القرارات. ويُشير عادةً إلى الدوال المنفذة في قاعدة البيانات للكلويّة بأنها دوال كلويّة.
2. العمليات الكلوية الأساسية
2.1 مفهوم الدوال الكلوية
الدوال الكلوية هي دوال تُستخدم في لغات استعلامات قواعد البيانات لإجراء سلسلة من الحسابات وإرجاع قيمة واحدة. في SQL ولغات الاستعلام المماثلة، يمكن للدوال الكلوية العمل على عمود من البيانات وإرجاع قيمة واحدة، مثل المجموع (SUM)، الوسط الحسابي (AVG)، العدد (COUNT)، الحد الأقصى (MAX)، والحد الأدنى (MIN). عندما نحتاج إلى إجراء تحليل إحصائي للبيانات، تُعتبر الدوال الكلوية أدوات مهمة لمعالجة مجموعات البيانات واستخراج البيانات الإحصائية.
2.2 الكلويّة لحقل واحد
في التطبيقات العملية، تُعد تحليل الكلويّة لحقل واحد متطلبًا شائعًا جدًا، وغالبًا ما يُستخدم للحصول على نتائج إحصائية مثل المجموع، الوسط الحسابي، الحد الأقصى، والحد الأدنى لعمود معين. لنفترض أن لدينا جدول معلومات الدفع، ونرغب في حساب المبلغ الإجمالي الذي دفعه المستخدمون. باستخدام الإطار ent
، يمكننا إنشاء استعلام من الكيان وتطبيق الدوال الكلوية على النحو التالي:
func Do(ctx context.Context, client *ent.Client) {
// حساب مجموع حقل القيمة لكيان الدفع
sum, err := client.Payment.Query().
Aggregate(
ent.Sum(payment.Amount),
).
Int(ctx)
if err != nil {
log.Fatalf("فشل في الحصول على المجموع: %v", err)
}
log.Printf("المبلغ الإجمالي للدفعات: %d", sum)
}
في المقتطف البرمجي أعلاه، نقوم ببدء استعلام لكيان الدفع باستخدام client.Payment.Query()
، ثم نستخدم الطريقة Aggregate()
لاستدعاء دالة ent.Sum
بمعلمة payment.Amount
لحساب المبلغ الإجمالي للدفعات. نستخدم .Int(ctx)
لتحويل نتيجة الكلويّة إلى عدد صحيح وتسجيلها.
2.3 الكلويّة لحقول متعددة
في العديد من الحالات، نحتاج إلى إجراء عمليات كلويّة على حقول متعددة بدلاً من حقل واحد فقط. في هذا القسم، سنوضح كيفية تحقيق الكلويّة لحقول متعددة من خلال مثال.
في هذا المثال، سنقوم بجمع الأعمار، وإيجاد العمر الأدنى، وإيجاد العمر الأقصى، وعدد حقل العمر
في جدول الحيوانات الأليفة.
func Do(ctx context.Context, client *ent.Client) {
var v []struct {
Sum, Min, Max, Count int
}
err := client.Pet.Query().
Aggregate(
ent.Sum(pet.FieldAge), // مجموع الأعمار
ent.Min(pet.FieldAge), // العمر الأدنى
ent.Max(pet.FieldAge), // العمر الأقصى
ent.Count(), // العدد
).
Scan(ctx, &v)
if err != nil {
log.Fatalf("فشل الاستعلام: %v", err)
}
// إخراج جميع نتائج الكلويّة
for _, agg := range v {
fmt.Printf("المجموع: %d الحد الأدنى: %d الحد الأقصى: %d العدد: %d\n", agg.Sum, agg.Min, agg.Max, agg.Count)
}
}
في الكود أعلاه، نستخدم دالة Aggregate
لإجراء كلويّة لحقول متعددة، ونستخدم دالة Scan
لتخزين نتائج الكلويّة في الشريط v
. ثم نكرر الشريط v
لإخراج جميع نتائج الكلويّة.
3. تطبيق الكلويّة بواسطة Group By
3.1 استخدام Group By لتجميع الحقول
في العمليات الخاصة بقواعد البيانات، Group By
هو طريقة شائعة لتجميع البيانات. في هذا القسم، سنتعلم كيفية استخدام Group By
لتجميع البيانات في قاعدة البيانات.
مثال تعليمي، كيفية تجميع حقل واحد أو أكثر باستخدام Group By.
لنفترض أن لدينا جدول مستخدمين ونحتاج إلى تجميع حقل الاسم
للمستخدمين وحساب عدد المستخدمين في كل مجموعة. فيما يلي مثال للكود عن كيفية تحقيق هذا المتطلب:
func Do(ctx context.Context, client *ent.Client) {
names, err := client.User.
Query().
GroupBy(user.FieldName).
Strings(ctx)
if err != nil {
log.Fatalf("فشل تنفيذ الاستعلام المجمع: %v", err)
}
// إخراج اسم كل مجموعة
for _, name := range names {
fmt.Println("اسم المجموعة:", name)
}
}
في الكود أعلاه، نستخدم طريقة GroupBy
لبناء الاستعلام للتحديد أي حقل نرغب في تجميعه.
3.2. تجميع البيانات والحسابات باستخدام حقول متعددة
أحيانًا، نحتاج إلى تجميع البيانات استنادًا إلى حقول متعددة وتنفيذ وظائف تجميع على كل مجموعة. فيما يلي مثال على كيفية تحقيق هذا المتطلب:
يوضح الكود التالي كيفية تجميع البيانات في جدول المستخدمين استنادًا إلى حقول "الاسم" و"العمر"، وحساب إجمالي الأعمار وعدد المستخدمين في كل مجموعة.
func Do(ctx context.Context, client *ent.Client) {
var v []struct {
Name string `json:"name"`
Age int `json:"age"`
Sum int `json:"sum"`
Count int `json:"count"`
}
err := client.User.Query().
GroupBy(user.FieldName, user.FieldAge).
Aggregate(ent.Count(), ent.Sum(user.FieldAge)).
Scan(ctx, &v)
if err != nil {
log.Fatalf("فشل تنفيذ استعلام تجميع الحقول المتعددة: %v", err)
}
// اخراج معلومات مفصلة لكل مجموعة
for _, group := range v {
fmt.Printf("الاسم: %s العمر: %d المجموع: %d العدد: %d\n", group.Name, group.Age, group.Sum, group.Count)
}
}
في هذا المثال، نقوم ليس فقط بتجميع البيانات استنادًا إلى حقول "الاسم" و"العمر" في جدول المستخدمين، ولكننا أيضًا نستخدم وظائف "العدد" و"المجموع" لحساب إجمالي عدد السجلات وإجمالي الأعمار في كل مجموعة.
4. الجمع بين الفلترة "Having" وعملية التجميع "Group By"
يقوم شرط "Having" بتصفية نتائج الوظائف التجميعية التي تم الحصول عليها بعد عملية "Group By". يوضح المثال التالي كيفية تحديد المستخدمين فقط بأكبر عمر في كل دور:
func Do(ctx context.Context, client *ent.Client) {
var users []struct {
Id Int
Age Int
Role string
}
err := client.User.Query().
Modify(func(s *sql.Selector) {
s.GroupBy(user.FieldRole)
s.Having(
sql.EQ(
user.FieldAge,
sql.Raw(sql.Max(user.FieldAge)),
),
)
}).
ScanX(ctx, &users)
if err != nil {
log.Fatalf("فشل تنفيذ الاستعلام المجموع مع الفلترة (Having) مجتمع مع عملية التجميع (Group By): %v", err)
}
// اخراج معلومات المستخدم الذي يستوفي شرط "Having"
for _, user := range users {
fmt.Printf("المعرّف: %d العمر: %d الدور: %s\n", user.Id, user.Age, user.Role)
}
}
سيقوم الكود السابق بإنشاء استعلام SQL مكافئ لتحديد المستخدمين بأكبر عمر في كل دور.