راهنمای جامع Scroll Cursor در SQL Server: پیمایش دقیق و انعطافپذیر دادهها
در SQL Server، نشانگرها (Cursors) ابزاری هستند که امکان پردازش سطر به سطر نتایج یک مجموعه داده را فراهم میآورند. زمانی که یک عبارت SELECT چندین سطر را برمیگرداند، این سطور به عنوان یک مجموعه نتیجه واحد مدیریت میشوند. اما گاهی اوقات نیاز است که هر سطر به صورت جداگانه پردازش شود، که اینجاست نشانگرها وارد عمل میشوند. نشانگرها به کاربر اجازه میدهند تا به یک سطر خاص در مجموعه نتایج اشاره کرده و عملیات مورد نظر را روی آن انجام دهد.
SQL Server چندین نوع نشانگر را پشتیبانی میکند که هر کدام ویژگیها و کاربردهای خاص خود را دارند:
* **STATIC (استاتیک):** این نوع نشانگر یک کپی موقتی از مجموعه داده را در TempDB ایجاد میکند. هرگونه تغییر در دادههای اصلی پس از باز شدن نشانگر در این کپی منعکس نمیشود. STATIC Cursors همیشه برای پیمایش به جلو و عقب در دسترس هستند.
* **DYNAMIC (دینامیک):** این نشانگر دقیقاً عکس STATIC عمل میکند. تمامی تغییرات ایجاد شده در دادههای اصلی، چه توسط خود نشانگر و چه توسط سایر اتصالات، در مجموعه نتایج DYNAMIC Cursor قابل مشاهده هستند. این نوع نشانگرها پیچیدگی و سربار بیشتری دارند.
* **KEYSET (مجموعه کلید):** این نشانگر ترکیبی از STATIC و DYNAMIC است. زمانی که نشانگر باز میشود، مجموعه کلیدهای سطرها (مانند کلید اصلی) در TempDB ذخیره میشوند. تغییرات در مقادیر ستونها یا حذف سطرها در مجموعه نتایج قابل مشاهده است، اما اضافه شدن سطرهای جدید توسط سایر اتصالات دیده نمیشود.
* **FAST_FORWARD (حرکت سریع به جلو):** این نوع نشانگر برای پیمایش سریع به جلو (فقط در یک جهت) بهینه شده است. FAST_FORWARD Cursors فقط یکبار به جلو حرکت میکنند و هیچ قابلیت اسکرول (پیمایش به عقب) ندارند.
Scroll Cursor در SQL Server چیست؟
Scroll Cursor نوعی نشانگر است که به کاربر اجازه میدهد تا علاوه بر حرکت به جلو، به عقب نیز در مجموعه نتایج حرکت کند. این قابلیت پیمایش دوطرفه، به کاربران کنترل بیشتری بر نحوه پردازش دادهها میدهد و آنها را قادر میسازد تا به سطر اول، آخر، قبلی، بعدی، یا یک سطر مشخص (مطلق یا نسبی) دسترسی پیدا کنند. Scroll Cursors با اضافه کردن کلمه کلیدی `SCROLL` در دستور `DECLARE CURSOR` تعریف میشوند.
نحوه تعریف Scroll Cursor
برای تعریف یک Scroll Cursor، باید کلمه کلیدی `SCROLL` را به دستور `DECLARE CURSOR` اضافه کنید. ساختار کلی به شکل زیر است:
DECLARE cursor_name SCROLL CURSOR FOR
SELECT column1, column2 FROM table_name WHERE condition;
**روشهای مختلف بازیابی دادهها از Scroll Cursor**
پس از باز کردن یک Scroll Cursor، میتوانید از دستور `FETCH` همراه با گزینههای مختلف برای حرکت در مجموعه نتایج و بازیابی دادهها استفاده کنید. گزینههای اصلی `FETCH` عبارتند از:
FETCH NEXT | PRIOR | FIRST | LAST | ABSOLUTE n | RELATIVE n
در ادامه به توضیح و مثال هر یک از این گزینهها میپردازیم:
* **FETCH NEXT:** این گزینه سطر بعدی را از مکان فعلی نشانگر بازیابی میکند. این حالت پیشفرض است اگر هیچ گزینهای مشخص نشود.
* **FETCH PRIOR:** این گزینه سطر قبلی را از مکان فعلی نشانگر بازیابی میکند. این قابلیت فقط در Scroll Cursorها موجود است.
* **FETCH FIRST:** این گزینه اولین سطر مجموعه نتایج را بازیابی میکند و نشانگر را به آن مکان منتقل میکند.
* **FETCH LAST:** این گزینه آخرین سطر مجموعه نتایج را بازیابی میکند و نشانگر را به آن مکان منتقل میکند.
* **FETCH ABSOLUTE n:** این گزینه سطر با شماره ترتیبی `n` را از ابتدای مجموعه نتایج بازیابی میکند. اگر `n` منفی باشد، از انتهای مجموعه نتایج شمارش میکند (مثلاً -1 برای آخرین سطر).
* **FETCH RELATIVE n:** این گزینه سطر `n` را نسبت به مکان فعلی نشانگر بازیابی میکند. اگر `n` مثبت باشد، به جلو حرکت میکند؛ اگر `n` منفی باشد، به عقب حرکت میکند.
**مثالهایی از Scroll Cursor در SQL Server**
برای نشان دادن نحوه عملکرد Scroll Cursorها، ابتدا یک جدول نمونه ایجاد کرده و تعدادی داده در آن وارد میکنیم.
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY,
FirstName VARCHAR(50),
LastName VARCHAR(50),
Department VARCHAR(50)
);
INSERT INTO Employees (EmployeeID, FirstName, LastName, Department) VALUES
(1, 'Ali', 'Ahmadi', 'HR'),
(2, 'Sara', 'Karimi', 'IT'),
(3, 'Reza', 'Mohammadi', 'Finance'),
(4, 'Mina', 'Hassani', 'Marketing'),
(5, 'Hossein', 'Ebrahimi', 'IT');
**مثال استفاده از FETCH NEXT و FETCH PRIOR:**
این مثال نشان میدهد که چگونه میتوان با استفاده از `FETCH NEXT` و `FETCH PRIOR` به جلو و عقب در مجموعه نتایج حرکت کرد.
DECLARE @EmployeeID INT, @FirstName VARCHAR(50);
DECLARE EmployeeCursor SCROLL CURSOR FOR
SELECT EmployeeID, FirstName FROM Employees ORDER BY EmployeeID;
OPEN EmployeeCursor;
PRINT 'Moving NEXT:';
FETCH NEXT FROM EmployeeCursor INTO @EmployeeID, @FirstName;
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'EmployeeID: ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
FETCH NEXT FROM EmployeeCursor INTO @EmployeeID, @FirstName;
END;
-- Now move back to the start
CLOSE EmployeeCursor;
OPEN EmployeeCursor;
PRINT 'Moving PRIOR (after re-opening and moving to last):';
FETCH LAST FROM EmployeeCursor INTO @EmployeeID, @FirstName; -- Move to last to show PRIOR
PRINT 'Last Employee: ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
FETCH PRIOR FROM EmployeeCursor INTO @EmployeeID, @FirstName;
PRINT 'Prior Employee: ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
FETCH PRIOR FROM EmployeeCursor INTO @EmployeeID, @FirstName;
PRINT 'Prior Employee: ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
CLOSE EmployeeCursor;
DEALLOCATE EmployeeCursor;
**مثال استفاده از FETCH FIRST و FETCH LAST:**
این مثال نحوه بازیابی اولین و آخرین سطر را نمایش میدهد.
DECLARE @EmployeeID INT, @FirstName VARCHAR(50);
DECLARE EmployeeCursor SCROLL CURSOR FOR
SELECT EmployeeID, FirstName FROM Employees ORDER BY EmployeeID;
OPEN EmployeeCursor;
FETCH FIRST FROM EmployeeCursor INTO @EmployeeID, @FirstName;
PRINT 'First Employee: ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
FETCH LAST FROM EmployeeCursor INTO @EmployeeID, @FirstName;
PRINT 'Last Employee: ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
CLOSE EmployeeCursor;
DEALLOCATE EmployeeCursor;
**مثال استفاده از FETCH ABSOLUTE:**
این مثال نحوه دسترسی مستقیم به یک سطر خاص بر اساس موقعیت مطلق آن را نشان میدهد.
DECLARE @EmployeeID INT, @FirstName VARCHAR(50);
DECLARE EmployeeCursor SCROLL CURSOR FOR
SELECT EmployeeID, FirstName FROM Employees ORDER BY EmployeeID;
OPEN EmployeeCursor;
FETCH ABSOLUTE 3 FROM EmployeeCursor INTO @EmployeeID, @FirstName;
PRINT 'Third Employee (Absolute 3): ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
FETCH ABSOLUTE 1 FROM EmployeeCursor INTO @EmployeeID, @FirstName;
PRINT 'First Employee (Absolute 1): ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
FETCH ABSOLUTE -1 FROM EmployeeCursor INTO @EmployeeID, @FirstName; -- Last row
PRINT 'Last Employee (Absolute -1): ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
CLOSE EmployeeCursor;
DEALLOCATE EmployeeCursor;
**مثال استفاده از FETCH RELATIVE:**
این مثال نحوه حرکت `n` سطر از موقعیت فعلی نشانگر را نمایش میدهد.
DECLARE @EmployeeID INT, @FirstName VARCHAR(50);
DECLARE EmployeeCursor SCROLL CURSOR FOR
SELECT EmployeeID, FirstName FROM Employees ORDER BY EmployeeID;
OPEN EmployeeCursor;
-- Start at the first row
FETCH FIRST FROM EmployeeCursor INTO @EmployeeID, @FirstName;
PRINT 'Current Employee (First): ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
-- Move 2 rows forward from current position (which is 1) -> to row 3
FETCH RELATIVE 2 FROM EmployeeCursor INTO @EmployeeID, @FirstName;
PRINT 'Employee (Relative +2): ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
-- Move 1 row backward from current position (which is 3) -> to row 2
FETCH RELATIVE -1 FROM EmployeeCursor INTO @EmployeeID, @FirstName;
PRINT 'Employee (Relative -1): ' + CAST(@EmployeeID AS VARCHAR) + ', FirstName: ' + @FirstName;
CLOSE EmployeeCursor;
DEALLOCATE EmployeeCursor;
ملاحظات مهم:
Scroll Cursors با وجود انعطافپذیری در پیمایش دادهها، میتوانند سربار عملکردی قابل توجهی داشته باشند. دلیل آن این است که برای پشتیبانی از پیمایش دوطرفه، SQL Server ممکن است نیاز به نگهداری کپیهایی از دادهها در TempDB یا ردیابی تغییرات داشته باشد. بنابراین، توصیه میشود تا حد امکان از دستورات SET-based (مبتنی بر مجموعه) به جای Cursors استفاده شود. اگر استفاده از Cursor اجتنابناپذیر است، باید نکات زیر را در نظر گرفت:
* **فقط خواندنی (Read-Only):** اگر نیازی به بهروزرسانی یا حذف دادهها از طریق Cursor ندارید، همیشه Cursor را به صورت `READ_ONLY` تعریف کنید. این کار میتواند سربار را کاهش دهد.
* **اندازه مجموعه نتایج:** برای مجموعههای داده بزرگ، Scroll Cursors میتوانند بسیار کند عمل کنند. تا حد امکان، با افزودن شرایط `WHERE` مناسب، مجموعه نتایج Cursor را محدود کنید.
* **حذف و بستن Cursor:** همیشه پس از اتمام کار، Cursor را ببندید (`CLOSE`) و آن را از حافظه آزاد کنید (`DEALLOCATE`). این کار از مصرف بیرویه منابع سیستم جلوگیری میکند.
در نهایت، Scroll Cursors ابزاری قدرتمند برای سناریوهای خاصی هستند که نیاز به کنترل دقیق روی پیمایش سطر به سطر دادهها وجود دارد، اما باید با آگاهی کامل از تأثیرات عملکردی آنها استفاده شوند.