# پایگاه‌های داده > SQL Server >  آموزش Cursor در sql

## amirjalili

دوستان این تایپیک بخشی از مقاله آقای ASKaffash هست که در اون Cursor ها رو به خوبی توضیح داده. با توجه به منابع فارسی کم من تصمیم گرفتم این بخش از این مقاله خوب رو اینجا قرار بدم تا دوستان دسترسی مستقیم تری به این بخش از آموزش داشته باشند.

*Cursor :*
هنگام استفاده از دستورات SQL  نظیر Select  کلیه رکوردهای درخواستی بطور کامل استخراج می گردد اما در مواردی نیاز است که رکوردهای استخراج شده تحت شرایطی خاص مورد پردازش مجدد قرار گرفته و به برنامه درخواست کننده ارسال گردد در این صورت استفاده از کرسرها بسیار حائز اهمیت خواهد بود در واقع برای استفاده از یک کرسر می توان به ترتیب مراحل ذیل عمل نمود.
-	یک متغیر از نوع کرسر تعریف می گردد. که شامل دستور Select  درخواستی خواهد بود.
-	با استفاده از دستور Open  یک کرسر آماده استفاده می گردد.
-	با استفاده از دستور Fetch  حرکت درون یک کرسر امکان پذیر می گردد که در این حالت مقدار فیلدهای اعلام شده در رکورد جاری در دسترس می باشد.
-	با استفاده از دستور Close  کرسر فعال شده بسته می شود.
-	با استفاده از Deallcate  فضای اختصاص داده شده برای کرسر آزاد می گردد.

روش ایجاد کرسر :
شکلی از دستور Declare  قادر به ایجاد نوع داده ای تحت عنوان کرسر خواهد بود که در شکل کلی ذیل قابل ارائه می باشد:

 Global ] یا  Cursor    [ Local           نام کرسر        Declare
] Dynamic     یا    Keyset        یا       Scroll ] [ Static       یا      [ Forward_Only
     Optimistic ]یا    Scroll_Locks    یا[ Read_Only     
دستور  For     Select
] ]  ستون ها [ For  UpDate  [ Of 


براساس این دستور موارد ذیل می بایستی مدنظر قرار گیرد:
-	Local  که پیش فرض دستور است باعث ایجاد یک کرسر محلی در یک SP  یا Trigger  خواهد بود که با خروج از آنها بطور اتوماتیک حافظه مربوطه آزاد خواهد شد.
-	Global  یک کرسر با زاویه دید عمومی ایجاد می کند که در کلیه SP ها یا دستورات اجرائی در دسترس خواهد بود و درصورت پایان ارتباط بصورت اتوماتیک حافظه مربوطه آزاد خواهد شد.
( 67 )

-	Forward_Only  که پیش فرض دستور است یک کرسر رو به جلو ایجاد می کند که در این حالت حرکت اشاره گر رکورد به عقب مجاز نخواهد بود و این موضوع سرعت عملیات را نیز افزایش می دهد و در مقابل Scroll  قابلیت جلو و عقب را برای کرسر ممکن می سازد.
-	Static  که پیش فرض دستور می باشد کرسری است که یک کپی موقت درون پایگاه داده TempDB  ایجاد می کند و در این حالت هیچ عکس العملی را به داده های اصلی منعکس   نمی کند.
-	Keyset  کرسری ایجاد می کند که تغییرات ایجاد شده توسط دیگران به شما منعکس می گردد و حذف و افزودن رکورد توسط دیگران به این کرسر منعکس نخواهد شد.
-	Dynamic  یک کرسر متغیر ایجاد می کند که کلیه تغییرات توسط دیگران به کرسر منعکس  می گردد.
-	Read_Only  کرسری ایجاد می کند که تغییرات در داده ها را منعکس نخواهد کرد.
-	Scroll_Locks  نوعی کرسر ایجاد می کند که تضمین می نماید که سطرها را به همان طریقی که خوانده است جهت تغییرات بعدی قفل نماید.
-	Optimistic نوعی کرسری است که تغییرات در داده ها را تضمین نمی کند و اطلاعات قفل نمی شود و جهت اعمال تغییرات در صورت وجود فیلد از نوع TimeStamp  استفاده کرده و در غیراینصورت از مقدار CheckSum  رکورد استفاده می نماید.
-	دستور Select  اعلام شده همان فرمان استاندارد Select  می باشد و استفاده از فرامینی مشابه Into  درون یک کرسر مجاز نخواهد بود.
-	پارامتر اختیاری For UpDate  جهت تعیین ستونهای قابل ویرایش پیش بینی شده است. 

حرکت روی رکوردها ( Fetching )  :
مشابه همه ابزارهای ارتباط با پایگاه داده نظیر ADO  حرکت روی رکوردهای بازگشتی با استفاده از دستور Fetch  امکان پذیر است و شکل کلی دستور Fetch  به صورت ذیل می باشد:  

یا     Last    یا     First      یا       Prior      یا       Fetch   [ Next
]  مقدار نمو       Relative      یا     شماره رکورد       Absolute
نام کرسر یا متغیر از نوع کرسر       From
]  فهرست متغیرها      [ Into

( 68 )

پارامترهای بکار رفته در دستور به شکل ذیل خواهد بود: 
-	Next  که پیش فرض دستور است حرکت به رکورد بعدی را میسر می کند.
-	Prior  حرکت اشاره گر به رکورد قبلی خواهد بود.
-	First  حرکت اشاره گر به رکورد اولیه خواهد بود.
-	Last  حرکت اشاره گر به رکورد انتهائی خواهد بود.
-	Absolute   اشاره گر را به شماره رکورد اعلام شده هدایت می کند.
-	Relative    اشاره گر را به اندازه تغییرات اعلام شده نسبت به رکورد جاری هدایت      می کند.
-	درصورتیکه قرار است ستونهای رکورد جاری در تعدادی متغیر جانشین شود از فرمان Into  به همراه فهرست متغیرها که قبلاً  اعلام شده اند استفاده می گردد.
-	مقدار تابع @@Fetch_Status  وضعیت کنونی کرسر را مورد تحلیل قرار می دهد و درصورتیکه مقدار صفر را داشته باشد نشان می دهد که عملیات دریافت داده توسط کرسر   موفقیت آمیز بوده است.

مثال) در سیستم پرسنلی تابعی بنویسید که متوسط تحصیلات افراد تحت تکفل هر شخص را محاسبه کند.
	همانطور که از ظاهر درخواست معین است جدول ChdLvL جهت انجام عملیات Fetch  مناسب ترین انتخاب است بدین منظور می نویسیم: 
Create  Function  AvgLvL (@PrsID   Int )
Returns    Real
AS    Begin
Declare    @i    TinyInt   , @L   Real   , @A    Real
Set    @i = 0     Set  @A = 0
Declare    TmPCursor    Cursor    For
Select    LID    From    ChdLvL    Where   PrsID = @PrsID
Open    TmPCursor
While   1=1    Begin
Fetch   Next   From   TmPCursor   Into   @L
If    @@ Fetch_Status < > 0   Break
Set   @i = @i + 1
Set   @A= @A +@L
End
Close   TmPCursor
Deallocate   TmPCursor
If    @i < >0   Set  @A = @A / @i
Return   @A
END
( 69 )
	در برنامه طراحی شده شماره کارمندی بعنوان پارامتر ورودی به تابع AvgLvL  وارد می گردد و در خط ششم کرسری بنام TmPCursor  براساس دستور Select  که افراد تحت تکفل PrsID  خاصی را باز می گرداند ایجاد شده است و درون حلقه مقدار کلیه تحصیلات افراد تکفل جمع می شود و براساس @@Fetch_Status  انتهای کرسر کنترل می گردد ، سپس کرسر ایجاد شده بادستور Close  بسته   می شود و دستور Deallocate  حافظه ایجاد شده توسط کرسر را آزاد می کند و در انتهای برنامه تابع میانگین سطح تحصیلات را باز می گرداند ، شایان ذکر است که میانگین سطح تحصیلات افراد تحت تکفل با استفاده از یک Select  نیز قابل محاسبه بود ولی در این تابع تنها هدف استفاده از کرسرها می باشد چون در مواردی با استفاده از یک دستور Select  نمی توان درخواست موردنظر را محقق کرد ، بعنوان مثال در مواردیکه نتیجه ای وابسته به مقایسه هر رکورد با رکورد ماقبل یا مابعد باشد استفاده از کرسرها یک الزام خواهد بود.

----------


## محمد سلیم آبادی

سلام،
نباید تا جایی که امکان دارد از Cursor استفاده کنید.
سعی کنید منطق Cursor ای که دارید از آن استفاده می کنید را با کمک روش های set-based پیاده سازی کنید.

یکی از دلایل اینکه از Cursor ها صرف نظر می شود این است که در حلقه ی کرسر هر عبارت FETCH معادل با یک عبارت SELECT هست که هزینه ی زیادی خواهد داشت.

----------


## jeus

> نباید تا جایی که امکان دارد از Cursor استفاده کنید.
> سعی کنید منطق Cursor ای که دارید از آن استفاده می کنید را با کمک روش های  set-based پیاده سازی کنید.
> 
> یکی از دلایل اینکه از Cursor ها صرف نظر می شود این است که در حلقه ی کرسر  هر عبارت FETCH معادل با یک عبارت SELECT هست که هزینه ی زیادی خواهد  داشت.


میشه یک منبع بیارید.

----------


## محمد سلیم آبادی

> میشه یک منبع بیارید.


 ابتدا یه سری به تصویری که در این تاپیک ضمیمه کردم بزنید:
http://www.barnamenevis.org/sh...highlight=mcts

سپس این مقاله را مطالعه کنید:
http://www.sql-server-performance.co...ursors_p1.aspx

یک چیزی هم داخل پرانتز بگم. در این مدتی که با T-SQL کار کردم تا حالا به cursor به عنوان آخرین راه حل هم فکر نکردم! یعنی با روشهای دیگه نیازم را بر طرف کردم. تنها اوایل که خیلی خیلی تازه کار بودم برای concatenation از cursor استفاده کردم و بعدها فهمیدم برای حل این مساله ده ها روش مناسب تر وجود داره!

----------

