Subquery: انواع، کاربردها و مثال‌ها در SQL Server

بهترین کاربردهای Subquery: از بهینه‌سازی تا مدیریت داده‌ها

Subquery، که اغلب به آن کوئری داخلی (Inner Query) یا کوئری تودرتو (Nested Query) نیز گفته می‌شود، یک دستور SELECT است که درون دستور SQL دیگری مانند SELECT، INSERT، UPDATE، DELETE یا حتی درون یک ساب‌کوئری دیگر قرار می‌گیرد. استفاده از ساب‌کوئری‌ها یک راه قدرتمند برای انجام عملیات پیچیده داده‌ای است و به حل مسائل مختلف پایگاه داده کمک می‌کند.

یک ساب‌کوئری می‌تواند برای فیلتر کردن داده‌ها، انجام محاسبات تجمعی، بررسی وجود داده‌ها و بسیاری موارد دیگر به کار رود. هدف اصلی آن بازگرداندن داده‌ها است که توسط کوئری بیرونی (Outer Query) استفاده می‌شوند.

انواع Subquery

ساب‌کوئری‌ها را می‌توان بر اساس تعداد ردیف‌هایی که بازمی‌گردانند و نحوه ارتباطشان با کوئری بیرونی به دسته‌های مختلفی تقسیم کرد:

Subquery تک‌سطری

این نوع Subquery دقیقاً یک ردیف و یک ستون را برمی‌گرداند. معمولاً با عملگرهای مقایسه‌ای مانند (=, >, =, <=, ) استفاده می‌شود.

Subquery چندسطری

این Subquery یک یا چند ردیف و یک ستون را برمی‌گرداند. با عملگرهای چندسطری مانند IN، NOT IN، ANY، ALL و EXISTS استفاده می‌شود.

Subquery همبسته (Correlated Subquery)

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

کاربردهای Subquery در SQL Server

Subquery در سناریوهای مختلفی برای مدیریت و بازیابی داده‌ها در SQL Server کاربرد دارند. در ادامه به بررسی مهم‌ترین کاربردهای ساب‌کوئری‌ها می‌پردازیم:

استفاده از Subquery در عبارت SELECT (ساب‌کوئری اسکالر)

Subquery در عبارت SELECT، که به آن ساب‌کوئری اسکالر (Scalar Subquery) نیز می‌گویند، باید همیشه یک مقدار (تک ردیف و تک ستون) را برگرداند. این روش برای نمایش یک مقدار تجمعی یا یک مقدار مرجع در کنار نتایج اصلی کوئری بسیار مفید است.

مثلاً، برای نمایش شناسه محصول، نام محصول، قیمت واحد و میانگین قیمت واحد تمام محصولات، می‌توانیم از یک ساب‌کوئری در عبارت SELECT استفاده کنیم:


SELECT
    ProductID,
    ProductName,
    ListPrice,
    (SELECT AVG(ListPrice) FROM Production.Product) AS AverageListPrice
FROM
    Production.Product;

این کوئری، اطلاعات هر محصول را به همراه میانگین قیمت لیست همه محصولات نمایش می‌دهد. ساب‌کوئری میانگین کل را محاسبه کرده و برای هر ردیف از کوئری بیرونی بازگردانده می‌شود.

استفاده از Subquery در عبارت FROM (جدول مشتق شده)

هنگامی که یک Subquery در عبارت FROM استفاده می‌شود، نتیجه آن به عنوان یک جدول موقت شناخته شده به عنوان “جدول مشتق شده” (Derived Table) عمل می‌کند. این جدول مشتق شده سپس می‌تواند با سایر جداول جوین شود یا برای فیلتر کردن داده‌ها مورد استفاده قرار گیرد.

به عنوان مثال، برای یافتن محصولات با بالاترین قیمت در هر زیردسته، ابتدا می‌توانیم یک جدول مشتق شده ایجاد کنیم که حداکثر قیمت را برای هر زیردسته محاسبه کند:


SELECT
    p.Name AS ProductName,
    p.ListPrice,
    ps.Name AS ProductSubcategoryName
FROM
    Production.Product p
INNER JOIN
    (SELECT ProductSubcategoryID, MAX(ListPrice) AS MaxSubcategoryPrice
     FROM Production.Product
     GROUP BY ProductSubcategoryID) AS SubcategoryMaxPrice
ON p.ProductSubcategoryID = SubcategoryMaxPrice.ProductSubcategoryID
AND p.ListPrice = SubcategoryMaxPrice.MaxSubcategoryPrice
INNER JOIN
    Production.ProductSubcategory ps
ON p.ProductSubcategoryID = ps.ProductSubcategoryID;

این کوئری لیستی از محصولاتی را برمی‌گرداند که قیمت آن‌ها برابر با حداکثر قیمت در زیردسته خودشان است، به همراه نام زیردسته محصول. ساب‌کوئری در FROM، حداکثر قیمت را برای هر زیردسته محاسبه می‌کند و نتایج آن به عنوان یک جدول موقت عمل می‌کند.

استفاده از Subquery در عبارت WHERE

یکی از رایج‌ترین کاربردهای Subqueryها در عبارت WHERE است که برای فیلتر کردن ردیف‌ها بر اساس نتایج حاصل از یک کوئری دیگر استفاده می‌شود. این کار به افزایش دقت فیلترینگ و کاهش حجم داده‌های پردازش شده کمک می‌کند.

Subquery تک‌سطری با عملگرهای مقایسه‌ای

برای مقایسه یک ستون با یک مقدار واحد برگشتی از Subquery:

مثلاً، برای یافتن همه سفارشاتی که مبلغ کل آن‌ها بیشتر از میانگین مبلغ کل همه سفارشات است:


SELECT
    SalesOrderID,
    OrderDate,
    TotalDue
FROM
    Sales.SalesOrderHeader
WHERE
    TotalDue > (SELECT AVG(TotalDue) FROM Sales.SalesOrderHeader);

این کوئری، همه سفارشاتی را نمایش می‌دهد که مبلغ کل آن‌ها از میانگین مبلغ کل سفارشات موجود در سیستم بیشتر است. Subquery داخلی میانگین کل مبلغ سفارشات را محاسبه می‌کند.

Subquery چندسطری با عملگر IN

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

مثلاً، برای یافتن همه محصولاتی که در سفارشات خاصی (با OrderQty بیشتر از 5) استفاده شده‌اند:


SELECT
    ProductID,
    Name
FROM
    Production.Product
WHERE
    ProductID IN (SELECT ProductID FROM Sales.SalesOrderDetail WHERE OrderQty > 5);

این کوئری، لیستی از محصولات را بازمی‌گرداند که حداقل در یک سفارش با تعداد بیشتر از 5 واحد حضور داشته‌اند. Subquery داخلی تمام شناسه‌های محصول را از جزئیات سفارش‌هایی که شرط را برآورده می‌کنند، جمع‌آوری می‌کند.

Subquery چندسطری با عملگرهای ANY / ALL

این عملگرها برای مقایسه یک ستون با هر (ANY) یا همه (ALL) مقادیر برگشتی از یک Subquery چندسطری استفاده می‌شوند.

  • ANY: اگر هر یک از مقادیر Subquery شرط را برآورده کند، درست است.

    مثلاً، برای یافتن محصولاتی که قیمت لیست آن‌ها کمتر از حداقل قیمت هر زیردسته باشد (که در واقع به معنی کوچکترین قیمت در کل است، اما برای مثال ANY):

    
    SELECT
        Name,
        ListPrice
    FROM
        Production.Product
    WHERE
        ListPrice < ANY (SELECT ListPrice FROM Production.Product WHERE ProductSubcategoryID = 1);
            

    این کوئری محصولاتی را برمی‌گرداند که قیمت لیست آن‌ها کمتر از حداقل یک محصول در زیردسته با شناسه 1 باشد. در عمل، می‌توان از MIN نیز استفاده کرد، اما این مثال کاربرد ANY را نشان می‌دهد.

  • ALL: اگر همه مقادیر Subquery شرط را برآورده کنند، درست است.

    مثلاً، برای یافتن محصولاتی که قیمت لیست آن‌ها بیشتر از حداکثر قیمت در هر زیردسته باشد (که در واقع به معنی بزرگترین قیمت در کل است، اما برای مثال ALL):

    
    SELECT
        Name,
        ListPrice
    FROM
        Production.Product
    WHERE
        ListPrice > ALL (SELECT ListPrice FROM Production.Product WHERE ProductSubcategoryID = 1);
            

    این کوئری محصولاتی را برمی‌گرداند که قیمت لیست آن‌ها بیشتر از همه محصولات در زیردسته با شناسه 1 باشد. این یعنی قیمت آن از گران‌ترین محصول آن زیردسته نیز بیشتر است.

Subquery همبسته با عملگر EXISTS

عملگر EXISTS برای بررسی وجود ردیف‌ها در نتیجه یک Subquery استفاده می‌شود. این عملگر به ویژه برای ساب‌کوئری‌های همبسته که به کوئری بیرونی وابسته هستند، کارآمد است.

مثلاً، برای یافتن مشتریانی که حداقل یک سفارش در سال 2014 داشته‌اند:


SELECT
    c.CustomerID,
    p.FirstName,
    p.LastName
FROM
    Sales.Customer c
INNER JOIN
    Person.Person p ON c.PersonID = p.BusinessEntityID
WHERE EXISTS (
    SELECT 1
    FROM Sales.SalesOrderHeader soh
    WHERE soh.CustomerID = c.CustomerID
    AND YEAR(soh.OrderDate) = 2014
);

این کوئری، لیستی از مشتریانی را برمی‌گرداند که حداقل یک سفارش در سال 2014 ثبت کرده‌اند. ساب‌کوئری EXISTS برای هر مشتری بررسی می‌کند که آیا سفارشی با شرایط مشخص شده وجود دارد یا خیر.

استفاده از Subquery در دستور INSERT

Subqueryها می‌توانند برای درج داده‌ها از یک کوئری SELECT به یک جدول دیگر استفاده شوند. این روش به جای درج مقادیر ثابت، نتایج یک کوئری را درج می‌کند.

مثلاً، برای درج اطلاعات سفارشات قدیمی به یک جدول آرشیو (با فرض وجود جدول `Sales.SalesOrderHeaderArchive`):


INSERT INTO Sales.SalesOrderHeaderArchive (
    SalesOrderID,
    RevisionNumber,
    OrderDate,
    DueDate,
    ShipDate,
    Status,
    OnlineOrderFlag,
    SalesOrderNumber,
    PurchaseOrderNumber,
    AccountNumber,
    CustomerID,
    SalesPersonID,
    TerritoryID,
    BillToAddressID,
    ShipToAddressID,
    ShipMethodID,
    CreditCardID,
    CreditCardApprovalCode,
    CurrencyRateID,
    SubTotal,
    TaxAmt,
    Freight,
    TotalDue,
    Comment,
    rowguid,
    ModifiedDate
)
SELECT
    SalesOrderID,
    RevisionNumber,
    OrderDate,
    DueDate,
    ShipDate,
    Status,
    OnlineOrderFlag,
    SalesOrderNumber,
    PurchaseOrderNumber,
    AccountNumber,
    CustomerID,
    SalesPersonID,
    TerritoryID,
    BillToAddressID,
    ShipToAddressID,
    ShipMethodID,
    CreditCardID,
    CreditCardApprovalCode,
    CurrencyRateID,
    SubTotal,
    TaxAmt,
    Freight,
    TotalDue,
    Comment,
    rowguid,
    ModifiedDate
FROM
    Sales.SalesOrderHeader
WHERE
    OrderDate < '2014-01-01';

این دستور، تمام سفارشات ثبت شده قبل از تاریخ 1 ژانویه 2014 را از جدول اصلی `Sales.SalesOrderHeader` به جدول `Sales.SalesOrderHeaderArchive` منتقل می‌کند. ساب‌کوئری `SELECT` داده‌هایی که باید درج شوند را انتخاب می‌کند.

استفاده از Subquery در دستور UPDATE

Subqueryها می‌توانند برای به روزرسانی ستون‌های یک جدول با استفاده از مقادیر برگشتی از یک کوئری دیگر استفاده شوند. این بسیار مفید است وقتی که نیاز به به روزرسانی یک ستون بر اساس داده‌های مرتبط در جداول دیگر دارید.

مثلاً، برای به روزرسانی فیلد `Comment` برای سفارشاتی که مبلغ کل آن‌ها بیشتر از یک مقدار خاص است (مثلاً 5000):


UPDATE
    Sales.SalesOrderHeader
SET
    Comment = 'High value order'
WHERE
    SalesOrderID IN (SELECT SalesOrderID FROM Sales.SalesOrderHeader WHERE TotalDue > 5000);

این کوئری، ستون `Comment` را برای تمام سفارشاتی که مبلغ کل (TotalDue) آن‌ها بیشتر از 5000 است، به ‘High value order’ تغییر می‌دهد. ساب‌کوئری `IN` شناسه‌های سفارشات واجد شرایط را شناسایی می‌کند.

استفاده از Subquery در دستور DELETE

Subqueryها می‌توانند برای حذف ردیف‌ها از یک جدول بر اساس نتایج یک کوئری دیگر استفاده شوند. این کار به حذف دقیق داده‌ها بر اساس شرایط پیچیده‌تر کمک می‌کند.

مثلاً، برای حذف سفارشات باستانی که در جدول آرشیو نیز وجود دارند (با فرض اینکه قبلاً منتقل شده‌اند):


DELETE FROM
    Sales.SalesOrderHeader
WHERE
    SalesOrderID IN (SELECT SalesOrderID FROM Sales.SalesOrderHeaderArchive);

این دستور، تمام سفارشاتی را از جدول `Sales.SalesOrderHeader` حذف می‌کند که شناسه‌های آن‌ها در جدول `Sales.SalesOrderHeaderArchive` موجود هستند. این کار پس از آرشیو کردن داده‌ها، به پاکسازی جدول اصلی کمک می‌کند. ساب‌کوئری `IN` شناسه‌های سفارشات آرشیو شده را بازمی‌گرداند.

من علی دستجردی‌ام؛ عاشق کار با دیتا، از SQL Server تا بیگ‌دیتا و هوش مصنوعی. دغدغه‌ام کشف ارزش داده‌ها و به‌اشتراک‌گذاری تجربه‌هاست. ✦ رزومه من: alidastjerdi.com ✦

عضویت
منو باخبر کن!!!
guest
نام
ایمیل

0 دیدگاه
Inline Feedbacks
دیدن تمامی کامنتها

فوتر سایت

ورود به سایت

sqlyar

هنوز عضو نیستید؟

ورود به سایت

هنوز تبت نام نکردید ؟