آموزش تابع LEAD در SQL Server با ۱۰ مثال عملی | آموزش جامع و مثال عملی

آموزش تابع LEAD در SQL Server با ۱۰ مثال عملی

توسط admin | گروه SQL Server | 1405/04/29

نظرات 0

آموزش جامع تابع LEAD در SQL Server با ۱۰ مثال عملی

مقدمه

تابع LEAD یکی از ابزارهای مهم تحلیل پنجره‌ای در Microsoft SQL Server است و برای خواندن مقدار ردیف آینده برای موعد بعدی، فاصله زمانی و تشخیص انتهای زنجیره به کار می‌رود. این مقاله از نحو پایه شروع می‌کند و سپس رفتار مرزی، NULL، ترتیب قطعی، سناریوی سازمانی و بهینه‌سازی را با Queryهای مستقل بررسی می‌کند.

مزیت تابع پنجره‌ای این است که جزئیات هر ردیف حفظ می‌شود و هم‌زمان محاسبه‌ای مبتنی بر ردیف‌های مرتبط در دسترس قرار می‌گیرد. این ویژگی گزارش‌سازی را از Self Joinهای شکننده، Cursor و پردازش تکراری در لایه برنامه بی‌نیاز می‌کند، مشروط بر اینکه ترتیب و پارتیشن درست تعریف شوند.

برای مشاهده نقشه کامل این خانواده، راهنمای جامع توابع تحلیلی و پنجره‌ای SQL Server را نیز مطالعه کنید.

تعریف و منطق تابع LEAD

LEAD برای خواندن مقدار ردیف آینده برای موعد بعدی، فاصله زمانی و تشخیص انتهای زنجیره استفاده می‌شود. پنجره تحلیل با OVER تعریف می‌گردد و موتور SQL Server نتیجه را بدون حذف ردیف‌های اصلی محاسبه می‌کند. این رفتار تفاوت بنیادی آن با GROUP BY است.

در طراحی حرفه‌ای باید سؤال کسب‌وکار به سه جزء تبدیل شود: مرز گروه با PARTITION BY، ترتیب منطقی با ORDER BY و در صورت نیاز Frame. سپس نوع خروجی، رفتار NULL و مقادیر هم‌رتبه بررسی می‌شود. LAG گذشته را می‌بیند، اما LEAD بدون جابه‌جایی فیزیکی ردیف، مقدار آینده را بازمی‌گرداند.

نحو استاندارد

LEAD ( scalar_expression [, offset] [, default] ) [ IGNORE NULLS | RESPECT NULLS ] OVER ( [ PARTITION BY ... ] ORDER BY ... )

پارامترها و اجزای مهم

  • scalar_expression یا عبارت مرتب‌سازی باید نوع داده مناسب و قابل پیش‌بینی داشته باشد.
  • PARTITION BY اختیاری است و در نبود آن، تمام ردیف‌های ورودی یک پارتیشن هستند.
  • ORDER BY ترتیب محاسبه را تعیین می‌کند و بهتر است با یک tie-breaker یکتا کامل شود.
  • Frame در توابع وابسته به مرز پنجره باید صریح نوشته شود؛ رفتار پیش‌فرض همیشه معادل کل پارتیشن نیست.
  • NULL باید با سیاست روشن مدیریت شود؛ صفر، رشته خالی و NULL معنای یکسان ندارند.

نوع خروجی

نوع خروجی LEAD: هم‌نوع با scalar_expression و در صورت امکان Nullable. در لایه گزارش، تبدیل به decimal یا متن فقط برای نمایش انجام شود و مقدار خام برای محاسبات بعدی نگه داشته شود.

نکته کلیدی: نبود default باعث NULL در انتهای پارتیشن می‌شود و نباید آن را همیشه خطا فرض کرد.

مثال‌های عملی مستقل و قابل اجرا

مثال 1: خواندن مقدار بعدی در یک توالی

در ساده‌ترین حالت، LEAD مبلغ بعدی را طبق ترتیب قطعی تاریخ و شناسه برمی‌گرداند.

WITH Sales AS
(
    SELECT *
    FROM (VALUES
        (1, N'فروش', CAST('2026-01-01' AS date), 100),
        (2, N'فروش', CAST('2026-02-01' AS date), 140),
        (3, N'فروش', CAST('2026-03-01' AS date), 120),
        (4, N'پشتیبانی', CAST('2026-01-01' AS date), 80),
        (5, N'پشتیبانی', CAST('2026-02-01' AS date), 110)
    ) AS V(Id, Department, SaleDate, Amount)
)
SELECT Id, SaleDate, Amount,
       LEAD(Amount) OVER (ORDER BY SaleDate, Id) AS LeadAmount
FROM Sales
WHERE Department = N'فروش'
ORDER BY SaleDate, Id;
تاریخمبلغمبلغ بعدی
2026-01-01100140
2026-02-01140120

نکته کاربردی: وجود Id در ORDER BY نتیجه را حتی در تاریخ‌های تکراری پایدار می‌کند.

مثال 2: شروع دوباره محاسبه در هر واحد

با PARTITION BY زنجیره هر دپارتمان مستقل می‌شود و مقدار واحد دیگر وارد مقایسه نمی‌گردد.

WITH Sales AS
(
    SELECT *
    FROM (VALUES
        (1, N'فروش', CAST('2026-01-01' AS date), 100),
        (2, N'فروش', CAST('2026-02-01' AS date), 140),
        (3, N'فروش', CAST('2026-03-01' AS date), 120),
        (4, N'پشتیبانی', CAST('2026-01-01' AS date), 80),
        (5, N'پشتیبانی', CAST('2026-02-01' AS date), 110)
    ) AS V(Id, Department, SaleDate, Amount)
)
SELECT Department, SaleDate, Amount,
       LEAD(Amount) OVER (PARTITION BY Department ORDER BY SaleDate, Id) AS AdjacentAmount
FROM Sales
ORDER BY Department, SaleDate, Id;
واحدمبلغمجاور
فروش100140
فروش140120

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

مثال 3: استفاده از فاصله دو ردیفی و مقدار پیش‌فرض

پارامتر offset می‌تواند بیش از یک باشد و default نبود ردیف کافی را به مقدار کنترل‌شده تبدیل می‌کند.

WITH Sales AS
(
    SELECT *
    FROM (VALUES
        (1, N'فروش', CAST('2026-01-01' AS date), 100),
        (2, N'فروش', CAST('2026-02-01' AS date), 140),
        (3, N'فروش', CAST('2026-03-01' AS date), 120),
        (4, N'پشتیبانی', CAST('2026-01-01' AS date), 80),
        (5, N'پشتیبانی', CAST('2026-02-01' AS date), 110)
    ) AS V(Id, Department, SaleDate, Amount)
)
SELECT SaleDate, Amount,
       LEAD(Amount, 2, 0) OVER (ORDER BY SaleDate, Id) AS TwoRowsAway
FROM Sales
WHERE Department = N'فروش'
ORDER BY SaleDate, Id;
ردیفمبلغدو ردیف فاصله
1100120
31200

نکته کاربردی: نوع مقدار پیش‌فرض باید با عبارت اصلی سازگار باشد تا تبدیل ضمنی نامطلوب رخ ندهد.

مثال 4: محاسبه تغییر مبلغ

تفاضل مقدار جاری و مجاور، روند رشد یا افت را بدون اتصال جدول به خودش نشان می‌دهد.

WITH Sales AS
(
    SELECT *
    FROM (VALUES
        (1, N'فروش', CAST('2026-01-01' AS date), 100),
        (2, N'فروش', CAST('2026-02-01' AS date), 140),
        (3, N'فروش', CAST('2026-03-01' AS date), 120),
        (4, N'پشتیبانی', CAST('2026-01-01' AS date), 80),
        (5, N'پشتیبانی', CAST('2026-02-01' AS date), 110)
    ) AS V(Id, Department, SaleDate, Amount)
)
SELECT SaleDate, Amount,
       LEAD(Amount) OVER (ORDER BY SaleDate) - Amount AS AmountChange
FROM Sales
WHERE Department = N'فروش'
ORDER BY SaleDate, Id;
تاریخمبلغتغییر
2026-02-01140-20
2026-03-01120NULL

نکته کاربردی: NULL در مرز پارتیشن اطلاعات مفیدی درباره نبود همسایه است و می‌توان آن را آگاهانه نگه داشت.

مثال 5: فیلتر کردن نتیجه پنجره با CTE

تابع پنجره‌ای مستقیماً در WHERE همان SELECT مجاز نیست؛ ابتدا مقدار محاسبه و سپس فیلتر می‌شود.

WITH Sales AS
(
    SELECT *
    FROM (VALUES
        (1, N'فروش', CAST('2026-01-01' AS date), 100),
        (2, N'فروش', CAST('2026-02-01' AS date), 140),
        (3, N'فروش', CAST('2026-03-01' AS date), 120),
        (4, N'پشتیبانی', CAST('2026-01-01' AS date), 80),
        (5, N'پشتیبانی', CAST('2026-02-01' AS date), 110)
    ) AS V(Id, Department, SaleDate, Amount)
)
, W AS
(
    SELECT *, LEAD(Amount) OVER (ORDER BY SaleDate, Id) AS AdjacentAmount
    FROM Sales
    WHERE Department = N'فروش'
)
SELECT Id, SaleDate, Amount, AdjacentAmount
FROM W
WHERE AdjacentAmount IS NOT NULL
  AND Amount < AdjacentAmount;
شناسهمبلغمجاور
3120140

نکته کاربردی: این الگو هم از نظر ترتیب منطقی SQL صحیح است و هم خوانایی شرط را افزایش می‌دهد.

مثال 6: رفتار NULL و IGNORE NULLS

در SQL Server 2022 می‌توان از نزدیک‌ترین مقدار غیرNULL عبور نکرد و NULLها را هنگام جست‌وجوی همسایه نادیده گرفت.

WITH D AS
(SELECT * FROM (VALUES (1,10),(2,NULL),(3,30),(4,NULL)) V(Id,Value))
SELECT Id, Value,
       LEAD(Value) RESPECT NULLS OVER (ORDER BY Id) AS RespectNulls,
       LEAD(Value) IGNORE NULLS OVER (ORDER BY Id) AS IgnoreNulls
FROM D
ORDER BY Id;
Idمقداربا نادیده‌گیری NULL
330NULL
4NULLNULL

نکته کاربردی: برای IGNORE NULLS نصب به‌روزرسانی‌های تجمعی SQL Server 2022، به‌ویژه اصلاحات پس از CU4، بررسی شود.

مثال 7: قطعی کردن ترتیب در داده هم‌زمان

وقتی چند رویداد زمان مساوی دارند، شماره رویداد به‌عنوان tie-breaker نتیجه را قطعی می‌کند.

WITH Events AS
(SELECT * FROM (VALUES (101,CAST('2026-07-20T10:00:00' AS datetime2),N'ثبت'),(102,CAST('2026-07-20T10:00:00' AS datetime2),N'تأیید'),(103,CAST('2026-07-20T10:05:00' AS datetime2),N'ارسال')) V(EventId,EventTime,State))
SELECT EventId, State,
       LEAD(State,1,N'مرز') OVER (ORDER BY EventTime, EventId) AS AdjacentState
FROM Events
ORDER BY EventTime, EventId;
رویدادوضعیتوضعیت مجاور
102تأییدارسال

نکته کاربردی: ORDER BY فقط با زمان، در این داده قطعی نیست؛ EventId مشکل را برطرف می‌کند.

مثال 8: محاسبه فاصله رخدادها

در مانیتورینگ سامانه می‌توان فاصله زمانی تا رخداد مجاور را برای کشف وقفه یا SLA محاسبه کرد.

WITH E AS
(SELECT * FROM (VALUES (1,CAST('2026-07-20T08:00:00' AS datetime2)),(2,CAST('2026-07-20T08:07:00' AS datetime2)),(3,CAST('2026-07-20T08:25:00' AS datetime2))) V(Id,EventDate))
SELECT Id, EventDate,
       DATEDIFF(day, EventDate, LEAD(EventDate) OVER (ORDER BY EventDate)) AS GapDays
FROM E
ORDER BY EventDate;
رخدادزمانفاصله روز
208:070
308:250

نکته کاربردی: برای فاصله دقیقه کافی است datepart تابع DATEDIFF از day به minute تغییر کند.

مثال 9: اصلاح روش اشتباه Self Join

اتصال براساس Id-1 در داده حذف‌شده یا شناسه غیردنباله‌ای می‌شکند؛ پنجره براساس ترتیب واقعی درست‌تر است.

WITH D AS
(SELECT * FROM (VALUES (10,100),(30,150),(90,130)) V(Id,Amount))
SELECT Id, Amount,
       LEAD(Amount) OVER (ORDER BY Id) AS CorrectAdjacent
FROM D
ORDER BY Id;
Idمبلغمجاور صحیح
30150130

نکته کاربردی: تابع پنجره‌ای به پیوستگی مصنوعی شناسه وابسته نیست و منطق کسب‌وکار را مستقیم بیان می‌کند.

مثال 10: الگوی ایندکس برای ورودی بزرگ

این مثال موقت نشان می‌دهد چگونه کلید پارتیشن و ترتیب و ستون INCLUDE برای Query پنجره‌ای آماده می‌شوند.

CREATE TABLE #MonthlySales
(DepartmentId int NOT NULL, SaleDate date NOT NULL, Id bigint NOT NULL, Amount decimal(12,2) NOT NULL);
INSERT #MonthlySales VALUES (1,'2026-01-01',1,100),(1,'2026-02-01',2,140),(2,'2026-01-01',3,80);
CREATE INDEX IX_MonthlySales_Window ON #MonthlySales(DepartmentId,SaleDate,Id) INCLUDE(Amount);
SELECT DepartmentId, SaleDate, Amount,
       LEAD(Amount) OVER (PARTITION BY DepartmentId ORDER BY SaleDate,Id) AS AdjacentAmount
FROM #MonthlySales;
DROP TABLE #MonthlySales;
ایندکسترتیب کلیدستون پوششی
IX_MonthlySales_WindowDepartmentId,SaleDate,IdAmount

نکته کاربردی: اثر واقعی ایندکس را با Actual Execution Plan، تعداد Sort و Logical Reads مقایسه کنید.

خطاهای رایج و روش اصلاح

  • نبود default باعث NULL در انتهای پارتیشن می‌شود و نباید آن را همیشه خطا فرض کرد.
  • استفاده از ORDER BY غیرقطعی؛ یک کلید یکتا به انتهای ترتیب اضافه کنید.
  • اعمال WHERE در سطح اشتباه؛ ابتدا مشخص کنید فیلتر باید جمعیت پنجره را عوض کند یا فقط خروجی را محدود نماید.
  • تبدیل نوع ضمنی در عبارت یا مقدار پیش‌فرض؛ نوع‌ها را صریح و سازگار تعریف کنید.
  • فرض اینکه خروجی تابع همیشه deterministic است؛ مستندات و ترتیب داده را برای امکان تکرار نتیجه کنترل کنید.

ملاحظات کارایی و تحلیل Execution Plan

مرتب‌سازی پوشش‌داده‌شده و محدود کردن ستون‌های ورودی، مصرف حافظه Window Operator را کم می‌کند.

ابتدا با SET STATISTICS IO, TIME ON خط مبنا بگیرید و Actual Execution Plan را ذخیره کنید. وجود Sort بزرگ، Memory Grant بیش از نیاز یا هشدار Spill به tempdb نشانه‌ای است که ترتیب داده، برآورد Cardinality یا ظرفیت حافظه باید بررسی شود.

ایندکس پیشنهادی معمولاً با ستون‌های فیلتر برابری و PARTITION BY آغاز می‌شود، سپس ستون‌های ORDER BY و tie-breaker می‌آیند و ستون خروجی در INCLUDE قرار می‌گیرد. این یک نسخه عمومی است؛ ترتیب نهایی باید با Query واقعی، Selectivity و هزینه نگهداری DML سنجیده شود.

چند تابع پنجره‌ای با Window Specification یکسان را در یک SELECT بنویسید تا Optimizer امکان استفاده مشترک از ترتیب را داشته باشد. تفاوت کوچک در ترتیب صعودی، نزولی یا Frame می‌تواند عملگر جداگانه بسازد؛ پس طرح اجرا را پس از هر تغییر مقایسه کنید.

فیلتر دوره زمانی و ستون‌های غیرضروری را در جایی اعمال کنید که معنای تحلیل حفظ شود. کاهش عرض و تعداد ردیف ورودی، مصرف حافظه و I/O را کم می‌کند، ولی بهینه‌سازی نباید جمعیت آماری مورد نیاز را ناخواسته حذف کند.

بهترین روش‌ها

  1. تعریف کسب‌وکار را پیش از نوشتن Query به پارتیشن، ترتیب و Frame تبدیل کنید.
  2. در ORDER BY از کلید یکتای پایدار برای شکستن tie استفاده کنید.
  3. NULL، پارتیشن تک‌ردیفی، مقادیر مساوی و مرزهای ابتدا و انتها را آزمون کنید.
  4. نوع داده خروجی و گرد کردن را مستند کنید و گرد کردن را تا لایه نمایش عقب بیندازید.
  5. Execution Plan، IO، CPU، Memory Grant و tempdb Spill را قبل و بعد از تغییر ثبت کنید.
  6. ایندکس را براساس بار کاری کامل طراحی کنید؛ بهبود SELECT نباید هزینه INSERT و UPDATE را نادیده بگیرد.

سؤالات متداول

۱. تابع LEAD دقیقاً چه مسئله‌ای را حل می‌کند؟

این تابع برای خواندن مقدار ردیف آینده برای موعد بعدی، فاصله زمانی و تشخیص انتهای زنجیره طراحی شده است. مزیت اصلی آن حفظ ردیف‌های جزئی در کنار محاسبه تحلیلی است و برخلاف GROUP BY الزاماً تعداد ردیف‌ها را کاهش نمی‌دهد.

۲. اجزای OVER در LEAD چه نقشی دارند؟

PARTITION BY مرز گروه منطقی را تعیین می‌کند و ORDER BY توالی تحلیل را می‌سازد. در توابع حساس به Frame، عبارت ROWS نیز دامنه ردیف‌های قابل مشاهده از هر ردیف را مشخص می‌کند.

۳. آیا استفاده از LEAD هزینه توسعه گزارش را کم می‌کند؟

در بسیاری از گزارش‌ها حذف Self Join، Cursor یا کد میانی باعث Query کوتاه‌تر و نگهداری ساده‌تر می‌شود. برای برآورد تجاری باید حجم داده، SLA، دفعات اجرا و هزینه ایندکس نیز اندازه‌گیری شود.

۴. چه زمانی بازطراحی Queryهای قدیمی با LEAD ارزش اقتصادی دارد؟

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

۵. تفاوت LEAD با گزینه نزدیک آن چیست؟

LAG گذشته را می‌بیند، اما LEAD بدون جابه‌جایی فیزیکی ردیف، مقدار آینده را بازمی‌گرداند. انتخاب نهایی باید براساس تعریف دقیق خروجی، رفتار tie، NULL و مرز پنجره انجام شود.

۶. برای پیاده‌سازی حرفه‌ای LEAD در پروژه سازمانی چه خدمتی لازم است؟

ابتدا Query و Execution Plan واقعی بررسی، سپس ایندکس و آزمون صحت روی داده مرزی طراحی می‌شود. خدمات تحلیل، آموزش تیم یا بهینه‌سازی پروژه می‌تواند این مراحل را با معیار پذیرش روشن اجرا کند.

۷. رایج‌ترین خطا در LEAD چیست؟

نبود default باعث NULL در انتهای پارتیشن می‌شود و نباید آن را همیشه خطا فرض کرد. علاوه بر آن، فیلتر کردن داده پیش از محاسبه می‌تواند جمعیت آماری یا همسایه‌های پنجره را ناخواسته تغییر دهد.

۸. چگونه Performance تابع LEAD را بسنجیم؟

مرتب‌سازی پوشش‌داده‌شده و محدود کردن ستون‌های ورودی، مصرف حافظه Window Operator را کم می‌کند. Actual Execution Plan، SET STATISTICS IO/TIME، Memory Grant، هشدار Spill و تعداد ردیف واقعی ابزارهای اصلی اندازه‌گیری هستند.

۹. بهترین روش نوشتن LEAD چیست؟

ترتیب قطعی با tie-breaker یکتا، پارتیشن متناسب با منطق کسب‌وکار، تبدیل نوع صریح و آزمون NULL و مرزها را رعایت کنید. ابتدا صحت و سپس سرعت را بهینه سازید.

۱۰. LEAD با کدام نسخه‌های SQL Server سازگار است؟

SQL Server 2012؛ گزینه IGNORE NULLS از SQL Server 2022. در Azure SQL نیز اصل قابلیت در دسترس است، اما Compatibility Level و اصلاحات تجمعی مرتبط با IGNORE NULLS یا Optimizer باید بررسی شود.

سؤالات مصاحبه و پاسخ کوتاه

چرا LEAD یک تابع پنجره‌ای است؟

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

چرا ORDER BY باید قطعی باشد؟

اگر چند ردیف کلید ترتیب یکسان داشته باشند، موتور می‌تواند ترتیب داخلی متفاوتی انتخاب کند. افزودن کلید یکتا نتیجه را تکرارپذیر و آزمون‌پذیر می‌سازد.

تفاوت WHERE داخلی و خارجی چیست؟

WHERE داخلی جمعیت ورودی پنجره را کاهش می‌دهد؛ WHERE خارجی فقط خروجی محاسبه‌شده را فیلتر می‌کند. بنابراین این دو از نظر معنا معادل نیستند.

چه چیزی را در Execution Plan بررسی می‌کنید؟

Sort، Segment، Sequence Project یا Window Aggregate، Memory Grant، Spill به tempdb، برآورد ردیف و هم‌راستایی ایندکس با PARTITION و ORDER BY بررسی می‌شوند.

آزمون واحد مناسب چیست؟

پارتیشن خالی یا تک‌ردیفی، NULL، مقدار تکراری، مرز ابتدا و انتها، ترتیب هم‌رتبه و داده حجیم باید پوشش داده شوند تا هم صحت و هم پایداری سنجیده شود.

چک‌لیست نهایی

  • نحو LEAD و Compatibility Level کنترل شده است.
  • پارتیشن دقیقاً مرز موجودیت کسب‌وکار است.
  • ترتیب با tie-breaker یکتا قطعی شده است.
  • Frame در صورت اثرگذاری صریح نوشته شده است.
  • حالت NULL، tie و پارتیشن کوچک آزمون شده است.
  • فیلتر داخلی و خارجی آگاهانه انتخاب شده‌اند.
  • طرح اجرا و آمار IO/TIME ثبت شده‌اند.
  • لینک مقاله مادر و نمونه‌ها پیش از انتشار کنترل شده‌اند.

جمع‌بندی

تابع LEAD وقتی ارزشمند است که برای خواندن مقدار ردیف آینده برای موعد بعدی، فاصله زمانی و تشخیص انتهای زنجیره به یک بیان مجموعه‌محور، خوانا و قابل بهینه‌سازی نیاز داشته باشیم. صحت ترتیب و پارتیشن مهم‌تر از کوتاهی ظاهری Query است و سنجش کارایی باید با داده و Plan واقعی انجام شود.

برای مقایسه LEAD با هفت تابع دیگر، به مقاله مادر توابع Analytic و Window در SQL Server بازگردید.

 

0 نظر

نظر محترم شما در مورد مقاله های وب سایت برنامه نویسی و پایگاه داده

نظرات محترم شما در خدمات رسانی بهتر ما را یاری می نمایند. لطفا اگر مایل بودید یک نظر ما را مهمان فرمائید. آدرس ایمیل و وب سایت شما نمایش داده نخواهد شد.

حرف 500 حداکثر

اطلاعات تماس

  • آدرس:اصفهان-خیابان ام کلثوم غربی - بعد خیابان تخم چی - بیست متر بعد از پیتزا ننه شب - کوچه تعمیر گاه سمار زغالی - پلاک 354 - درب مشکی - طبقه هفتم
  • آدرس ایمیل:najafzade@gmail.com
  • وب سایت:http://www.a00b.com/
  • تلفن ثابت:(+98)9131253620
  • تلفن همراه:09131253620