اذهب الي المحتوي
أوفيسنا

قواعد البيانات بين النظرية والتطبيق


zahrah2016

الردود الموصى بها

قواعد البيانات بين النظرية والتطبيق

اعزائي الكرام
السلام عليكم ورحمة الله وبركاته
سنناقش في هذاالموضوع عدداً من المشاكل التي قد تصادفكم لدى تعاملكم مع البيانات , وسنعرض الحلول الممكنه لها. واقترح أن تحاولوا إيجاد حل قبل قراءة الحل المعطى في النص . وعلينا أن نشير هنا إلي أن هناك عدة طرق لحل مشكلة معينة . في الحقيقه قد نتمكن من الحصول على حل أكثر فعاليه من الحل المقترح هنا إن الهدف الأساسي من هذه المشاكل والحلول هي إعطاؤكم غذاء فكريا نافعا .
قبل البدء, دعونا نشير إلي أن الكثير من الحلول المقترحة يعتمد على استخدام الاستعلامات الفرعيه .
يسمح 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) الذي يتضمن المدد الزمنيه ( محسوبة بالساعة ) لعدة أحداث .
a92260c486.gif
ونريد أن نحسب لكل حدث مجموع المدد الزمنية للآحداث التي تسبقه يدعي هذا المجموع بالمجموع الجاري . الحل : 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 ) الى مجالات زمنية ، المشكلة هي تعيين من أجل كل ساعة من اليوم ، عدد المجالات التي تتضمن هذه الساعة .
4719dcc2eb.gif
ولحل هذه المشكلة نستخدم جدول HOURS المبين في الجدول ( 1-3 )
7ff017210d.gif
الحل : 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


يتبع

  • Like 1
رابط هذا التعليق
شارك

المجالات المتقاطعه Overlapping Intervals II : II

تستخدم شركه عمالاً ومشرفين ، وخلال يوم عمل عادي ، يعمل كل عامل وكل مشرف ورديه واحده أو ورديتين ، تتألف من ساعات عمل متعاقبه ، يعرض الجدول ( 4-1 ) يوماً تقليدياً من الساعه 12 ظهراً وحتى الساعه 12 في منتصف الليل .

e4709af4a7.gif

نريد هنا حساب العدد الآعظمي للعمال الذين يشرف عليهم مشرف واحد في وقت معين .

الحل : Solution

يمكن معالجة هذه المشكله بإجراء مؤلف من مرحلتين ، نقوم اولاً بحساب عدد العمال في كل ساعه ونأخذ عبارة SQL التاليه :

SELECT Hours.Hour, 
(SELECT Count(EmpType) FROM SuperLoad WHERE (Starthour <= Hours.Hour) And (Hours.Hour < EndHour) And (EmpType='Worker')) AS CountOfWorkers 
FROM Hours
لاحظ هنا اننا نستخدم استعلاماً فرعياً يعيد سجلاً وحيداً يعطي عدد العمال الذين يعملون في ساعه معينه . بإستخدام عبارة SQL هذه نقوم بإنشاء استعلام اسمه qry1 بحيث يمكن استخدامه في المرحله الثانيه . انظر الجدول ( 1-5) من اجل نتائج هذا الإستعلام .
a4d51fbc4a.gif
الخطوه التاليه هي حساب تحميل المشرف على انه العدد الآعظمي للعمال في كل ورديه للمشرف ، نقوم بذلك باستخدام اسم الآستعلام من المرحله السابقه في عبارة SQL التاليه :
sSQL = "SELECT SuperLoad.EmpID, SuperLoad.EmpType," & _ 
" (SELECT Max(CountOfWorkers) AS WorkerLoad" & _ 
" FROM [" & qry1.Name & "]" & _ 
" WHERE((Hours.Hour >= StartHour) And(Hours.Hour< Endhour)))" & _ 
" FROM SuperLoad " & _ 
" WHERE SuperLoad.EmpType = 'Super' "
والنتائج مبينه في الجدول ( 1-6)
94d59a9d38.gif
والكود الكامل لهذا الحل مبينه في المثال ( 1-3) المثال ( 1-3) : حساب العدد الآعظمي للعمال من اجل كل مشرف .
Private Sub SupervisorLoad( ) 
Dim db As Database 
Set db = CurrentDb
Dim qry1 As QueryDef 
Dim qry2 As QueryDef 
Dim sSQL1 As String 
Dim sSQL2 As String
On Error Resume Next 
db.QueryDefs.Delete "temp1" 
db.QueryDefs.Delete "temp2" 
On Error GoTo 0
sSQL = "SELECT Hours.Hour, " & _ 
" (SELECT Count(Emptype) FROM SuperLoad" & _ 
" WHERE(StartHour <= Hours.Hour) And (Hours.Hour < EndHour)" & _ 
" And (Emptype='Worker'))" & _ 
" AS CountOfWorkers" & _ 
" FROM Hours"
Set qry1 = db.CreateQueryDef("temp1", sSQL1) 
' Uncomment to see how this step looks 
' DoCmd.OpenQuery qry1.Name
sSQL = "SELECT SuperLoad.EmpID, SuperLoad.EmpType," & _ 
" (SELECT Max(CountOfWorkers) AS WorkerLoad" & _ 
" FROM [" & qry1.Name & "]" & _ 
" WHERE((Hours.Hour >= StartHour) And (Hours.Hour< Endhour)))" & _ 
" FROM SuperLoad " & _ 
" WHERE SuperLoad.EmpType = 'Super' "
Set qry2 = db.CreateQueryDef("temp2", sSQL2)
DoCmd.OpenQuery qry2.Name 
End Sub
إجراءات عمليات الإسناد باستخدام القيمه الافتراضية : Assignment With Default Making تخيل وجود مؤتمر، ومهمتك فيه توزيع الحضور على قاعات المحاضرات ، يعرض الجدول ( 1-7) الحضور المسجل مع ارقام الغرف الموافقه .
8459be90b7.gif
لاحظ ان الجدول يتضمن عدة خيارات افتراضيه ( Default ) وإذا لم يكن شخص موجوداً في الجدول ، فإنك تريد تخصيصه بغرفه من الغرف الآفتراضيه ، بالإضافه الى ذلك ، ولتجنب الازدحام في الغرف ، تريد تخصيص ارقام الغرف الافتراضيه بشكل عشوائي ، فكيف نقوم بذلك . الحل : Solution يمكن حل هذه المشكله بعدة طرق ، تقوم إحداها على استخدام الاستعلامات الفرعيه مع عبارةUNION لنأخذ اولا عبارة SQL التاليه :
sSQL1="SELECT ROOM From Assignment WHERE (Name=[Enter Name])"
نذكر بأن استخدام عبارة (Enter Narme ) هو عبارة عن وسيط . وعند تشغيل الاستعلام ، يتم الطلب من المستخدم إدخال اسم يتم استبداله بالوسيط المذكور ، ستعيد هذه العباره السجل الموافق لاسم معين إذا كان موجوداً ، وإلا فإنها ستعيد مجموعة سجلات فارغه . لتأخذ الآن عبارة SQL التاليه : يتظمن المثال ( 1-4 ) الكود الكامل لتنفيذ الحل المطلوب . معالجة توزيع الغرف الافتراضية والمحجوزة مسبقا .
sSQL2 = "SELECT Room FROM Assignment" & _ 
"WHERE (Name = '_default') AND ([Enter Name] NOT IN (SELECT Name FROM Assignment))"
إن عبارة
[Enter Name] NOT IN (SELECT Name FROM Assignment)
ستعيد القيمه True إذا وفقط إذا كان الاسم الذي ادخله المستخدم غير موجود في الجدول وبالتالي فإن عبارة sSQL2 يمكن ان تكتب من جديد استناداً الى حالتين : 1.الاسم في الجدول
sSQL2 = "SELECT Room FROM Assignment WHERE (Name = '_default') AND False"
2. الاسم ليس في الجدول
sSQL1 = "SELECT Room FROM Assignment WHERE (Name = '_default') AND True"
ويمكن اختصار العباره من اجل حالة الاسم في الجدول لتصبح :
sSQL2 = "SELECT Room FROM Assignment WHERE FALSE
والعبارة من اجل حالة الاسم ليس في الجدول :
sSQL1 = "SELECT Room FROM Assignment WHERE (Name = '_default')"
وهكذا فإن العباره ستعيد مجموعة سجلات فارغه اذا كان الاسم في الجدول والسجلات الافتراضيه اذا لم يكن الاسم في الجدول . سنأخذ الآن دمج أو توحيد العبارتين :
sSQL3=sSQL1 & "UNION" & sSQL2
ستعيد هذه العباره رقم الغرفه من اجل اسم اذا كان الاسم في الجدول وإلا فإنها ستعيد السجلات الافتراضيه ان كل ماتحتاجه الان هو استرجاع سجل عشوائي . لاحظ ان ذلك يمكن ان يعمل في كلا الحالتين ، لانه اذا كان الاسم في الجدول ، فهناك سجل واحد وبالتالي فإن الخيار العشوائي هو ذلك السجل . يتضمن الكود التالي الشيفرة الكاملة للحل المطلوب معالجة توزيع الغرف الافتراضية والمحجوزة مسبقا .
Private Sub AssignmentWithDefault( ) 
Dim db As Database 
Set db = CurrentDb 
Dim sName As String 
Dim qry1 As QueryDef 
Dim rs As Recordset 
Dim sSQL1 As String 
Dim sSQL2 As String 
Dim sSQL3 As String 
Dim lRandom As Long 
Dim lcRecords As Long 
On Error Resume Next 
db.QueryDefs.Delete "temp1" 
On Error GoTo 0 
sSQL1 = "SELECT Room FROM Assignment" & _ 
"WHERE (Name = [Enter Name])" 
sSQL2 = "SELECT Room FROM Assignment" & _ 
"WHERE (Name = '_default') AND ([Enter Name] NOT IN (SELECT Name 
FROM Assignment))" 
sSQL3 = sSQL1 & " UNION " & sSQL2 
Set qry1 = db.CreateQueryDef("temp", sSQL3) 
sName = InputBox("Enter name") 
qry1.Parameters(0) = sName 

' لمشاهدة النتائج 
' 'DoCmd.OpenQuery qry1.Name
Set rs = qry1.OpenRecordset 
' Populate and get recordCount 
rs.MoveLast 
lcRecords = rs.RecordCount 

' Random record 
Randomize Timer 
' lRandom is between 0 and lcRecords-1 
lRandom = Int(lcRecords * Rnd) 
rs.MoveFirst 
rs.Move lRandom 
MsgBox "Room for " & sName & " is " & rs!Room 
End Sub

يتبع

رابط هذا التعليق
شارك

زمن الإنجاز : Time to Completion

سنعرض هنا مشكله بسيطه حول تحديد الزمن الكلي لإنجاز مهمه ما . يعرض الجدول ( 1- 8) حالة إنتاج سلعه في الشركه التي تعمل بها . يقوم العمال في المراحل المختلفه لعملية الإنتاج بإدخال سجل في الجدول للاشاره الى الزمن المتبقي لإنها السلعه .

نريد تحديد السلع التي لم تنته بعد .

الحل : Solution

ان عبارة SQL التاليه ستقوم بالعمل المطلوب . لاحظ استخدام نموذج Not In في الاستعلام الفرعي .

SELECT DISTINCT WidgeID FROM Widgets As W1 
WHERE 0 NOT IN (SELECT TimeToCompletion FROM Widgets As W2 
WHERE W2.WidgetID = W1.WidgetID)
b6da4d816d.gif
زمن الانجاز2 : Time To Completion2 دعنا نقوم بتعقيد مشكلة الانجاز قليلا . تخيل ثانية أنك تتابع حالة إنتاج السلع في الشركة . تتألف كل سلعة من أربعة أجزاء , يتم تجميع كل منها على حدة . يتضمن الجدول (9-1) عينة من البيانات .
8e0a8e86d2.gif
سنحاول هنا تعيين أو تحديد السلع التي يكون فيها الجزء الأول (Module 1 ) هو الجزء الوحيد الكامل – أي أن قيمة Time To Completion تساوي الصفر من أجل الجزء الأول , ولكن مختلفة عن الصفر من أجل الأجزاء الأخرى في السلعة . الحل : Solution لنأخذ عبارة SQL التالية :
SELECT WidgetID 
FROM Widgets AS W1 
WHERE (TimeToCompletion = 0) AND 0 NOT IN (SELECT TimeToCompletion FROM Widgets AS W2 
WHERE (W2.WidgetID=W1.WidgetID) AND (W2.ModuleID < > 1))
يحدد الاستعلام الفرعي من أجل سلعة معينة , جميع قيم Time To Completion لجميع الأجزاء باستثناء الجزء الأول . تستطيع بعد ذلك إجراء اختبار لرؤية فيما إذا كانت تلك المجموعة من أزمنة الانجاز Time To Completion تتضمن القيمة صفر . ويتضمن المثال (1-5) الكود الكامل . المثال (1-5) : مثال Time To Completion
Private Sub TimeToCompletion( ) 
Dim db As Database 
Set db = CurrentDb 
Dim qry1 As QueryDef 
Dim sSQL1 As String 
On Error Resume Next 
db.QueryDefs.Delete "temp1" 
On Error GoTo 0 
sSQL1 = "SELECT widgetID FROM widgets As W1" &_ 
" WHERE (TimeToCompletion = 0) AND" & _ 
" 0 NOT IN" & _ 
" (SELECT TimeToCompletion FROM Widgets AS W2" & _ 
"WHERE (W2.WidgetID=W1.WidgetID) AND (W2.ModuleID <> 1) )" 
Set qry1 = db.CreateQueryDef("temp1", Ssql1) 
DoCmd.OpenQuery qry1.Name 
End Sub
زمن الانجاز III مسألة الحد الأعظم والحد الأدنى : Time To Completion III a MaxMin Problem دعنا نضيف مزيداً من التعقيد على مسألة زمن الانجاز . لنفترض أن كل جزء مؤلف من عدة أقسام . وسيقوم العمال المكلفين بقسم معين بإدخال قيد في جدول قاعدة البيانات وبشكل دوري , وذلك كما هو مبين في الجدول (1-10) . ونرغب هنا بحساب زمن انجاز كل قسم , وجزء وسلعة . لاحظ أنه يمكن أن يكون هناك عدة قيود من أجل قسم معين . وزمن الانجاز اللازم لقسم معين هو الحد الأدنى للأزمنة في هذه الصفوف .
d4d3a0ceaa.gif
الحل الاول : Solution1 دعنا نعتمد طريقة تقوم على مبدأ الخطوة خطوة . وتستطيع لاحقاً عرض حل أكثر أناقة ولكنه أكثر صعوبة للفهم والقراءة . نقوم أولا بإنشاء عبارة SQL تعيد فقط صفوف الجدول لكل سلعة / جزء التي لها أصغر زمن انجاز للقسم Time To Completion . ونستطيع القيام بذلك على مرحلتين . تقوم أول عبارة SQL بتحديد حقل Time To Completion لجميع السجلات في المجموعة Widgets والتي لها قيم معينة لكل من Widget ID , Module ID , Part ID .
' Time to completion for given WidgetID/Modul*ID/PartID
sSQL1 = "SELECT TimeToCompletion FROM Widgets2 As W2" & _ 
" WHERE (W2.WidgetID = W1. WidgetID)" & _
" And (W2.ModuleID = W1.ModuleID)" & _
" And (W2.PartID = W1.PartID)"
أما عبارة SQL الثانية فتعيد جميع السجلات التي يكون فيها Time To Completion أصغر أو مساوٍ لجميع السجلات التي تمت إعادتها في عبارة SQL الأولى , أي جميع السجلات المتعلقة بقيم معينة Widget ID , Module ID , Part ID .
' Those records that have minimum time to completion for each part 
sSQL2 = "SELECT WidgetID, ModuleID, PartID," & _ 
" TimeToCompletion AS TimeToFinishPart FROM Widget2 AS W1" & _ 
" WHERE TimeToCompletion <= ALL (" &sSQL1&")"
هناك طريقة أخرى تقوم على استخدام عبارة Select متشابكة وحيدة :
sSQL2 = "SELECT WidgetID, ModuleID, PartID," & _ 
" (SELECT MIN(TimeToCompletion)" & _ 
" FROM Widgets2 as W2 WHERE" & _ 
" (W2.WidgetID = W1.WidgetID) And" & _ 
" (W2.ModuleID = W1.ModuleID) And" & _
" (W2.PartID = W1.PartID)" & _ 
" AS TimeToFinishPart" & _ 
" FROM Widgets2 AS W1
بتنفيذ هذا الاستعلام :
Set qry1=db.CreateQueryDef("temp1", sSQL2) 
DoCmd.OpenQuery qry1.Name
سنحصل على الجدول (1-11) .
6d2f4764f5.gif
وباستخدام الاستعلام التالي يصبح من السهل الحصول على زمن الانجاز لكل جزء .
' Time to finish each module 
sSQL3 = "SELECT WidgetID, ModuleID," & _ 
" Max(TimeToFinishPart) AS TimeToFinishModule FROM " & qry1.Name & _ 
" GROUP BY WidgetID, ModuleID" 
Set qry2 = db.CreateQueryDef("temp2", sSQL3) 
' Show it 
DoCmd.OpenQuery qry2.Name
يتضمن الجدول (1-12) نتائج تنفيذ هذا الاستعلام .
53678cb435.gif
أخيراً نستطيع حساب زمن الانجاز لكل سلعة :
' Time to finish each Widget 
sSQL4 = "SELECT WidgetID," & _ 
" Max(TimeToFinishModule) AS TimeToFinishWidget FROM " & _ 
qry2.Name & " GROUP BY WidgetID" 

Set qry3 = db.CreateQueryDef("temp3", sSQL4)
والنتائج مبينة في الجدول (1-13) .
8407fbd821.gif
الحل الثاني : Solution2 من الممكن الحصول على زمن الانجاز باستخدام عبارة SQL وحيدة , رغم أننا لا ننصح بذلك . قد تكون النتائج أكثر أناقة , ولكنها صعبة القراءة . سيكون لدينا مثلاً من أجل الأجزاء (Modules) :
sSQL1 = "SELECT DISTINCT WidgetID, ModuleID, " & _ 
" TimeToCompletion FROM Widgets2 AS W1" & _ 
" WHERE TimeToCompletion = " & _ 
" (SELECT MAX(TimeToCompletion) FROM Widgets2 As W2" & _ 
" WHERE TimeToCompletion = " & _ 
" (SELECT MIN(TimeToCompletion) FROM Widgets2 As W3" & _ 
" WHERE (W3.WidgetID = W2.WidgetID) " & _ 
" And (W3.ModuleID = W2.ModuleID) " & _ 
" And (W3.PartID = W2.PartID)" & _ 
" Group BY W3.WidgeetID, W3.ModuleID, W3.PartID)" & _ 
" AND (W2.WidgetID = W1.WidgetID)And(W2.ModuleID = _ 
" W1.ModuleID)" & GROUP BY W2.WidgetID, W2.ModuleID)"

إن هضم عبارة SQL هذه سيكون صعباً ويتطلب وقتاً أطول , ونرجو أنها ستجعلك تعيد النظر باستخدام مثل هذه العبارات في تطبيقاتك المستقبلية .

يتبع

رابط هذا التعليق
شارك

عامودي الى افقي : Vertical to Horizontal

تخيل ان لديك قاعدة بيانات إحصائيات شخصية مع جدولين ( الجدول 1-14 ) والجدول 1-15 )

bd87b0966b.gif

لاحظ ان للجدول (1-15) صف واحد لكل احصاء . وهكذا فإن البيانات المتعلقة بشخص معين مرتبة عاموديا . ولاحظ ايضا ان بعض البيانات ناقصة على سبيل المثال لا يوجد بيانات من اجل " زهــره " . نريد الان عرض البيانات افقيا كما في الجدول (1-16)

1b5c8eefb7.gif

الحل : Solution

تقدم لنا عبارة SQL التالية حلا لهذه المسألة :

SELECT DISTINCT Employees.EmpID, Name, 
(SELECT Value FROM EmployeesData AS T2 WHERE (T2.StatType 'Age') And (T2.EmpID = Employees.EmpID)) As Age, 
(SELECT Value FROM EmployeesData AS T2 WHERE (T2.StatType Height) And (T2.EmpID = Employees.EmpID)) As Height, 
(SELECT Value FROM EmployeesData AS T2 WHERE (T2.StatType Weight) And (T2.EmpID = Employees.EmpID)) As Weight, 
FROM (Employees INNER JOIN EmployeesData ON Employees.EmpID= EmployeesData.EmpID
لدينا هنا عدة عبارات استعلام فرعي Select ضمن عبارة Select الرئيسية . وتقوم العبارة التالية :
(Select Value From EmployeesData As T2 WHERE (T2.StatType='Age') And (T2.EmpID= Employees.EmpID)) As Age
بتحديد العمر للشخص المحدد في ظرف أو عبارة Select الرئيسية . وكلما ازداد عدد الاحصائيات تصبح عبارة SQL اكثر تعقيدا . يعرض المثال ( 1-16 ) حلا بديلا بإستخدامADO ولا يتطلب تعديل لدى ازدياد الإحصائيات . المثال ( 1-16 ) مثال : Vertical to Horizontal
Private Sub VerticalToHorizontal2( ) 
Dim db As Database 
Set db = CurrentDb 
Dim rsEmp As Recordset 
Dim rsData As Recordset 
Dim rsHor As Recordset
Set rsEmp = db.OpenRecordset("Employees") 
Set rsHor = db. OpenRecordset("EmployeesOutput") 
Do While Not rsEmp.EOF 
Set rsData = db.OpenRecordset( _ 
"SELECT * FROM EmployeesData WHERE EmpID = " & rsEmp!EmpID) 
rsHor.AddNew 
rsHor!EmpID = rsEmp!EmpID 
rsHor!Name = rsEmp!Name 
Do While Not rsData.EOF 
rsHor.Fields(rsData!StatType).Value = rsData!Value 
rsData.MoveNext 
Loop 
rsHor.Update 
rsEmp.MoveNext 
Loop 
rsEmp.Close 
rsData.Close 
rsHor.Close 
End Sub
مسألة تطابق : A Matching Problem يتضمن الجدول ( 1-17 ) مبرمجين ولغات البرمجمة التي يستخدمونها . أما الجدول ( 1-18 ) فيحدد متطلبات اللغة من اجل اعمال مختلفة . نريد هنا عرض قائمة بالاعمال والمبرمجين المؤهلين للقيام بها .
d38edd6431.gif
4b98664978.gif
الحل : Solution تقدم لنا عبارة SQL التالية أحد الحلول :
SELECT ProgrammingJobs.JobID, Programmers.Name 
FROM Programmers INNER JOIN ProgrammingJobs ON Programmers.Language = ProgrammingJobs.Language 
GROUP BY ProgrammingJobs.JobID, Programmers.Name 
HAVING Count(Programmers.Language)= 
(SELECT Count([Language]) FROM ProgrammingJobs AS pj 
WHERE pj.JobID= ProgrammingJobs.JobID)
نبدأ بربط داخلي Inner Join للجدولين بإستخدام حقل Language ومن أجل كل زوج Programmer/Job تنشئ علاقة الربط الداخلي مجموعة من السجلات كما في الشكل
JobID X      Language1-ProgrammerName Y 
JobID X      Language2-ProgrammerName Y 
JobID X      Language3-ProgrammerName Y
حيث يتطلب العمل للغة والمبرمج مهارة بتلك اللغة . نحتاج الان ان نضمن لكل زوج عمل / مبرمج بأن يكون عدد من هذه السجلات هو نفس عدد اللغات المطلوبة لذلك العمل . ويتم ذلك عن طريق تحميع السجلات وفق الازواج عمل / مبرمج ومن ثم استخدام ظرف Having الذي يقارن عدد هذه السجلات مع عدد اللغات لذلك العمل .والنتيجة هي ما يتضمنه الجدول الجدول (1-19)
32ec9f66c9.gif
تساوي المجموعات : Equality of Sets من المسائل الشائعة تعيين متى تتساوى مجموعتان أي متى يكون لهما نفس العناصر . لنأخذ الجدول ( 1-20 ) الذي يعرض خمس مجموعات وعناصرها . لاختصار المسألة وتبسيطها نقوم بترقيم المجموعات ونفترض انها تتضمن ارقاما بحد ذاتها . ونريد الحصول على قائمة بالمجموعات المتساوية .
366d5c0825.gif
الحل : Solution لهذه المسألة حل أنيق بإستخدام عبارة SQL وحيدة . بينما لا يسمح SQL بشكل عام بمقارنة مجموعتين مباشرة كما في العبارة :
(Select Members From Equality Where Set=1)= (Select Members From Equality Where Set=2)
لكنه يقبل كثل هذه العبارة اذا كانت عبارات Select تعيد قيمة وحيدة . لنأخذ الان عبارة SQL التالية :
SELECt Equality.Set, E2.Set 
FROM Equality INNER JOIN Equality AS E2 ON (Equality.Member = E2.Member) And (Equality.Set < E2.Set) 
GROUP BY Equality.Set, E2.Set 
HAVING 
((SELECT Count(Member) FROM Equality As E3 WHERE E3.Set=Equality.Set) = 
(SELECT Count(Member)FROM Equality As E3 WHERE E3.Set=E2.Set)) AND (Count(Equality.Set) = 
(SELECT Count(Member) FROM Equality As E3 WHERE E3.Set=E2.Set))
ان علاقة الربط الداخلي Inner Join هي :
(Equality.Member = E2.Member) And (Equality.Set < E2.Set)
ان الجزء الهام في هذه العبارة هو الجزء الاول . انه يفترض بأننا نريد جميع ازواج المجموعات التي لها عضو مشترك . ويمنع الجزء الثاني من العبارة اعادة ازواج متكررة . على سبيل المثال اذا تضمنت المجموعتان 1 و 2 العضو 3 فإننا لا نريد استرجاع الزوج ( 1-2 ) والزوج ( 2-1 ) . ولبيان ذلك بشكل اوضح بما ان العضو 3 مجوجود في المجموعات 1 ، 2 ، 3 ، 4 فإن السجلات المستعادة للعضو 3 هي على النحو التالي : ( 2-1 ) ( من العضو 3 ) ( 3-1 ) ( من العضو 3 ) ( 4-1 ) ( من العضو 3 ) ( 3-2 ) ( من العضو 3 ) ( 4-2 ) ( من العضو 3 ) ( 4-3 ) ( من العضو 3 ) واذا لم يكن الجزء Equality.Set < E2.Set موجودا فإننا كنا سنحصل على ( 1-1 ) و ( 2-2 ) ..... ( 4-4 ) بالاضافة الى ( 1-2 ) و ( 1-3 ) وهكذا . يمكننا الان ان نطرح السؤال التالي : كم مره سيظهر زوج مجموعة ما ؟ ان زوج المجموعة ( 2-1 ) مثلا سيظهر بعدد المرات التي يوجد فيها عناصر مشتركة بين المجموعتين . أي انه سيظهر بعدد مرات يكافئ حجم التقاطع بين المجموعتين . لذلك اذا قمنا بالتجميع حسب ازواج المجموعات ( Group By ) نستطيع فحص التقاطعات وتحديد حجم النتائج المسترجعة بإستخدام ظرف Having . ان هذا الظرف يقول بأن المجموعتين متساويتين . ولكن تتساوى المجموعتان A و B اذا كان حجمهما وتقاطعهما هو نفسه ان العبارة :
(Select Count(Member) From Equality As E3 WHERE E3.Set=Equality.Set)= 
Select Count(Member) From Equality As E3 WHERE E3.Set=E2.Set)
تقول انه من اجل زوج مجموعات معين ( Equality.Set , E2.Set ) من العبارة الرئيسية Select فإن حجم Equality.Set مساوي لحجم E2.Set . اما العبارة :
Count(Equality.Set)= (Select Count(Member) From Equality As E3 
WHERE E3.Set=E2.Set)

فتقول بأن حجم تقاطع Equality.Set و E2.Set هو نفسه ومماثل لحجم E2.Set وهذا هو المطلوب .

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

اختكم

زهـره

المصدر من كتاب : Some Common Data Manipulation Problems

رابط هذا التعليق
شارك

دروس قوية

لكن لى رجاء

بالنسبة لى وأظن أن أغلب الأعضاء يود الاحتفاظ بهذه الدروس محفوظة ، ولكن يوجد سبب فى المنتدى يمنع الحفظ بأى طريقة سواء كان Web page أو من خلال خيارات واختيار بصورة وورد

فهل نطلب ارفاق ملف بهذه الدروس لمطالعتها على مهل.

رابط هذا التعليق
شارك

نعم يا أحمد العيسى وفقك الله وأحسن إليك

استاذتنا ما قصرت

لكن بيني وبينك أنا والله معها مثل الأطرش في الزفة

بصراحة يا زهرة ما فهمت شيء

وأرجو أن تعطيني على قدر فهمي ولو أتعبتك

لكن سامحينا

هذه الأكواد التي أمامي مثل الأشباح

ماذا أصنع بها ، وأين أضعها ؟

أشعر بدوامة ، فهذا الدرس أكبر مني بكثير

أعطينا نموذجا حيا أفهم من خلاله

شعرت أن حديثك عمومي

ما أقسى شعور المعلم إذا تورط في طالب غبي مثلي

حتى الملف وجدته نفس الشرح هذا بصيغة وورد

لم أفهم شيئا يا زهرة ، وأشعر بالإحباط

أقدر جهدك وأشكره

واعلم تعبك وأدعو لك

ولكن ارفقي بنا رفق الله بك

وهاتي مشكلة محددة مع وضع محدد ومبين بأمثلة

هل أنا مزعج ؟

لا أشك ذلك

رابط هذا التعليق
شارك

الاخ الفاضل جرير الصغير

السلام عليكم ورحمة الله وبركاته

ان الدروس والشروحات السابقة هي للمتقدمين والمحترفين في عالم البرمجة والاكسيس وكل مثال يحتاج الى وقفات طويلة فهي تقوم على فكرة طرح مشكلة ما ومن ثم ايجاد الحلول لها برمجيا بواسطة الكود .

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

ختاما اشكرك على هذه الكلمات واتمنى ان اراك من كبار المبرمجين .

رابط هذا التعليق
شارك

من فضلك سجل دخول لتتمكن من التعليق

ستتمكن من اضافه تعليقات بعد التسجيل



سجل دخولك الان
  • تصفح هذا الموضوع مؤخراً   0 اعضاء متواجدين الان

    • لايوجد اعضاء مسجلون يتصفحون هذه الصفحه
×
×
  • اضف...

Important Information