آموزش EXEC در SQL Server با ۱۰ مثال عملی
مقدمه
دستور EXEC یکی از اجزای اصلی کار با Stored Procedure در Microsoft SQL Server است. این مقاله از تعریف و Syntax آغاز میکند و سپس با ده مثال مستقل، مدیریت پارامتر، رفتار NULL، خطا، امنیت و کارایی را بررسی میکند. هدف این است که اسکریپت نهایی در SSMS قابل آزمون و برای پروژه واقعی قابل اقتباس باشد.
برای مشاهده جایگاه EXEC در چرخه کامل ایجاد، تغییر، اجرا و حذف رویهها، راهنمای جامع رویههای ذخیرهشده در SQL Server را نیز مطالعه کنید. لینک حاضر بدون وابستگی به دامنه ساخته شده و با جابهجایی سایت همچنان معتبر میماند.
تعریف و کاربرد EXEC
EXEC برای اجرای رویه ذخیرهشده یا یک Batch پویا با فرم کوتاه EXEC استفاده میشود. در طراحی حرفهای، این دستور فقط یک عبارت نحوی نیست؛ بخشی از قرارداد داده، مدل امنیت، چرخه انتشار و قابلیت مشاهده سامانه محسوب میشود.
اصل حرفهای: پیش از استفاده از EXEC، اثر آن بر قرارداد مصرفکننده، مجوزها، تراکنش و Planهای اجرایی را مشخص و قابل آزمون کنید.
Syntax استاندارد
[ EXEC [ UTE ] ]
[ @return_status = ]
[ schema_name. ] procedure_name
[ [ @parameter = ] { value | @variable [ OUTPUT ] | DEFAULT } [ ,...n ] ];
EXEC ( { @string_variable | N'string' } [ + ...n ] );
پارامترها و اجزای مهم
- return_status یک متغیر int برای دریافت کد RETURN رویه است.
- procedure_name بهتر است با Schema نوشته شود تا Resolution و Plan Cache پایدار بماند.
- پارامتر میتواند مقدار ثابت، متغیر، DEFAULT یا متغیر OUTPUT باشد.
- فرم پرانتزی رشتهای یک Batch پویا اجرا میکند و برای ورودی کاربر باید با احتیاط جایگزین sp_executesql شود.
نوع خروجی و اثر دستور
EXEC میتواند Result Setهای رویه، مقدار پارامتر OUTPUT و Return Code صحیح را در سه کانال مستقل به فراخواننده برساند.
تحلیل فنی و معماری
EXEC صورت کوتاه EXECUTE و رایجترین روش فراخوانی رویه در T-SQL است. خوانایی فراخوان زمانی بیشینه میشود که نام Schema و نام پارامترها صریح باشند. اتکا به ترتیب پارامترها در اسکریپتهای طولانی خطاپذیر است و تغییرهای بعدی را دشوار میکند.
Result Set، OUTPUT و RETURN هدفهای متفاوتی دارند. مجموعه ردیف برای داده جدولی، OUTPUT برای چند مقدار مشخص و RETURN برای وضعیت عددی محدود مناسب است. ترکیب بیقاعده این کانالها، قرارداد مصرفکننده و مدیریت خطا را مبهم میکند.
EXEC رشتهای برای نام شیء یا ساختار واقعاً پویا کاربرد دارد، اما الحاق مقدار کاربر خطر SQL Injection و آشفتگی Plan Cache ایجاد میکند. داده را با sys.sp_executesql و تعریف نوع پارامتر جدا کنید و نام شیء را فقط از Allowlist معتبر بسازید.
پارامترهای نامتناسب با ستون میتوانند Conversion ضمنی و Scan ایجاد کنند، حتی اگر رویه از نظر منطقی صحیح باشد. نوع و طول متغیر فراخوان را با امضای رویه هماهنگ کنید. Actual Plan و هشدار PlanAffectingConvert برای کشف این مشکل مهماند.
INSERT ... EXEC امکان ذخیره یک Result Set را میدهد، اما محدودیت nesting و وابستگی شدید به شکل خروجی دارد. برای رابطهای پایدار، نام، ترتیب و نوع ستونها را مستند و با تست قرارداد کنترل کنید. تغییر خاموش Result Set میتواند Job یا ETL را بشکند.
اندازهگیری یک EXEC باید با چند توزیع پارامتر و در شرایط کش سرد و گرم انجام شود. Parameter Sensitive Plan، Parameter Sniffing، آمار و ایندکس میتوانند زمان اجرا را تغییر دهند. Query Store تصویر معتبرتری از رفتار واقعی نسبت به یک اجرای دستی ارائه میکند.
ده مثال عملی و قابل اجرا
مثال 1: اجرای رویه بدون پارامتر
فرم EXEC یک رویه ساده را اجرا میکند و Result Set آن را به فراخواننده میرساند.
CREATE OR ALTER PROCEDURE dbo.usp_ExecDemo01 AS
BEGIN SET NOCOUNT ON; SELECT N'آماده' AS ServiceState; END;
GO
EXEC dbo.usp_ExecDemo01;
| خروجی یا وضعیت | مقدار مورد انتظار |
|---|
| نتیجه | ServiceState = آماده |
نکته کاربردی: Schema-qualified بودن نام، Resolution و استفاده مجدد از Plan را قابل پیشبینی میکند.
مثال 2: پارامترهای نامدار
پارامترها با نام ارسال میشوند تا خوانایی EXEC و ایمنی نگهداری افزایش یابد.
CREATE OR ALTER PROCEDURE dbo.usp_ExecDemo02
@Quantity int, @Price decimal(18,2)
AS
BEGIN SET NOCOUNT ON; SELECT @Quantity * @Price AS TotalAmount; END;
GO
EXEC dbo.usp_ExecDemo02 @Quantity = 4, @Price = 75000;
| خروجی یا وضعیت | مقدار مورد انتظار |
|---|
| نتیجه | TotalAmount = 300000.00 |
نکته کاربردی: در یک فراخوان، پس از نخستین پارامتر نامدار همه پارامترهای بعدی را نیز نامدار بنویسید.
مثال 3: پارامتر پیشفرض و NULL
یک بار از مقدار پیشفرض و بار دیگر از NULL صریح استفاده میکنیم تا تفاوت قرارداد EXEC دیده شود.
CREATE OR ALTER PROCEDURE dbo.usp_ExecDemo03 @Label nvarchar(30) = N'پیشفرض'
AS
BEGIN SET NOCOUNT ON; SELECT COALESCE(@Label, N'تهی') AS FinalLabel; END;
GO
EXEC dbo.usp_ExecDemo03;
EXEC dbo.usp_ExecDemo03 @Label = NULL;
| خروجی یا وضعیت | مقدار مورد انتظار |
|---|
| نتیجه | دو خروجی: پیشفرض و تهی |
نکته کاربردی: حذف آرگومان با ارسال NULL یکسان نیست و باید جداگانه آزمون شود.
مثال 4: دریافت OUTPUT
متغیر محلی با کلیدواژه OUTPUT به رویه داده میشود و پس از EXEC مقدار جدید را در خود نگه میدارد.
CREATE OR ALTER PROCEDURE dbo.usp_ExecDemo04 @Input int, @Output int OUTPUT
AS
BEGIN SET NOCOUNT ON; SET @Output = @Input * 2; END;
GO
DECLARE @Value int;
EXEC dbo.usp_ExecDemo04 21, @Value OUTPUT;
SELECT @Value AS OutputValue;
| خروجی یا وضعیت | مقدار مورد انتظار |
|---|
| نتیجه | OutputValue = 42 |
نکته کاربردی: نوع متغیر دریافتکننده باید با پارامتر خروجی سازگار باشد.
مثال 5: دریافت Return Code
کد بازگشت صحیح در سمت چپ EXEC دریافت میشود و وضعیت اعتبارسنجی را اعلام میکند.
CREATE OR ALTER PROCEDURE dbo.usp_ExecDemo05 @IsValid bit
AS
BEGIN IF @IsValid = 1 RETURN 0; RETURN 2; END;
GO
DECLARE @Status int;
EXEC @Status = dbo.usp_ExecDemo05 @IsValid = 1;
SELECT @Status AS StatusCode;
| خروجی یا وضعیت | مقدار مورد انتظار |
|---|
| نتیجه | StatusCode = 0 |
نکته کاربردی: Return Code را برای وضعیت محدود و Result Set یا OUTPUT را برای داده استفاده کنید.
مثال 6: اجرای SQL پویا
فرم رشتهای EXEC یک Batch پویا را اجرا میکند؛ این نمونه فقط متن ثابت کنترلشده دارد.
DECLARE @Sql nvarchar(max) = N'
SELECT DB_NAME() AS DatabaseName, COUNT(*) AS ProcedureCount
FROM sys.procedures;';
EXEC(@Sql);
| خروجی یا وضعیت | مقدار مورد انتظار |
|---|
| نتیجه | نام پایگاه داده و تعداد رویهها |
نکته کاربردی: برای دادههای ورودی کاربر، sp_executesql با پارامتر را به الحاق رشته ترجیح دهید.
مثال 7: sp_executesql پارامتری
در این سناریو EXEC روی sys.sp_executesql انجام میشود تا نوع پارامتر و امکان استفاده مجدد از Plan حفظ شود.
DECLARE @Statement nvarchar(max) = N'SELECT @A + @B AS SumValue;';
EXEC sys.sp_executesql
@Statement,
N'@A int, @B int',
@A = 18,
@B = 24;
| خروجی یا وضعیت | مقدار مورد انتظار |
|---|
| نتیجه | SumValue = 42 |
نکته کاربردی: پارامترها علاوه بر امنیت، Conversion و Cardinality را نیز شفافتر میکنند.
مثال 8: گرفتن Result Set در جدول
خروجی EXEC با INSERT ... EXEC در جدول موقت ذخیره میشود تا در ادامه Query قابل استفاده باشد.
CREATE OR ALTER PROCEDURE dbo.usp_ExecDemo08
AS
BEGIN SET NOCOUNT ON; SELECT 1 AS ItemId, N'کالا' AS ItemTitle; END;
GO
CREATE TABLE #Result(ItemId int, ItemTitle nvarchar(50));
INSERT INTO #Result(ItemId, ItemTitle)
EXEC dbo.usp_ExecDemo08;
SELECT * FROM #Result;
DROP TABLE #Result;
| خروجی یا وضعیت | مقدار مورد انتظار |
|---|
| نتیجه | ItemId = 1 و ItemTitle = کالا |
نکته کاربردی: ساختار Result Set باید دقیقاً با جدول مقصد سازگار و پایدار باشد.
مثال 9: مدیریت خطای فراخوان
فراخوان EXEC داخل TRY/CATCH قرار میگیرد و جزئیات استاندارد خطا در خروجی کنترلشده دیده میشود.
CREATE OR ALTER PROCEDURE dbo.usp_ExecDemo09 @Divisor int
AS
BEGIN SET NOCOUNT ON; SELECT 100 / @Divisor AS DivisionResult; END;
GO
BEGIN TRY
EXEC dbo.usp_ExecDemo09 @Divisor = 4;
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber, ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
| خروجی یا وضعیت | مقدار مورد انتظار |
|---|
| نتیجه | DivisionResult = 25 |
نکته کاربردی: در مسیر خطا، نام رویه و شماره خط را نیز برای Observability ثبت کنید.
مثال 10: اندازهگیری I/O و زمان
برای ارزیابی کارایی فراخوان EXEC آمار زمان و I/O فعال میشود؛ Query نمونه فقط کاتالوگ کوچک را میخواند.
CREATE OR ALTER PROCEDURE dbo.usp_ExecDemo10
AS
BEGIN SET NOCOUNT ON; SELECT COUNT(*) AS ObjectCount FROM sys.objects; END;
GO
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
EXEC dbo.usp_ExecDemo10;
SET STATISTICS TIME OFF;
SET STATISTICS IO OFF;
| خروجی یا وضعیت | مقدار مورد انتظار |
|---|
| نتیجه | ObjectCount و پیامهای Statistics |
نکته کاربردی: اعداد را در محیط مشابه تولید و در چند اجرا مقایسه کنید؛ یک اجرای سرد معیار کافی نیست.
خطاهای رایج
- نوشتن نام شیء بدون Schema در EXEC باعث Resolution مبهم، خطای محیطی و گاهی استفاده کمتر مؤثر از Plan Cache میشود.
- یکسان ندانستن مقدار پیشفرض با NULL صریح میتواند منطق تجاری را تغییر دهد؛ هر دو مسیر را جداگانه آزمایش کنید.
- الحاق مستقیم ورودی کاربر به Dynamic SQL خطر تزریق، Conversion و تولید Planهای پراکنده ایجاد میکند.
- نادیدهگرفتن مجوز، وابستگی یا شکل Result Set باعث شکست پس از استقرار میشود، حتی اگر اسکریپت DDL بدون خطا اجرا شده باشد.
- بلعیدن خطا در CATCH و برنگرداندن آن به فراخواننده، پایش و Rollback لایه کاربرد را غیرقابل اعتماد میکند.
ملاحظات Performance و بهینهسازی
برای سنجش EXEC از حدس استفاده نکنید. Baseline شامل Duration، CPU، Logical Reads، تعداد اجرا و Waitها بسازید؛ سپس Actual Execution Plan و داده Query Store را پیش و پس از تغییر مقایسه کنید. تفاوت نوع پارامتر با ستون، Parameter Sniffing، آمار قدیمی و ایندکس نامناسب از علتهای رایج افت کاراییاند.
- نام Schema و نوع پارامترها را دقیق و همسان با ستون مقصد نگه دارید.
- از SELECT * در قراردادهای پایدار دوری و ستونهای لازم را صریح انتخاب کنید.
- تراکنش را کوتاه نگه دارید و دسترسی به اشیا را با ترتیب ثابت انجام دهید تا Deadlock کمتر شود.
- WITH RECOMPILE را راهحل پیشفرض ندانید؛ هزینه Compilation و تنوع پارامتر را با داده واقعی بسنجید.
- برای رگرسیون از Query Store و برای رخدادهای دقیق از Extended Events استفاده کنید.
Best Practices
- اسکریپت را idempotent و در کنترل نسخه نگهداری کنید.
- برای تغییرهای مخرب، برنامه Rollback و Health Check آماده داشته باشید.
- کمترین سطح مجوز را از طریق Role و GRANT هدفمند پیاده کنید.
- ورودی، خروجی، NULL، خطا و سازگاری نسخه را در تست خودکار پوشش دهید.
- نامگذاری تجاری پایدار و توضیح مسئولیت رویه را در مستند فنی ثبت کنید.
- کد Production را با داده نماینده و حجم نزدیک به واقعیت آزمایش کنید.
سؤالات متداول
۱. EXEC در SQL Server دقیقاً چه کاری انجام میدهد؟
این دستور برای اجرای رویه ذخیرهشده یا یک Batch پویا با فرم کوتاه EXEC بهکار میرود. اثر آن در سطح پایگاه داده ثبت میشود و باید با نامگذاری روشن، کنترل نسخه و آزمون قابل تکرار همراه باشد تا رفتار محیط توسعه و تولید یکسان بماند.
۲. از چه نسخهای میتوان از قابلیتهای جدید EXEC استفاده کرد؟
هسته دستور در نسخههای قدیمی SQL Server نیز وجود دارد، اما گزینههایی مانند CREATE OR ALTER یا DROP IF EXISTS به نسخه وابستهاند. پیش از استقرار، Compatibility Level و مستندات همان نسخه را بررسی کنید.
۳. آیا استفاده از EXEC هزینه نگهداری سامانه را کاهش میدهد؟
اگر استاندارد کدنویسی، ثبت تغییر و پایش اجرا رعایت شود، بله؛ منطق متمرکز و قابل ممیزی هزینه خطای انسانی را کم میکند. برای سامانههای حساس، بازبینی تخصصی و آزمون کارایی پیش از انتشار ارزش تجاری مستقیمی دارد.
۴. چه زمانی برای طراحی مبتنی بر EXEC به مشاوره نیاز داریم؟
وقتی زنجیره وابستگی، مجوزها، حجم تراکنش یا حساسیت امنیتی زیاد است، ارزیابی معماری مفید خواهد بود. مشاوره SQL Server میتواند قرارداد ورودی و خروجی، طرح بازگشت و شاخصهای پایش را پیش از پیادهسازی تثبیت کند.
۵. تفاوت کاربرد EXEC با اجرای مستقیم Query چیست؟
EXEC یک واحد نامدار و قابل کنترل در چرخه استقرار ایجاد میکند، در حالی که Query مستقیم معمولاً پراکندهتر است. انتخاب صحیح به نیاز استفاده مجدد، امنیت، پارامتردهی، کش طرح اجرا و مسئولیت تیمها بستگی دارد.
۶. چگونه میتوان پیادهسازی EXEC را برای پروژه سفارش داد؟
ابتدا ورودیها، خروجیها، SLA، سطح دسترسی و سناریوهای خطا مستند میشوند؛ سپس نمونه قابل آزمون، اسکریپت استقرار و معیار پذیرش تهیه میشود. این روش تحویل پروژه را قابل سنجش و پشتیبانی را سادهتر میکند.
۷. رایجترین خطای مرتبط با EXEC چیست؟
رایجترین خطا نادیدهگرفتن زمینه اجرایی، Schema یا وابستگیهاست. بهویژه کنترل نوع پارامتر، Schema، مجوز و وابستگی ضروری است. ثبت متن کامل خطا، شماره خط و نام رویه در CATCH، یافتن علت را بسیار سریعتر میکند.
۸. EXEC چه اثری بر Performance دارد؟
خود دستور فقط بخشی از مسئله است؛ کیفیت Queryهای داخل رویه، نوع پارامتر، آمار، ایندکس و همزمانی تعیینکنندهاند. Query Store، Actual Execution Plan و Extended Events ابزارهای مناسب سنجش پیش و پس از تغییر هستند.
۹. بهترین روش استفاده از EXEC چیست؟
اسکریپت idempotent، نام Schema-qualified، کمترین سطح مجوز، قرارداد پایدار، SET NOCOUNT ON، مدیریت خطا و آزمون بازگشت را همزمان رعایت کنید. هر تغییر باید در کنترل نسخه و فرایند انتشار ثبت شود.
۱۰. آیا EXEC با Azure SQL و نسخههای جدید سازگار است؟
بخش اصلی در SQL Server و Azure SQL Database قابل استفاده است، ولی برخی گزینههای امنیتی یا اجرای راهدور میان محصولات تفاوت دارند. اسکریپت را روی موتور و سطح سازگاری مقصد آزمایش کنید و به فرض سازگاری کامل اکتفا نکنید.
سؤالات مصاحبه
۱. تفاوت Result Set، OUTPUT و RETURN هنگام EXEC چیست؟
Result Set داده جدولی، OUTPUT مقدار پارامتری و RETURN کد وضعیت int است. پاسخ حرفهای باید درباره قرارداد، نوع داده و نحوه دریافت هر سه در فراخواننده توضیح دهد.
۲. چرا Schema-qualified بودن نام در EXEC مهم است؟
ابهام نام را از بین میبرد، امنیت و خوانایی را بهتر میکند و Resolution شیء و استفاده مجدد از Plan را قابل پیشبینیتر میسازد.
۳. چگونه SQL Injection را در اجرای پویا مهار میکنید؟
مقادیر را با sp_executesql پارامتری ارسال میکنیم، نام اشیا را از Allowlist کنترلشده میگیریم، QUOTENAME را فقط برای Identifier معتبر بهکار میبریم و مجوز حساب اجرا را محدود نگه میداریم.
۴. پس از تغییر رویه چه شاخصهایی را مقایسه میکنید؟
Duration، CPU، Logical Reads، Cardinality Estimate، Memory Grant، Spill، Waitها و Planهای Query Store باید با Baseline و چند توزیع پارامتر مقایسه شوند.
۵. طرح بازگشت مناسب چیست؟
متن نسخه قبلی، ترتیب بازگردانی وابستگیها، مجوزها، آزمون سلامت و معیار تصمیم Rollback از پیش آماده میشوند. بازگشت نباید در زمان حادثه از حافظه طراحی شود.
چکلیست نهایی
- Syntax و نسخه پشتیبان EXEC بررسی شد.
- Schema، نام و انواع پارامتر صریحاند.
- رفتار NULL و مقادیر مرزی تست شده است.
- مجوزها حداقلی و قابل ممیزیاند.
- خطا و تراکنش مسیر موفق و ناموفق را پوشش میدهند.
- Baseline و نتیجه آزمون Performance ثبت شده است.
- اسکریپت انتشار و بازگشت در کنترل نسخه قرار دارد.
جمعبندی
EXEC زمانی ارزش واقعی ایجاد میکند که همراه قرارداد روشن، امنیت حداقلی، مدیریت خطا و سنجش کارایی استفاده شود. مثالهای این مقاله الگوهای پایه تا حرفهای را پوشش دادند؛ آنها را با Schema، نوع داده و سیاست انتشار پروژه خود تطبیق دهید و پیش از Production در محیط آزمایشی اجرا کنید.
برای مرور همه دستورات این مجموعه، به مقاله مادر Stored Procedures در SQL Server بازگردید.