۱۴۰۴/۰۶/۲۹ Nebular

ساب‌کوئری Correlated vs Non-Correlated

ساب‌کوئری‌ها به دو دسته اصلی تقسیم می‌شوند:

  1. Non-Correlated Subquery (ساب‌کوئری غیر وابسته)
  2. Correlated Subquery (ساب‌کوئری وابسته)

1️⃣ Non-Correlated Subquery (غیر وابسته)

🔹 تعریف ساده:

  • ساب‌کوئری مستقل از کوئری اصلی است.
  • فقط یکبار اجرا می‌شود و خروجی آن به کوئری اصلی داده می‌شود.

🔹 کاربردها:

  • وقتی بخواهیم یک مقدار سراسری یا Aggregate از جدول بگیریم و روی هر ردیف مقایسه کنیم.
  • معمولاً برای مقایسه با میانگین، بیشترین یا کمترین مقدار کل جدول استفاده می‌شود.

🔹 مثال:

-- پیدا کردن کارمندانی که حقوقشان بالاتر از میانگین حقوق کل هستند
SELECT Name, Salary
FROM Employees
WHERE Salary > (SELECT AVG(Salary) FROM Employees);

🔹 توضیح مثال:

  1. ساب‌کوئری (SELECT AVG(Salary) FROM Employees) → یکبار اجرا می‌شود و میانگین کل حقوق‌ها را محاسبه می‌کند.
  2. سپس هر ردیف جدول Employees بررسی می‌شود که آیا حقوق آن از میانگین بیشتر است یا نه.
  3. خروجی = لیست کارمندانی که بالاتر از میانگین حقوق دارند.

🔹 نکته تستی:

  • Aggregate مثل AVG، MAX، MIN بدون ساب‌کوئری نمی‌تواند مستقیماً در WHERE استفاده شود.

2️⃣ Correlated Subquery (وابسته)

🔹 تعریف ساده:

  • ساب‌کوئری به هر ردیف کوئری بیرونی وابسته است.
  • برای هر ردیف کوئری اصلی اجرا می‌شود.
  • معمولاً وقتی می‌خواهیم مقدار خاصی را با مقادیر مرتبط به همان ردیف مقایسه کنیم، استفاده می‌شود.

🔹 کاربردها:

  • پیدا کردن رکوردهای برتر در گروه خاص (مثل بیشترین حقوق هر دپارتمان)
  • مقایسه هر ردیف با داده‌های مرتبط خودش

🔹 مثال:

-- پیدا کردن کارمندانی که بالاترین حقوق در دپارتمان خود را دارند
SELECT Name, DepartmentID, Salary
FROM Employees E1
WHERE Salary = (SELECT MAX(Salary)
                FROM Employees E2
                WHERE E2.DepartmentID = E1.DepartmentID);

🔹 توضیح مثال:

  1. E1 → کوئری اصلی (هر ردیف جدول Employees)
  2. ساب‌کوئری: SELECT MAX(Salary) FROM Employees E2 WHERE E2.DepartmentID = E1.DepartmentID
    • برای هر ردیف E1 اجرا می‌شود و بیشترین حقوق در همان دپارتمان را محاسبه می‌کند.
  3. سپس شرط مقایسه می‌شود که حقوق ردیف اصلی با بیشترین حقوق همان دپارتمان برابر است یا نه.
  4. خروجی = فقط کارمندانی که بالاترین حقوق در دپارتمان خود دارند.

🔹 تفاوت کلیدی

ویژگیNon-CorrelatedCorrelated
وابستگی به کوئری بیرونیندارددارد
تعداد اجرایک باربرای هر ردیف کوئری اصلی اجرا می‌شود
سرعتسریعترکندتر (به دلیل اجرای متعدد)
مثالحقوق بالاتر از میانگین کلبالاترین حقوق در هر دپارتمان

🔹 مثال دیگر Correlated

  • پیدا کردن مشتریانی که بیشترین تعداد سفارش را دارند:
SELECT CustomerID, Name
FROM Customers C1
WHERE CustomerID = (
    SELECT TOP 1 CustomerID
    FROM Orders O
    WHERE O.CustomerID = C1.CustomerID
    ORDER BY COUNT(*) DESC
);
  • این هم یک Correlated Subquery است، چون ساب‌کوئری به ردیف C1 وابسته است.

🔹 نکات تستی Correlated Subquery

  1. همیشه از Alias برای جدول ساب‌کوئری و جدول اصلی استفاده کنید.
  2. کند است، مخصوصاً روی جداول بزرگ → می‌توان با JOIN جایگزین کرد.
  3. معمولاً برای محاسبه مقدار مرتبط با همان ردیف استفاده می‌شود (مثل حداکثر، حداقل، تعداد، جمع).
  4. هر شرطی که داخل ساب‌کوئری به کوئری بیرونی وابسته باشد → Correlated است.

برای همچین سوالاتی راه حل های دیگری هم وجود دارد مثلا استفاده از join


در SQL برای بیشتر سوالات مثل “مقایسه هر رکورد با Aggregate کل جدول” یا “پیدا کردن رکوردهایی با بیشترین/کمترین مقدار در گروه”، همیشه چند روش وجود دارد.

بیاییم راه‌های جایگزین برای مثال Correlated Subquery را بررسی کنیم:


1️⃣ مثال: پیدا کردن کارمندانی که بالاترین حقوق در دپارتمان خود را دارند

روش 1: Correlated Subquery

SELECT Name, DepartmentID, Salary
FROM Employees E1
WHERE Salary = (SELECT MAX(Salary)
                FROM Employees E2
                WHERE E2.DepartmentID = E1.DepartmentID);
  • ساده و مستقیم
  • ولی کند روی جدول بزرگ، چون ساب‌کوئری برای هر ردیف اجرا می‌شود.

روش 2: استفاده از JOIN با Derived Table (ساب‌کوئری در FROM)

SELECT E.Name, E.DepartmentID, E.Salary
FROM Employees E
JOIN (
    SELECT DepartmentID, MAX(Salary) AS MaxSalary
    FROM Employees
    GROUP BY DepartmentID
) AS DeptMax
ON E.DepartmentID = DeptMax.DepartmentID
   AND E.Salary = DeptMax.MaxSalary;
  • توضیح:
    1. ساب‌کوئری داخل FROM → بیشترین حقوق هر دپارتمان را محاسبه می‌کند.
    2. JOIN → به جدول اصلی متصل می‌شود و فقط کارمندانی که حقوقشان با MaxSalary برابر است، انتخاب می‌شوند.
  • مزیت: سریع‌تر از Correlated Subquery روی جدول بزرگ

روش 3: استفاده از RANK() یا ROW_NUMBER() (Window Function)

SELECT Name, DepartmentID, Salary
FROM (
    SELECT *,
           RANK() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS rnk
    FROM Employees
) AS Ranked
WHERE rnk = 1;
  • توضیح:
    • PARTITION BY DepartmentID → گروه‌بندی بر اساس دپارتمان
    • ORDER BY Salary DESC → مرتب‌سازی بر اساس حقوق
    • RANK() → به هر کارمند یک رتبه داخل گروه می‌دهد
    • شرط WHERE rnk = 1 → فقط کسانی که بالاترین حقوق دارند
  • مزیت: بسیار بهینه و قابل توسعه، حتی برای چند رتبه بالاتر یا شرط‌های پیچیده

2️⃣ مثال: مقایسه حقوق با میانگین حقوق کل

روش 1: Non-Correlated Subquery

SELECT Name, Salary
FROM Employees
WHERE Salary > (SELECT AVG(Salary) FROM Employees);
  • ساده و مستقیم

روش 2: استفاده از JOIN (تقریباً غیر رایج برای این حالت)

SELECT E.Name, E.Salary
FROM Employees E
JOIN (SELECT AVG(Salary) AS AvgSalary FROM Employees) AS AvgTbl
ON E.Salary > AvgTbl.AvgSalary;
  • همان نتیجه را می‌دهد
  • مزیت: اگر بخواهید AvgSalary را با چند جدول دیگر ترکیب کنید، JOIN مفید است

روش 3: استفاده از CTE

WITH AvgSalaryCTE AS (
    SELECT AVG(Salary) AS AvgSalary FROM Employees
)
SELECT E.Name, E.Salary
FROM Employees E, AvgSalaryCTE A
WHERE E.Salary > A.AvgSalary;
  • مزیت: واضح و قابل توسعه برای کوئری‌های پیچیده

✅ جمع‌بندی

هدفCorrelated SubqueryJOIN + Derived TableWindow Function / CTE
پیدا کردن رکوردهای با Max/Min در گروهساده ولی کند روی جدول بزرگسریع‌تر و قابل فهمبسیار بهینه، انعطاف‌پذیر
مقایسه با Aggregate کل جدولNon-Correlated SubqueryJOIN یا CTEمناسب برای کوئری‌های پیچیده یا ترکیبی

💡 نکته تستی:
در آزمون SQL معمولاً می‌توان هم جواب با ساب‌کوئری را انتخاب کرد و هم با JOIN یا Window Function. اما در جداول بزرگ و دنیای واقعی، روش JOIN یا Window Function سریع‌تر و استانداردتر است.

پیدا کردن کارمندانی که بالاترین حقوق در دپارتمان خود را دارند

بیاییم کوئری Correlated Subquery شما را به صورت کامل تریس کنیم و مرحله به مرحله اجرا را بررسی کنیم تا دقیقاً بفهمیم چه اتفاقی می‌افتد.


🏗️ کوئری اصلی

SELECT Name, DepartmentID, Salary
FROM Employees E1
WHERE Salary = (
    SELECT MAX(Salary)
    FROM Employees E2
    WHERE E2.DepartmentID = E1.DepartmentID
);

1️⃣ مرحله اول: Outer Query و Inner Query

  • Outer Query (E1) → جدول Employees را می‌گیرد و هر ردیف را بررسی می‌کند.
  • Inner Query (E2) → برای هر ردیف E1 اجرا می‌شود و بیشترین حقوق همان دپارتمان را محاسبه می‌کند.
  • بنابراین این یک Correlated Subquery است: Inner Query به ستون Outer Query وابسته است (E1.DepartmentID).

2️⃣ فرض جدول Employees نمونه

EmpIDNameDepartmentIDSalary
1Ali15000
2Sara17000
3Reza26000
4Mina26500
5Arash38000

3️⃣ مرحله به مرحله اجرا

🔹 Outer Query ردیف 1 → E1 = Ali, DepartmentID = 1, Salary = 5000

  • Inner Query برای E1 = Ali:
SELECT MAX(Salary)
FROM Employees E2
WHERE E2.DepartmentID = 1;
  • داده‌های E2 که DepartmentID = 1 هستند: Ali (5000), Sara (7000)
  • MAX(Salary) = 7000
  • شرط Outer: 5000 = 7000False → Ali حذف می‌شود

🔹 Outer Query ردیف 2 → E1 = Sara, DepartmentID = 1, Salary = 7000

  • Inner Query برای E1 = Sara:
SELECT MAX(Salary)
FROM Employees E2
WHERE E2.DepartmentID = 1;
  • MAX(Salary) = 7000
  • شرط Outer: 7000 = 7000True → Sara انتخاب می‌شود

🔹 Outer Query ردیف 3 → E1 = Reza, DepartmentID = 2, Salary = 6000

  • Inner Query برای E1 = Reza:
SELECT MAX(Salary)
FROM Employees E2
WHERE E2.DepartmentID = 2;
  • MAX(Salary) = 6500
  • شرط Outer: 6000 = 6500False → Reza حذف می‌شود

🔹 Outer Query ردیف 4 → E1 = Mina, DepartmentID = 2, Salary = 6500

  • Inner Query برای E1 = Mina:
SELECT MAX(Salary)
FROM Employees E2
WHERE E2.DepartmentID = 2;
  • MAX(Salary) = 6500
  • شرط Outer: 6500 = 6500True → Mina انتخاب می‌شود

🔹 Outer Query ردیف 5 → E1 = Arash, DepartmentID = 3, Salary = 8000

  • Inner Query برای E1 = Arash:
SELECT MAX(Salary)
FROM Employees E2
WHERE E2.DepartmentID = 3;
  • MAX(Salary) = 8000
  • شرط Outer: 8000 = 8000True → Arash انتخاب می‌شود

4️⃣ خروجی نهایی

NameDepartmentIDSalary
Sara17000
Mina26500
Arash38000
  • فقط کسانی که حقوقشان برابر با بالاترین حقوق دپارتمانشان است، باقی مانده‌اند ✅

5️⃣ نکات مهم Trace

  1. Correlated Subquery → Inner Query برای هر ردیف Outer Query اجرا می‌شود.
  2. Alias (E1, E2) ضروری است تا SQL بداند ستون‌ها به کدام جدول اشاره دارند.
  3. هر Inner Query مقدار MAX(Salary) همان دپارتمان را محاسبه می‌کند و Outer Query با آن مقایسه می‌کند.
  4. روی جدول بزرگ ممکن است کند شود → جایگزین سریع‌تر: JOIN + GROUP BY یا Window Function.


بیاییم همان مسئله را (پیدا کردن کارمندانی که بالاترین حقوق در دپارتمان خود را دارند) با JOIN + GROUP BY و Window Function حل کنیم و مرحله به مرحله تریس کنیم تا ببینیم چطور اجرا می‌شوند.


1️⃣ روش JOIN + GROUP BY

کد:

SELECT E.Name, E.DepartmentID, E.Salary
FROM Employees E
JOIN (
    SELECT DepartmentID, MAX(Salary) AS MaxSalary
    FROM Employees
    GROUP BY DepartmentID
) AS DeptMax
ON E.DepartmentID = DeptMax.DepartmentID
   AND E.Salary = DeptMax.MaxSalary;

مرحله به مرحله تریس

فرض جدول Employees:

EmpIDNameDepartmentIDSalary
1Ali15000
2Sara17000
3Reza26000
4Mina26500
5Arash38000

1️⃣ ساب‌کوئری در FROM:

SELECT DepartmentID, MAX(Salary) AS MaxSalary
FROM Employees
GROUP BY DepartmentID;
  • خروجی ساب‌کوئری (DeptMax):
DepartmentIDMaxSalary
17000
26500
38000

2️⃣ JOIN با جدول اصلی

  • شرط JOIN:
E.DepartmentID = DeptMax.DepartmentID AND E.Salary = DeptMax.MaxSalary
  • بررسی هر ردیف:
E.NameE.DepartmentIDE.SalaryDeptMax.MaxSalaryMatch?
Ali150007000No
Sara170007000Yes
Reza260006500No
Mina265006500Yes
Arash380008000Yes
  • فقط ردیف‌هایی که Match = Yes هستند، در خروجی قرار می‌گیرند.

3️⃣ خروجی نهایی:

NameDepartmentIDSalary
Sara17000
Mina26500
Arash38000

✅ مشابه Correlated Subquery، ولی سریع‌تر روی جداول بزرگ


2️⃣ روش Window Function

کد:

SELECT Name, DepartmentID, Salary
FROM (
    SELECT *,
           RANK() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS rnk
    FROM Employees
) AS Ranked
WHERE rnk = 1;

مرحله به مرحله تریس

1️⃣ بخش داخلی:

SELECT *,
       RANK() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS rnk
FROM Employees;
  • PARTITION BY DepartmentID → گروه‌بندی داخلی برای هر دپارتمان
  • ORDER BY Salary DESC → مرتب‌سازی از بیشترین حقوق به کمترین
  • RANK() → به هر ردیف رتبه در گروه می‌دهد
NameDepartmentIDSalaryrnk
Ali150002
Sara170001
Reza260002
Mina265001
Arash380001

2️⃣ بخش بیرونی:

WHERE rnk = 1
  • فقط ردیف‌هایی که رتبه 1 دارند انتخاب می‌شوند → یعنی بالاترین حقوق هر دپارتمان

3️⃣ خروجی نهایی:

NameDepartmentIDSalary
Sara17000
Mina26500
Arash38000

✅ نتیجه دقیقاً مشابه دو روش دیگر


🔹 مقایسه روش‌ها

روشتوضیحمزایامعایب
Correlated Subqueryساب‌کوئری وابسته به Outer Queryساده و مستقیمکند روی جدول بزرگ
JOIN + GROUP BYمحاسبه Max در ساب‌کوئری و JOINسریع، واضحکمی طولانی‌تر
Window Function (RANK/ROW_NUMBER)محاسبه رتبه در هر گروهبهینه، انعطاف‌پذیر، برای چند رتبه برتر مناسبنیازمند نسخه SQL Server پشتیبانی‌کننده


❓ مسئله

پیدا کردن مشتریانی که بیشتر از میانگین مجموع سفارشات همه مشتری‌ها خرید کرده‌اند

فرض کنیم دو جدول داریم:

Customers

CustomerIDName
1Ali
2Sara
3Reza

Orders

OrderIDCustomerIDAmount
1011100
1021200
1032150
1043300
1053200

1️⃣ روش حل با ساب‌کوئری (Subquery)

کد SQL:

SELECT CustomerID, Name, SUM(Amount) AS TotalAmount
FROM Orders O
JOIN Customers C ON O.CustomerID = C.CustomerID
GROUP BY CustomerID, Name
HAVING SUM(Amount) > (
    SELECT AVG(TotalAmount) 
    FROM (
        SELECT SUM(Amount) AS TotalAmount
        FROM Orders
        GROUP BY CustomerID
    ) AS SubQ
);

2️⃣ مرحله به مرحله تریس

🔹 مرحله 1: محاسبه مجموع سفارشات هر مشتری

SELECT SUM(Amount) AS TotalAmount
FROM Orders
GROUP BY CustomerID;
  • خروجی (ساب‌کوئری داخلی):
    | CustomerID | TotalAmount |
    |————|————-|
    | 1 | 300 |
    | 2 | 150 |
    | 3 | 500 |

🔹 مرحله 2: محاسبه میانگین مجموع سفارشات

SELECT AVG(TotalAmount) 
FROM (
    SELECT SUM(Amount) AS TotalAmount
    FROM Orders
    GROUP BY CustomerID
) AS SubQ;
  • محاسبه میانگین:

AVG(300,150,500)=300+150+5003=316.67AVG(300, 150, 500) = \frac{300+150+500}{3} = 316.67AVG(300,150,500)=3300+150+500​=316.67

  • پس میانگین = 316.67

🔹 مرحله 3: محاسبه مجموع سفارشات هر مشتری و مقایسه با میانگین

SELECT CustomerID, Name, SUM(Amount) AS TotalAmount
FROM Orders O
JOIN Customers C ON O.CustomerID = C.CustomerID
GROUP BY CustomerID, Name
HAVING SUM(Amount) > 316.67;
  • مجموع سفارشات مشتری‌ها:
CustomerIDNameTotalAmount
1Ali300
2Sara150
3Reza500
  • مقایسه با میانگین = 316.67
    • Ali → 300 < 316.67 → حذف
    • Sara → 150 < 316.67 → حذف
    • Reza → 500 > 316.67 → انتخاب ✅

🔹 خروجی نهایی

CustomerIDNameTotalAmount
3Reza500

3️⃣ روش جایگزین: با CTE

کد:

WITH CustomerTotals AS (
    SELECT CustomerID, Name, SUM(Amount) AS TotalAmount
    FROM Orders O
    JOIN Customers C ON O.CustomerID = C.CustomerID
    GROUP BY CustomerID, Name
)
SELECT CustomerID, Name, TotalAmount
FROM CustomerTotals
WHERE TotalAmount > (SELECT AVG(TotalAmount) FROM CustomerTotals);
  • این روش خواناتر است و ساب‌کوئری داخلی فقط یکبار نوشته می‌شود.

4️⃣ روش جایگزین: با Window Function

کد:

SELECT DISTINCT CustomerID, Name, TotalAmount
FROM (
    SELECT C.CustomerID, C.Name, SUM(O.Amount) AS TotalAmount,
           AVG(SUM(O.Amount)) OVER () AS AvgTotal
    FROM Orders O
    JOIN Customers C ON O.CustomerID = C.CustomerID
    GROUP BY C.CustomerID, C.Name
) AS T
WHERE TotalAmount > AvgTotal;
  • توضیح:
    • AVG(SUM(O.Amount)) OVER () → میانگین کل مجموع سفارشات محاسبه می‌شود (Window Function)
    • سپس هر ردیف با این میانگین مقایسه می‌شود
  • خروجی همانند روش‌های قبل است.

✅ جمع‌بندی

روشتوضیحمزایا
ساب‌کوئری تو در تو (Nested Subquery)ساده و واضحکلاسیک، آزمونی
CTEخواناتر و قابل توسعهواضح و قابل استفاده در کوئری‌های پیچیده
Window Functionسریع و حرفه‌ایمناسب جداول بزرگ و تحلیل داده‌ها

SQL Query Cheat Sheet برای حل سریع مسائل

نوع مسئلهنشانه‌ها در سؤالابزار مناسبالگوی کوئری پایه
انتخاب ساده (Filtering)«مشتریانی که در تهران هستند» یا «حقوق > 5000»WHERESELECT ... FROM Table WHERE شرط
مقایسه با یک مقدار محاسبه‌شده«کارمندانی که حقوق‌شان بالاتر از میانگین است»Subquery (غیرمرتبط)WHERE Salary > (SELECT AVG(Salary) FROM Employees)
مقایسه داخل همان جدول (Correlated)«کارمندانی که بیشترین حقوق در دپارتمان خود را دارند»Subquery (مرتبط)WHERE Salary = (SELECT MAX(Salary) FROM Employees E2 WHERE E2.DepartmentID = E1.DepartmentID)
ترکیب دو جدول«لیست مشتری‌ها با سفارش‌هایشان»INNER JOINSELECT ... FROM Customers C JOIN Orders O ON C.CustomerID = O.CustomerID
تمام داده‌های یک جدول + داده‌های مرتبط (اختیاری)«همه مشتری‌ها + حتی اگر سفارش نداشته باشند»LEFT JOIN... LEFT JOIN ... ON ... WHERE O.CustomerID IS NULL (برای پیدا کردن بدون سفارش)
خلاصه‌سازی«تعداد سفارش هر مشتری» یا «مجموع حقوق هر دپارتمان»GROUP BY + AggregatesSELECT CustomerID, COUNT(*) FROM Orders GROUP BY CustomerID
فیلتر روی نتایج خلاصه‌شده«مشتریانی که بیش از 2 سفارش دارند»HAVING... GROUP BY CustomerID HAVING COUNT(*) > 2
مقایسه چند مجموعه«مشتریانی که سفارش دارند اما فاکتور ندارند»Set Operations (EXCEPT, INTERSECT) یا NOT EXISTSSELECT ... FROM Orders EXCEPT SELECT ... FROM Invoices
یافتن یکتاها«تعداد شهرهای مختلف مشتریان»DISTINCTSELECT COUNT(DISTINCT City) FROM Customers
حذف یا تغییر داده‌ها«حذف سفارش‌های مشتری X» یا «افزایش حقوق دپارتمان Y»DELETE, UPDATEDELETE FROM Orders WHERE CustomerID = 5
UPDATE Employees SET Salary = Salary*1.1 WHERE DepartmentID = 2

📌 نکات تستی طلایی:

  1. NOT IN vs NOT EXISTS vs LEFT JOIN
    برای پیدا کردن داده‌هایی که وجود ندارند، بهتره از NOT EXISTS یا LEFT JOIN ... IS NULL استفاده کنی. چون NOT IN با NULL مشکل‌ساز میشه.
  2. HAVING فقط بعد از GROUP BY میاد، برای شرط روی توابع تجمعی.
    شرط روی ردیف‌ها باید در WHERE باشه.
  3. Correlated Subquery همیشه به ردیف بیرونی اشاره می‌کنه.
    تستی‌ها دوست دارن اینو قاطی کنن.
  4. JOIN vs Subquery
    معمولاً هر چیزی که با Subquery میشه نوشت، با JOIN هم قابل حله (ولی لزوماً بهینه نیست).

🎯 مثال سریع با این Cheat Sheet

سؤال: “مشتریانی که بیشتر از 2 سفارش دارند”

راه‌حل با GROUP BY

اگر جدول Orders داریم (هر ردیف = یک سفارش):

SELECT CustomerID, COUNT(*) AS OrderCount
FROM Orders
GROUP BY CustomerID
HAVING COUNT(*) > 2;

🔎 توضیح:

  • GROUP BY CustomerID → همه سفارش‌های هر مشتری رو کنار هم جمع می‌کنیم.
  • COUNT(*) → تعداد سفارش هر مشتری.
  • HAVING COUNT(*) > 2 → فقط مشتری‌هایی که بیشتر از ۲ سفارش دارن رو نگه می‌داریم.

2️⃣ راه‌حل با Window Function

SELECT DISTINCT CustomerID
FROM (
    SELECT CustomerID, COUNT(*) OVER(PARTITION BY CustomerID) AS OrderCount
    FROM Orders
) t
WHERE OrderCount > 2;

🔎 توضیح:

  • COUNT(*) OVER(PARTITION BY CustomerID) → تعداد سفارش‌های هر مشتری محاسبه میشه و روی همه ردیف‌هاش تکرار میشه.
  • بعد با DISTINCT یا ساب‌کوئری، فقط یک بار CustomerID رو می‌گیریم.

📌 خروجی دقیقاً مثل روش اول میشه.


🔑 نکته استادانه

  • برای این مسئله ساده، روش GROUP BY بهترین و بهینه‌ترین انتخابه.
  • ولی اگر بخوای همزمان اطلاعات دیگه از سفارش‌ها (مثلاً تاریخ آخرین سفارش، مقدار کل خرید، …) رو هم بیاری، Window Function خیلی قدرتمندتر میشه چون مجبور نیستی چند بار JOIN یا Subquery بزنی.
Accept Cookies
Accept Cookies
[your-shortcode]