اعزائي الكرام
السلام عليكم ورحمة الله وبركاته
سنناقش في هذاالموضوع عدداً من المشاكل التي قد تصادفكم لدى تعاملكم مع البيانات , وسنعرض الحلول الممكنه لها. واقترح أن تحاولوا إيجاد حل قبل قراءة الحل المعطى في النص . وعلينا أن نشير هنا إلي أن هناك عدة طرق لحل مشكلة معينة . في الحقيقه قد نتمكن من الحصول على حل أكثر فعاليه من الحل المقترح هنا إن الهدف الأساسي من هذه المشاكل والحلول هي إعطاؤكم غذاء فكريا نافعا .
قبل البدء, دعونا نشير إلي أن الكثير من الحلول المقترحة يعتمد على استخدام الاستعلامات الفرعيه .
يسمح Access SQL بأستخدام عبارات SeIectضمن عبارات SeIect أخرى (كما هي الحال في عبارات أخرى مثل عبارات ( INSERT INTO) . تدعى عبارة SeIect الداخليه أو المتشابكه بالاستعلام الفرعي.
لاحظ أنه يمكن أن تضمن عبارة SeIect داخليه ضمن عبارة SeIect أساسية فقط إذا كانت عبارة SeIect الداخلية تعيد سجلا واحدا على الأغلب ولبيان ذلك لنأخذ عبارة SQL الأساسية التالية:
SELECT HOUR, (Select Count(Interval) From StartTimes WHERE (StartTime <=Hour)) FROM HOURS
تعيد عبارة Select الداخلية :
Select Count(Interval) From StartTimes WHERE (StartTime <=Hour)
على الأغلب سجلاً واحدا لأنها تعيد عدد (Count) لاحظ أيضاً أن ظرف WHEREفي عبارة SeIect الداخليه يشير إلى حقل HOUR الذي هو جزء من عبارة SQL الرئيسية , وبالتالي فانه يقوم بربط القيمه المسترجعه للعبارة الداخلية إلى السجل الحالي في جدول HOURS
المجاميع الجارية : Running Sums
يعتبر حساب المجاميع الجارية من العمليات الشائعه ولبيان هذا المفهوم سنأخذ الجدول ( 1-1) الذي يتضمن المدد الزمنيه ( محسوبة بالساعة ) لعدة أحداث .

ونريد أن نحسب لكل حدث مجموع المدد الزمنية للآحداث التي تسبقه يدعي هذا المجموع بالمجموع الجاري .
الحل : Solution
إن إحدى طرق الحل هي بإجراء الجداء السلمي للجدول مع نفسه نستطيع بهذه الطريقه الوصول إلي جميع السجلات التي يسبق رقم حدثها ( Event ) رقم سجل (أوحدث) معين . على سبيل المثال ,من أجل السجل الخامس ، نحتاج الي الوصول إلي السجلات التي تتضمن الاحداث رقم 1 وحتى 4 . أن الجداء السلمي يعطينا هذه السجلات .
إليك عبارة SQL التي تقوم بهذا العمل :
SELECT Running.Event, Sum(RunningCopy.Duration) AS StartTime FROM Running, Running AS RunningCopy WHERE (RunningCopy.Event < Running.Event) GROUP BY Running.Event
يقوم ظرف FORM بإنشاء الجداء السلمي للجدول مع نفسه . أما ظرف WHERE فيحدد السجلات التي تحقق المعيار
Running Copy.Event < Running. Event
أي ان السجلات التي ستقدم المعلوكات حول السجلات التي تسبق كل سجل في الجدول ( 1-1 ).
أخيرا ، نقوم بتجميع النتائج حسب الحدث ( Group By Event ) وحساب مجموع المدد الزمنية . ان المشكلة هي ان الجداء السلمي غير فعال ويستخدم حجما كبيرا من الموارد ( إذا كان عدد سجلات الجدول ( 1-1 ) 100000 سجل فإن الجداء السلمي سينفذ عملية ضرب على 100000 × 100000 سجل أي 10.000.000.000 سجل .
ان الحل الاكثر فعالية هو استخدام عبارة Select متشابكة ( داخلية ) أي استخدام عبارة Select ضمن عبارة Select اساسية . نعود لنذكر هنا بأن Access SQL يسمح بذلك اذا كانت العبارة الداخلية تعيد على الغالب سجلا واحدا .
لاحظ استخدمنا لألقاب ( نسخ ) للجدول في عبارة Select التالية ونحتاج الى هذه الالقاب لاننا سنشير الى الجدول ( 1-1 ) في سياقين
Select R1.Event (Select Sum(R2.Duration) From Running As R2 Where R2.Event <R1.Event) As StartTime FROM Running As R1
ان عبارة SQL الداخلية :
Select Sum(R2.Duration) From Running As R2 Where R2.Event <R1.Event
تعيد مجموع المدة لجميع الاحداث التي تسبق الحدث الحالي ، والذي نشير إليه بالعبارة R1.Event
يعرض المثال ( 1-1 ) اجرائية VBA لتنفيذ عبارة SQL السابقة . ويستخدم الكائن DoCmd في VBA لتشغيل اجراء Access وبالتالي فأن السطر البرمجي :
DoCmd.OpenQuery
يقوم بفتح استعلام في Access
المثال ( 1-1 ) : حساب المجموع الجاري بإستخدام عبارة SQL
Private Sub RunningSumSQL ()
Dim db As ADO.Database
Set db = CurrentDb
Dim qry As ADO.QueryDef
Dim sSQL As String
On Error Resume Next
db.QueryDefs.Delete "temp"
On Error GoTo 0
sSQL = "SELECT R1.Event," & _
" (SELECT SUM(R2.Duration)" & _
"FROM Running As R2" & _
"WHERE R2.Event < R1.Event)" & _
"AS StartTime" & _
" FROM Running As R1"
Set qry = db.CreateQueryDef("temp", sSQL)
DoCmd.OpenQuery qry.Nqme
End Sub
هناك طريقة أخرى تقوم على استخدام ADO الذي يؤمن طريق أو حل بسيط في هذه الحالة فهو ينشئ جدول نتائج دائم في حين ان الحل السابق يقوم بإنشاء استعلام تحديد ويعرض المثال ( 1-2 ) شفرة ADO التي تنفذ نفس العمل
Private Sub RunningSumDAO( )
Dim db As Database
Dim rs As Recordset
Dim lRunningSum As Long
Set db = CurrentDb
lRunningSum = 0
Set rs = db.OpenRecordset("SELECT * FROM Running ORDER BY Event")
Do While Not rs.EOF
rs.Edit
rs!RunningSum = lRunningSum
rs.Update
lRunningSum = lRunningSum + rs!Duration
rs.MoveNext
Loop
rs.Close
End Subالمجالات المتقاطعة : Overlapping Interavl
تشير الصفوف في الجدول ( 1-2 ) الى مجالات زمنية ، المشكلة هي تعيين من أجل كل ساعة من اليوم ، عدد المجالات التي تتضمن هذه الساعة .

ولحل هذه المشكلة نستخدم جدول HOURS المبين في الجدول ( 1-3 )

الحل : Solution
يمكن حل هذه المشكلة بإستخدام عبارة Select متشابكة ( أي بإستخدام استعلام فرعي ) والكود التالي يقوم بالعمل المطلوب :
Private Sub OverlappingIntervals( )
Dim db As Database
Set db = CurrentDb
Dim qry As QueryDef
Dim sSQL As String
On Error Resume Next
db.QueryDefs.Delete "temp"
On Error GoTo 0
sSQL = "SELECT Hours.Hour, " & _
" (SELECT Count(Interval) AS CountOfIntervals" & _
" FROM Overlap" & _
" WHERE (StartTime <= Hours.Hour) And" & _
" (Hours.Hour < EndTime))" & _
" FROM Hours"
Set qry = db.CreateQueryDef("temp", sSQL)
DoCmd.OpenQuery qry.Name
End Sub
يتبع

















