الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

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

مغلق
بدأه zahrah في 26 مايو 2005 · 8 رد · 6,068 مشاهدة · في قواعد بيانات Microsoft Access
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

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

اعزائي الكرام

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

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

قبل البدء, دعونا نشير إلي أن الكثير من الحلول المقترحة يعتمد على استخدام الاستعلامات الفرعيه .

يسمح 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) الذي يتضمن المدد الزمنيه ( محسوبة بالساعة ) لعدة أحداث .

11.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 ) الى مجالات زمنية ، المشكلة هي تعيين من أجل كل ساعة من اليوم ، عدد المجالات التي تتضمن هذه الساعة .

12.gif

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

13.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

يتبع

1
#2

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

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

14.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) من اجل نتائج هذا الإستعلام .

15.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)

16.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) الحضور المسجل مع ارقام الغرف الموافقه .

17.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

يتبع

1
#3

زمن الإنجاز : 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)

18.gif

زمن الانجاز2 : Time To Completion2

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

19.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) .

ونرغب هنا بحساب زمن انجاز كل قسم , وجزء وسلعة . لاحظ أنه يمكن أن يكون هناك عدة قيود من أجل قسم معين . وزمن الانجاز اللازم لقسم معين هو الحد الأدنى للأزمنة في هذه الصفوف .

20.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) .

21.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) نتائج تنفيذ هذا الاستعلام .

22.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) .

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

يتبع

1
#4

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

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

24.gif

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

25.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 ) فيحدد متطلبات اللغة من اجل اعمال مختلفة . نريد هنا عرض قائمة بالاعمال والمبرمجين المؤهلين للقيام بها .

26.gif

27.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)

28.gif

تساوي المجموعات : Equality of Sets

من المسائل الشائعة تعيين متى تتساوى مجموعتان أي متى يكون لهما نفس العناصر . لنأخذ الجدول ( 1-20 ) الذي يعرض خمس مجموعات وعناصرها . لاختصار المسألة وتبسيطها نقوم بترقيم المجموعات ونفترض انها تتضمن ارقاما بحد ذاتها . ونريد الحصول على قائمة بالمجموعات المتساوية .

29.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

#5

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

أختي // زهـــــــــــــــــــــــــــــــــــــــــــــــــــــــــرة

تحية طيبة و بعد ...

و الله لا أستطيع أن أقول إلا ....

زادك الله من علمه .. و وفقك الله و جميع رواد هذا المنتدى الكريم إلى كل ما فيه الخير

بصراحة مواضيعك و مشاركاتك أكثر من مميزة و هامة جداً جداً ..

و يعلم الله أنني و باقي زملائي من رواد هذا المنتدى المعطاء

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

جعل الله ذلك في موازين أعمالك ( اللهم آمين ) و إلى الأمام

و حقاً كما كتبتي في توقيعك :

((( خير الناس أنفعهم للناس ))

و شكراً ...

سنمبس

سبحان الله و بحمده ، سبحان الله العظيم

#6

جزاك الله خيراً عن كل من سيستفيد من موضوعك المهم و لمن سيراجع معلوماته

جزاك الله خيراً

سبحان الله و بحمده سبحان الله العظيم

أبو كريم و مازن و نور

vb.net - c# - asp.net - oracle - sql server

manager@tm-center.com

00966508268649

#7

بارك فيك !!!

أستاذه / زهره

عمل متعوب عليه ،،، بالتوفيق

عبدالمانع

إنما الأمم الأخلاق ما بقيت ××× فإن هم ذهبت أخلاقهم ذهبوا ...
#8

انت فوق الابداع والله يوفقك في الدين والدنيا

#9

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

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

تحياتي

من مواضيعي

برنامج للكفالات والأقساط

برنامج لإدارة مؤسسة نقل الطالبات

بـــــرنامج الصراف الآلي

_______________________

سبحان الله وبحمده سبحان الله العظيم

هذا الموضوع مغلق.

مواضيع مشابهة