تدمج العبارة JOIN
البيانات من جدولين، وتعيد مجموعة مختلطة من الأعمدة من كلا الجدولين، وذلك حسب نوع الضمّ المُستخدم، ومعاييره (كيفية ربط الصفوف من كلا الجدولين).
يمكن ضمّ جدولٍ إلى نفسه، أو بأيّ جدول آخر. وإذا كانت هناك حاجة للوصول إلى معلومات من أكثر من جدولين، فيمكن استخدام الضمّ عدّة مرّات في عبارة FROM
.
الضمّ الذاتي Self Join
يمكن ضمّ جدول إلى نفسه، بحيث تتطابق الصفوف مع بعضها البعض وفق شروط معينة. في مثل هذه الحالة، يجب استخدام الكُنى (aliases) للتمييز بين العناصر المكرّرة من الجدول.
في المثال التالي، لكلّ موظّف في جدول الموظّفين Employees
، يُعاد سجلّ يحتوي الاسم الأول للموظّف، والاسم الأول لمديره. ولمّا كان المدراء هم أيضًا موظفين، فسنضمّ الجدول إلى نفسه:
SELECT e.FName AS "Employee", m.FName AS "Manager" FROM Employees e JOIN Employees m ON e.ManagerId = m.Id
سيعيد هذا الاستعلام البيانات التالية:
Employee | Manager |
---|---|
John | James |
Michael | James |
Johnathon | John |
شرح الاستعلام
يحتوي الجدول الأصلي على هذه السجلات:
Id | FName | LName | PhoneNumber | ManagerId | DepartmentId | Salary | HireDate |
---|---|---|---|---|---|---|---|
1 | James | Smith | 1234567890 | NULL | 1 | 1000 | 01-01-2002 |
2 | John | Johnson | 2468101214 | 1 | 1 | 400 | 23-03-2005 |
3 | Michael | Williams | 1357911131 | 1 | 2 | 600 | 12-05-2009 |
4 | Johnathon | Smith | 1212121212 | 2 | 1 | 500 | 24-07-2016 |
الخطوة الأولى في تنفيذ الاستعلام هي إجراء جداء ديكارتي لجميع السجلات في الجداول المستخدمة في عبارة FROM
. في حالتنا هذه، استخدمنا جدول الموظفين مرّتين، لذا سيبدو الجدول الوسيط كما يلي (أزلنا الحقول غير المستخدمة في المثال):
e.Id | e.FName | e.ManagerId | m.Id | m.FName | m.ManagerId |
---|---|---|---|---|---|
1 | James | NULL | 1 | James | NULL |
1 | James | NULL | 2 | John | 1 |
1 | James | NULL | 3 | Michael | 1 |
1 | James | NULL | 4 | Johnathon | 2 |
2 | John | 1 | 1 | James | NULL |
2 | John | 1 | 2 | John | 1 |
2 | John | 1 | 3 | Michael | 1 |
2 | John | 1 | 4 | Johnathon | 2 |
3 | Michael | 1 | 1 | James | NULL |
3 | Michael | 1 | 2 | John | 1 |
3 | Michael | 1 | 3 | Michael | 1 |
3 | Michael | 1 | 4 | Johnathon | 2 |
4 | Johnathon | 2 | 1 | James | NULL |
4 | Johnathon | 2 | 2 | John | 1 |
4 | Johnathon | 2 | 3 | Michael | 1 |
4 | Johnathon | 2 | 4 | Johnathon | 2 |
الخطوة التالية هي ترشيح السجلات، والإبقاء على السجلات التي تفي بشرط الضمّ وحسب، أي سجلات الجدول e
التي يساوي الحقل ManagerId
خاصتها الحقلَ Id
في الجدول m
:
e.Id | e.FName | e.ManagerId | m.Id | m.FName | m.ManagerId |
---|---|---|---|---|---|
2 | John | 1 | 1 | James | NULL |
3 | Michael | 1 | 1 | James | NULL |
4 | Johnathon | 2 | 2 | John | 1 |
بعد ذلك، تُقيّم كل التعبيرات المستخدمة في عبارة SELECT
لإعادة الجدول التالي:
e.FName | m.FName |
---|---|
John | James |
Michael | James |
Johnathon | John |
أخيرًا، يُستبدل اسما العمودين e.FName
و m.FName
بكُنيتيهما:
Employee | Manager |
---|---|
John | James |
Michael | James |
Johnathon | John |
الاختلاف بين الضم الداخلي والخارجي
هناك عدّة أنواع من الضمّ في SQL، وتختلف تلك الأنواع عن بعضها من حيث ما إذا كانت الصفوف التي (لا) تحقّق الشرط ستُضمّ أم لا.
هذه بعض أهمّ أنواع الضمّ: INNER JOIN
و LEFT OUTER
JOINو
RIGHT OUTER JOINو
FULL OUTER JOIN(الكلمتان المفتَاحيتان
INNERو
OUTER` اختياريتان).
يوضّح الشكل أدناه الاختلافات بين مختلف أنواع الضمّ: تمثل المنطقة الزرقاء النتائج المُعادة من عملية الضمّ، فيما تمثّل المنطقة البيضاء النتائج التي لن تعيدها عملية الضمّ.
وهذه صورة لتمثيل الضمّ المتقاطع (Join SQL)
على سبيل المثال، إليك الجدولين التاليين:
A B - - 1 3 2 4 3 5 4 6
لاحظ أنّ القيمتين (1،2) حصريتان للجدول A، أمّا القيمتان (3،4) فمُشتركتان، و القيمتان (5،6) حصريتان لـ B.
الضمّ الداخلي
يعيد الضم الداخلي تقاطع الجدولين، أي الصفوف المشترك بينهما:
select * from a INNER JOIN b on a.a = b.b; select a.*,b.* from a,b where a.a = b.b; a | b --+-- 3 | 3 4 | 4
الضم الخارجي اليساري Left outer join
يعيد الضم الخارجي اليساري جميع صفوف A، بالإضافة إلى الصفوف المشتركة مع B:
select * from a LEFT OUTER JOIN b on a.a = b.b; a | b --+----- 1 | null 2 | null 3 | 3 4 | 4
الضم الخارجي اليميني Right outer join
وبالمثل، يعيد الضمّ الخارجي اليميني كل صفوف B، بالإضافة إلى الصفوف المشتركة في A:
select * from a RIGHT OUTER JOIN b on a.a = b.b; a | b -----+---- 3 | 3 4 | 4 null | 5 null | 6
الضمّ الخارجي التام Full outer join
يعيد الضمّ الخارجي التام اتحاد A و B، أي جميع الصفوف الموجودة في A وجميع الصفوف الموجودة في B. إذا كانت هناك بيانات في A بدون بيانات مقابلة في B، فسيكون الجزء الخاص بـ B معدوما (null). والعكس صحيح.
select * from a FULL OUTER JOIN b on a.a = b.b; a | b -----+----- 1 | null 2 | null 3 | 3 4 | 4 null | 6 null | 5
اصطلاحات الضمّ JOIN Terminology
لنفترض أنّ لدينا جدولين A و B، وأنّ بعض صفوفِهما متطابقة (وفق شرط JOIN):
هناك عدّة أنواع مختلفة من الضمّ يمكن استخدامها لأجل تضمين أو استبعاد الصفوف التي (لا) تحقق شرط الضمّ في كلا الجانبين.
تستخدم الأمثلة أدناه البيانات التالية:
CREATE TABLE A ( X varchar(255) PRIMARY KEY ); CREATE TABLE B ( Y varchar(255) PRIMARY KEY ); INSERT INTO A VALUES ('Amy'), ('John'), ('Lisa'), ('Marco'), ('Phil'); INSERT INTO B VALUES ('Lisa'), ('Marco'), ('Phil'), ('Tim'), ('Vincent');
الضمّ الداخلي Inner Join
يجمع الضمّ الداخلي بين الصفوف اليسرى واليمنى المتطابقة.
SELECT * FROM A JOIN B ON X = Y; X Y ------ ----- Lisa Lisa Marco Marco Phil Phil
الضم الخارجي اليساري Left outer join
يُسمّى اختصارًا الضمّ اليساري. ويجمع بين الصفوف اليسرى واليمنى التي تحقّق الشرط، مع تضمين الصفوف اليسرى التي لا تحقّق الشرط.
SELECT * FROM A LEFT JOIN B ON X = Y; X Y ----- ----- Amy NULL John NULL Lisa Lisa Marco Marco Phil Phil
الضم الخارجي اليميني Right outer join
يُسمّى اختصارًا الضمّ الأيمن. ويجمع بين الصفوف اليسرى واليمنى التي تحقّق الشرط، مع تضمين الصفوف اليمنى التي لا تحقّق الشرط.
SELECT * FROM A RIGHT JOIN B ON X = Y; X Y ----- ------- Lisa Lisa Marco Marco Phil Phil NULL Tim NULL Vincent
الضمّ الخارجي التام Full outer join
يُسمّى اختصارًا الضمّ التام. وهو اتحاد لعمليتي الضم اليساري واليميني.
SELECT * FROM A FULL JOIN B ON X = Y; X Y ----- ------- Amy NULL John NULL Lisa Lisa Marco Marco Phil Phil NULL Tim NULL Vincent
الضمّ شبه اليساري
يضمّ هذا النوع الصفوفَ اليُسرى التي تتطابق مع الصفوف اليمنى.
SELECT * FROM A WHERE X IN (SELECT Y FROM B); X ----- Lisa Marco Phil
الضمّ شبه اليميني Right Semi Join
يضمّ هذا النوع الصفوف اليمنى التي تطابق الصفوف اليسرى.
SELECT * FROM B WHERE Y IN (SELECT X FROM A); Y ----- Lisa Marco Phil
لا توجد صياغة للعبارة IN مُخصّصة للضمّ شبه اليساري أو شبه اليميني - كلّ ما عليك فعله هو تبديل مواضع الجدول في SQL.
الضمّ شبه اليساري المعكوس Left Anti Semi Join
يُضمِّن هذا النوع الصفوفَ اليُسرى التي لا تتطابق مع الصفوف اليمنى.
SELECT * FROM A WHERE X NOT IN (SELECT Y FROM B); X ---- Amy John
تنبيه: استخدام NOT IN
في الأعمدة التي تقبل القيم المعدومة NULL
قد يسبّب بعض المشاكل (المزيد من التفاصيل هنا).
الضمّ شبه اليميني المعكوس Right Anti Semi Join
يُضمِّن هذا النوع الصفوف اليمنى التي لا تطابق الصفوف اليسرى.
SELECT * FROM B WHERE Y NOT IN (SELECT X FROM A); Y ------- Tim Vincent
لا توجد صياغة للعبارة IN مخصّصة للضمّ شبه اليساري أو شبه اليميني المعكوس - كلّ ما عليك فعله هو تبديل مواضع الجدول في SQL.
الضم المتقاطع Cross Join
يُجري هذا النوع من الضمّ جداءً ديكارتيًا (Cartesian product) بين الصوف اليسرى والصفوف اليمنى.
SELECT * FROM A CROSS JOIN B; X Y ----- ------- Amy Lisa John Lisa Lisa Lisa Marco Lisa Phil Lisa Amy Marco John Marco Lisa Marco Marco Marco Phil Marco Amy Phil John Phil Lisa Phil Marco Phil Phil Phil Amy Tim John Tim Lisa Tim Marco Tim Phil Tim Amy Vincent John Vincent Lisa Vincent Marco Vincent Phil Vincent
يكافئ الضمّ المتقاطع ضمًّا داخليًا ذا شرط يتحقّق دائمًا، لذا سيعيد الاستعلام التالي النتيجة نفسها:
SELECT * FROM A JOIN B ON 1 = 1;
الضمّ الذاتي Self-Join
يشير هذا النوع من الضم إلى ضمّ الجدول إلى نفسه. يمكن أن تكون عملية الضمّ الذاتي من أيّ نوع من أنواع الضمّ التي ناقشناها أعلاه. على سبيل المثال، هذا ضمّ ذاتي داخلي ( inner self-join):
SELECT * FROM A A1 JOIN A A2 ON LEN(A1.X) < LEN(A2.X); X X ---- ----- Amy John Amy Lisa Amy Marco John Marco Lisa Marco Phil Marco Amy Phil
الضمّ الخارجي اليساري Left Outer Join
يضمن الضمّ الخارجي اليساري (المعروف أيضًا باسم الضمّ اليساري أو الضمّ الخارجي) تمثيل جميع صفوف الجدول الأيسر؛ وفي حال عدم وجود صفّ مطابق في الجدول الأيمن، فسيُعطى الحقل المقابل القيمةَ NULL
.
سيختار المثال التالي جميع الأقسام (departments) والأسماء الأولى للموظّفين الذين يعملون في تلك الأقسام. وستُعاد الأقسام التي لا تحتوي على أيّ موظفين، مع إعطاء اسم الموظف المقابل لها القيمة NULL
:
SELECT Departments.Name, Employees.FName FROM Departments LEFT OUTER JOIN Employees ON Departments.Id = Employees.DepartmentId
سنحصل على الخرج التالي:
Departments.Name | Employees.FName |
---|---|
HR | James |
HR | John |
HR | Johnathon |
Sales | Michael |
Tech | NULL |
شرح الاستعلام
يوجد جدولان في عبارة FROM
، وهما:
Id | FName | LName | PhoneNumber | ManagerId | DepartmentId | Salary | HireDate |
---|---|---|---|---|---|---|---|
1 | James | Smith | 1234567890 | NULL | 1 | 1000 | 01-01-2002 |
2 | John | Johnson | 2468101214 | 1 | 1 | 400 | 23-03-2005 |
3 | Michael | Williams | 1357911131 | 1 | 2 | 600 | 12-05-2009 |
4 | Johnathon | Smith | 1212121212 | 2 | 1 | 500 | 24-07-2016 |
وهذا هو الجدول الثاني:
Id | Name |
---|---|
1 | HR |
2 | Sales |
3 | Tech |
في المرحلة الأولى، يُنشأ جداء ديكارتي للجدولين، وينتج عنه جدول وسيط.
يُغلَّظُ خطّ السجلات التي تفي بشرط الضمّ (والذي هو في هذه الحالة: Departments.Id = Employees.DepartmentId
)؛ وتُمرَّر إلى المرحلة التالية من الاستعلام.
لمّا كان هذا الضّمّ ضمًّا خارجيًا يساريًا (LEFT OUTER JOIN
)، فستُعاد جميع السجلّات الموجودة في الجانب الأيسر من الضمّ (أي الأقسام Departments)، في حين تُعطى السجلات الموجودة على الجانب الأيمن القيمة المعدومة (NULL
) في حال لم تُطابق شرط الضمّ.
Id | Name | Id | FName | LName | PhoneNumber | ManagerId | DepartmentId | Salary | HireDate |
---|---|---|---|---|---|---|---|---|---|
1 | HR | 1 | James | Smitd | 1234567890 | NULL | 1 | 1000 | 01-01-2002 |
1 | HR | 2 | John | Johnson | 2468101214 | 1 | 1 | 400 | 23-03-2005 |
1 | HR | 3 | Michael | Williams | 1357911131 | 1 | 2 | 600 | 12-05-2009 |
1 | HR | 4 | Johnatdon | Smitd | 1212121212 | 2 | 1 | 500 | 24-07-2016 |
2 | Sales | 1 | James | Smith | 1234567890 | NULL | 1 | 1000 | 01-01-2002 |
2 | Sales | 2 | John | Johnson | 2468101214 | 1 | 1 | 400 | 23-03-2005 |
2 | Sales | 3 | Michael | Williams | 1357911131 | 1 | 2 | 600 | 12-05-2009 |
2 | Sales | 4 | Johnathon | Smith | 1212121212 | 2 | 1 | 500 | 24-07-2016 |
3 | Tech | 1 | James | Smith | 1234567890 | NULL | 1 | 1000 | 01-01-2002 |
3 | Tech | 2 | John | Johnson | 2468101214 | 1 | 1 | 400 | 23-03-2005 |
3 | Tech | 3 | Michael | Williams | 1357911131 | 1 | 2 | 600 | 12-05-2009 |
3 | Tech | 4 | Johnathon | Smith | 1212121212 | 2 | 1 | 500 | 24-07-2016 |
بعد ذلك، تُقيّم كل التعبيرات المستخدَمة في عبارة SELECT
لإعادة الجدول التالي:
Departments.Name | Employees.FName |
---|---|
HR | James |
HR | John |
Sales | Richard |
Tech | NULL |
الضمّ الضمني Implicit Join
يمكن أيضًا إجراء عملية الضمّ على عدّة جداول، حيث توضع في عبارة from
مفصولة بالفاصلة ,
، مع تحديد العلاقة بينها في العبارة where
. تسمى هذه التقنية "الضمّ الضمني" - Implicit Join - (لأنها لا تحتوي فعليًا العبارةَ join
).
تدعم جميع أنظمة معالجة قواعد البيانات (RDBMSs) هذه التقنية، ولكن ينصح بتجنّب استخدامها للأسباب التالية:
- قد تتداخل صياغة الضمّ الضمني مع صياغة الضمّ المتقاطع (cross join)، وهو ما قد يؤدي إلى إعادة نتائج غير صحيحة، خاصةً إذا كان الاستعلام يحتوي الكثير من عمليات الضمّ.
-
إذا كنت تنوي استخدام الضم المتقاطع، فلن يكون ذلك واضحًا من الصياغة (اكتب
CROSS JOIN
بدلاً من ذلك)، ومن المحتمل أن يعدّلها شخص ما أثناء صيانة الشيفرة دون أن ينتبه.
سيختار المثال التالي أسماء الموظفين الأولى وكذلك أسماء الأقسام التي يعملون فيها:
SELECT e.FName, d.Name FROM Employee e, Departments d WHERE e.DeptartmentId = d.Id
سنحصل على الخرج التالي:
e.FName | d.Name |
---|---|
James | HR |
John | HR |
Richard | Sales |
الضم المتقاطع CROSS JOIN
يُجري الضمّ المتقاطع جداءً ديكارتيًا (Cartesian product) على جدولين (الجداء الديكارتي هو عملية تُجمِّع كلّ صفّ من الجدول الأول مع كل صفّ من الجدول الثاني).
على سبيل المثال، إذا كان كلّ من الجدولين TABLEA
و TABLEB
يحتويان 20 صفًا، فستتألّف النتيجة المُعادة من 20*20 = 400
صفًّا.
إليك المثال التالي:
SELECT d.Name, e.FName FROM Departments d CROSS JOIN Employees e;
سنحصل على الخرج التالي:
d.Name | e.FName |
---|---|
HR | James |
HR | John |
HR | Michael |
HR | Johnathon |
Sales | James |
Sales | John |
Sales | Michael |
Sales | Johnathon |
Tech | James |
Tech | John |
Tech | Michael |
Tech | Johnathon |
يوصى بكتابة CROSS JOIN
بشكل صريح إن أردت إجراء ضمّ ديكارتي دفعًا للُّبس.
التطبيق المتقاطع و الضم الحرفي CROSS APPLY & LATERAL JOIN
هناك نوع خاص من الضمّ يُسمّى الضمّ الحرفي LATERAL JOIN (أضيف حديثًا إلى الإصدار 9.3 وما بعده من PostgreSQL)، والذي يُعرف أيضًا باسم التطبيق المتقاطع CROSS APPLY أو التطبيق الخارجي OUTER APPLY في كلّ من SQL Server و Oracle.
الفكرة الأساسية التي ينبني عليها هذا النوع من الضمّ هي أنه سيتم تطبيق دالة (أو استعلام فرعي مضمّن - inline subquery) على كل الصفوف المضمومة.
يتيح هذا التحكم في عملية الضمّ، مثلًا يمكنك الاكتفاء بضمّ أوّل مُدخل يحقّق شرط الضمّ (matching entry) في الجدول الآخر.
يكمن الاختلاف بين الضمّ العادي والضمّ الحرفي في حقيقة أنّه يمكنك استخدام عمود سبق أن ضممته في الاستعلام الفرعي (subquery) الذي طبّقته تقاطعيًا (CROSS APPLY).
هذه صياغة الضم الحرفي.
- PostgreSQL 9.3 والإصدارات الأحدث:
left | right | inner JOIN LATERAL
- SQL Server
CROSS | OUTER APPLY
INNER JOIN LATERAL
و CROSS APPLY
متكافئتان، وكذلك LEFT JOIN LATERAL
و OUTER APPLY
إليك المثال التالي (الإصدار 9.3 وما بعده من PostgreSQL):
SELECT * FROM T_Contacts --LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1 --WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989 LEFT JOIN LATERAL ( SELECT --MAP_CTCOU_UID MAP_CTCOU_CT_UID ,MAP_CTCOU_COU_UID ,MAP_CTCOU_DateFrom ,MAP_CTCOU_DateTo FROM T_MAP_Contacts_Ref_OrganisationalUnit WHERE MAP_CTCOU_SoftDeleteStatus = 1 AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID /* AND ( (__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo) AND (__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom) ) */ ORDER BY MAP_CTCOU_DateFrom LIMIT 1 ) AS FirstOE
وهذا مثال يخصّ SQL-Server:
SELECT * FROM T_Contacts --LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1 --WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989 -- CROSS APPLY -- = INNER JOIN OUTER APPLY -- = LEFT JOIN ( SELECT TOP 1 --MAP_CTCOU_UID MAP_CTCOU_CT_UID ,MAP_CTCOU_COU_UID ,MAP_CTCOU_DateFrom ,MAP_CTCOU_DateTo FROM T_MAP_Contacts_Ref_OrganisationalUnit WHERE MAP_CTCOU_SoftDeleteStatus = 1 AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID /* AND ( (@in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo) AND (@in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom) ) */ ORDER BY MAP_CTCOU_DateFrom ) AS FirstOE
الضم التام FULL JOIN
هناك نوع آخر من الضمّ أقل شهرة من غيره، وهو الضمّ التام FULL JOIN
(ملاحظة: لا تدعم MySQL الضمّ التام)
يعيد الضمّ التام الخارجي FULL OUTER JOIN جميع صفوف الجدول الأيسر، وكذلك جميع صفوف الجدول الأيمن.
ستُدرج صفوف الجدول الأيسر التي ليس لها مُطابِقَات مقابلة في الجدول الأيمن، وكذلك في الحالة المعكوسة.
إليك المثال التالي:
SELECT * FROM Table1 FULL JOIN Table2 ON 1 = 2
وهذا مثال آخر:
SELECT COALESCE(T_Budget.Year, tYear.Year) AS RPT_BudgetInYear ,COALESCE(T_Budget.Value, 0.0) AS RPT_Value FROM T_Budget FULL JOIN tfu_RPT_All_CreateYearInterval(@budget_year_from, @budget_year_to) AS tYear ON tYear.Year = T_Budget.Year
إن كنت تستخدم عمليات الحذف اللينة soft-deletes (والتي لا تحذف البيانات بشكل نهائي)، فسيتعيّن عليك التحقق من حالة الحذف الليّن مرة أخرى في عبارة WHERE
(لأنّ سلوك الضمّ التام - FULL JOIN - يتصرف بشكل يشبه الاتحاد UNION)؛
عند إجراء الضمّ التام، سيتعيّن عليك عادةً السماح بـاستخدام القيمة المعدومة NULL
في عبارة WHERE
؛ وفي حال نسيت ذلك، فسيتصرّف الضمّ كما لو كان ضمًّا داخليًا (INNER join)، وهو ما لا تريده عند إجراء الضمّ التام.
إليك المثال التالي:
SELECT T_AccountPlan.AP_UID ,T_AccountPlan.AP_Code ,T_AccountPlan.AP_Lang_EN ,T_BudgetPositions.BUP_Budget ,T_BudgetPositions.BUP_UID ,T_BudgetPositions.BUP_Jahr FROM T_BudgetPositions FULL JOIN T_AccountPlan ON T_AccountPlan.AP_UID = T_BudgetPositions.BUP_AP_UID AND T_AccountPlan.AP_SoftDeleteStatus = 1 WHERE (1=1) AND (T_BudgetPositions.BUP_SoftDeleteStatus = 1 OR T_BudgetPositions.BUP_SoftDeleteStatus IS NULL) AND (T_AccountPlan.AP_SoftDeleteStatus = 1 OR T_AccountPlan.AP_SoftDeleteStatus IS NULL)
الضم العودي Recursive JOIN
يُستخدم الضمّ العودي عادة للحصول على بيانات من نوع أب-ابن (parent-child data). في SQL، تُقدّم عمليات الضمّ العودية باستخدام تعبيرات الجدول العادية كما يوضّح المثال التالي:
WITH RECURSIVE MyDescendants AS ( SELECT Name FROM People WHERE Name = 'John Doe' UNION ALL SELECT People.Name FROM People JOIN MyDescendants ON People.Name = MyDescendants.Parent ) SELECT * FROM MyDescendants;
الضم الداخلي الصريح
يستعلم الضمّ الأولي - basic join (يُسمّى أيضًا الضمّ الداخلي - inner join) عن البيانات من جدولين، حيث تُحدَّد العلاقة بينهما في عبارة join
.
يستعلم المثال التالي عن أسماء الموظفين (FName
) من جدول الموظفين Employees
، وأسماء الأقسام التي يعملون فيها (Name
) من جدول الأقسام Departments
:
SELECT Employees.FName, Departments.Name FROM Employees JOIN Departments ON Employees.DepartmentId = Departments.Id
سنحصل على الخرج التالي:
Employees.FName | Departments.Name |
---|---|
James | HR |
John | HR |
Richard | Sales |
الضم في استعلام فرعي Joining on a Subquery
غالبًا ما يُستخدم الضمّ في الاستعلامات الفرعية (subquery) للحصول على بيانات مُجمّعة (aggregate data) من جدول يحتوي التفاصيل (الجدول الإبن) وعرضها جنبًا إلى جنب مع السجلات من الجدول الأصلي (الجدول الأب).
على سبيل المثال، قد ترغب في الحصول على عدد السجلات الفرعية (child records)، أو متوسط قيم عمود معيّن في السجلات الفرعية، أو الصف ذو القيمة الأكبر أو الأصغر.
يستخدم هذا المثال الكُنى (لتسهيل قراءة الاستعلامات التي تشمل عدّة جداول)، يعطي المثال فكرة عامّة عن كيفية صياغة عمليات ضمّ الاستعلامات الفرعية. إذ يعيد جميع صفوف الجدول الأصلي "Purchase Orders"، مع إعادة الصف الأول وحسب لكل سجلّ أصلي (parent record) من الجدول الفرعي PurchaseOrderLineItems.
SELECT po.Id, po.PODate, po.VendorName, po.Status, item.ItemNo, item.Description, item.Cost, item.Price FROM PurchaseOrders po LEFT JOIN ( SELECT l.PurchaseOrderId, l.ItemNo, l.Description, l.Cost, l.Price, Min(l.id) as Id FROM PurchaseOrderLineItems l GROUP BY l.PurchaseOrderId, l.ItemNo, l.Description, l.Cost, l.Price ) AS item ON item.PurchaseOrderId = po.Id
ترجمة -وبتصرّف- للفصل Chapter 18: JOIN من الكتاب SQL Notes for Professionals
اقرأ أيضًا:
- المقال التالي: تحديث الجداول في SQL
- المقال السابق: البحث والتنقيب والترشيح في SQL
- النسخة العربية الكاملة لكتاب ملاحظات للعاملين بلغة SQL 1.0.0
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.