آموزش

تفاوت INNER JOIN و LEFT JOIN (و گیری که COUNT با آن می‌خورید)

یک کوئری که با INNER JOIN درست کار می‌کند، می‌تواند دقیقاً همان کاربرهایی را که دنبالشان هستید حذف کند — بدون اینکه خطایی بدهد.

تحریریه‌ی دینا کد
نویسنده
۴ دقیقه مطالعه۰ بازدید
اشتراک گذاری۰

جواب کوتاه: INNER JOIN فقط ردیف‌هایی را برمی‌گرداند که در هر دو جدول طرف دارند؛ LEFT JOIN همه‌ی ردیف‌های جدول چپ را نگه می‌دارد، حتی اگر طرف مقابل چیزی نداشته باشد. اگر دنبال «کاربرانی که سفارش نداشتند» هستید، INNER JOIN همان‌هایی را که می‌خواهید ببینید، بی‌سروصدا حذف می‌کند.

یک مثال با داده‌ی واقعی

سه کاربر داریم؛ یکی‌شان (مریم) هیچ سفارشی نداده:

sqlSELECT users.name, orders.amount
FROM users INNER JOIN orders ON orders.user_id = users.id;
name  amount
----  ------
سارا  150000
سارا  90000
رضا   300000

مریم اصلاً در خروجی نیست — نه خطا، نه هشدار، فقط یک ردیف که هیچ‌وقت وجود نداشت.

LEFT JOIN همان کاربر را با NULL نگه می‌دارد

sqlSELECT users.name, orders.amount
FROM users LEFT JOIN orders ON orders.user_id = users.id;
name  amount
----  ------
سارا  90000
سارا  150000
رضا   300000
مریم  NULL

حالا مریم هست، با amount خالی. این خالی‌بودن، دقیقاً همان اطلاعاتی است که در نسخه‌ی INNER JOIN گم شده بود.

پیدا کردن «چه کسی هیچ‌کدام را ندارد»

sqlSELECT users.name
FROM users LEFT JOIN orders ON orders.user_id = users.id
WHERE orders.id IS NULL;

این الگو — LEFT JOIN به‌علاوه‌ی WHERE ... IS NULL روی کلید جدول راست — تنها راه استاندارد برای «چیزی که وجود ندارد» است. با INNER JOIN اصلاً نمی‌شود این سؤال را پرسید، چون ردیفی که وجود ندارد را نمی‌شود فیلتر کرد.

گیری که COUNT با آن می‌خورد

sqlSELECT users.name, COUNT(*) AS order_count
FROM users LEFT JOIN orders ON orders.user_id = users.id
GROUP BY users.id;
name  order_count
----  -----------
سارا  2
رضا   1
مریم  1        -- غلط! مریم صفر سفارش دارد

LEFT JOIN برای مریم یک ردیف با NULL می‌سازد، و COUNT(*) همان یک ردیف را می‌شمارد — نتیجه می‌شود ۱، نه ۰. راه درست، شمردن ستونی از جدول راست است، نه *:

sqlSELECT users.name, COUNT(orders.id) AS order_count
FROM users LEFT JOIN orders ON orders.user_id = users.id
GROUP BY users.id;
-- مریم: 0

COUNT(orders.id) مقدارهای NULL را نمی‌شمارد، پس مریم درست صفر می‌شود. این یک‌کلمه فرق — * به‌جای orders.id — دلیل کلاسیک گزارش‌های اشتباه است.

یک گیر دیگر: شرط در WHERE، LEFT JOIN را دوباره INNER می‌کند

sqlSELECT users.name, orders.amount
FROM users LEFT JOIN orders ON orders.user_id = users.id
WHERE orders.amount > 100000;
name  amount
----  ------
سارا  150000
رضا   300000

مریم دوباره غیب شد — چون NULL > 100000 نه true است نه false، نامعلوم است، و WHERE هر ردیفی را که شرطش نامعلوم باشد حذف می‌کند. LEFT JOIN نوشته بودید، ولی نتیجه دقیقاً همان INNER JOIN شد. راه‌حل، جابه‌جا کردن شرط از WHERE به ON است:

sqlSELECT users.name, orders.amount
FROM users LEFT JOIN orders ON orders.user_id = users.id AND orders.amount > 100000;

حالا مریم با NULL می‌ماند، چون فیلتر قبل از پیوستن جدول‌ها اعمال شده، نه بعدش. قانون کلی: هر شرطی که فقط روی ستون‌های جدول راست باشد، در ON بنویسید — نه WHERE — وگرنه LEFT JOIN عملاً بی‌معنی می‌شود.

اگر می‌خواهید SQL را از صفر و با کوئری‌های واقعی یاد بگیرید، دوره‌ی SQL دقیقاً همین گیرهای رایج را تمرین می‌کند.

تحریریه‌ی دینا کد
نویسنده

نظرها

اولین نفری باشید که نظر می‌دهد
برای ثبت نظر وارد شوید

سؤالتان را بپرسید یا تجربه‌تان را بنویسید — نویسنده‌ی مطلب پاسخ می‌دهد.

مطالب مرتبط

از همین دسته
آموزش

چرا Rust به‌جای segfault، خطای کامپایل می‌دهد

در C یا ++C یک اشاره‌گر نامعتبر معمولاً ماه‌ها بعد، در پروداکشن، خودش را نشان می‌دهد. در Rust همان اشتباه، همان لحظه‌ی کامپایل متوقفت می‌کند.

تحریریه‌ی دینا کد۵ دقیقه
تفاوت INNER JOIN و LEFT JOIN در SQL — دینا کد