<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های رحمان</title>
        <link>https://virgool.io/feed/@rahtav68</link>
        <description></description>
        <language>fa</language>
        <pubDate>2026-04-14 14:58:24</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/930546/avatar/avatar.png?height=120&amp;width=120</url>
            <title>رحمان</title>
            <link>https://virgool.io/@rahtav68</link>
        </image>

                    <item>
                <title>پایتونی – Underscores</title>
                <link>https://virgool.io/@rahtav68/%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86%DB%8C-%E2%80%93-underscores-xmobvkc7rnr4</link>
                <description>خط زیر، یا بهتره بگیم Underscore ها چه از یکی (single) و چه از نوع دابل (double) در پایتون معنای متفاوتی دارند و حتی جای قرار گیریشون هم معنی متفاوتی ایجاد میکنه. هر چند برخی از این معانی بیشتر حالت قراردادی داره تا یک الزام باشه. 5 حالت underscore که قراره در ادامه بررسی شوند به این صورت هستن:Single Leading Underscore : _var_Single Trailing Underscore : varDouble Leading Underscore : __varDouble Leading and Trailing Underscore : var_ : Single Underscore1- Single Leading Underscore یا  “var_”پایتون برخلاف زبان های برنامه نویسی مثل java الزامی برای تعیین private یا public بودن متغیرها نداره ولی به صورت قراردادی و بین برنامه نویسان پایتون مرسومه که در صورت استفاده از یک underscore قبل از متغیر اون متغیر private هستش. یعنی اینه که “برنامه نویس عزیز، رفیق، دوست من؛ این متغیر برای استفاده عمومی نیست ازش لطفا استفاده نکن”. به عبارت دیگه یعنی ” این متغیر private هستش خواهشا استفاده نفرمایید حتی شما دوست عزیز”. هر چند که شما دسترسی دارید می تونید تغییر بدید متغیر رو یا استفاده کنید. به طور مثال:class Test:
    def __init__(self):
        self.foo = 11
        self._bar = 232- Single Trailing Underscore یا  “_var”گاهی پیش میاد که می خواهیم از نام هایی که قبلا در پایتون استفاده شده، استفاده کنیم. ولی در صورت استفاده با خطای invalid syntax مواجه می شویم. اگر اصرار به استفاده از اون عبارت داریم یک راهکار اینه که از underscore پس از نام عبارت مورد نظرمون استفاده کنیم. به طور مثال:def make_object(name, class): //SyntaxError: &quot;invalid syntax&quot;
    pass
def make_object(name, class_):
    passداستان underscore ها در حالت دابل کمی متفاوت تره!به دو underscore پشت سر هم dunder هم میگن!به طور مثال به __init__ میگن dunder init و البته به var__ هم میگن dunder var3- Double Leading Underscore یا  “var__”مفسر پایتون در صورت استفاده از دو underscore به صورت پیشوند؛ اون عبارت رو چه متغیر باشه و چه متود (method) برای جلوگیری از تداخل نام ها (در subclass) تغییر نام میده. به این عمل name mangling می گویند. به طور مثال:class Test:
    def __init__(self):
        self.foo = 11
        self._bar = 23
        self.__baz = 23

t = Test()
print(dir(t))در خروجی اگه کمی دقت کنید baz__ رو پیدا نمی کنید! چون پایتون نام رو به Test__baz_ تغییر داده.یا مثال زیر:class ManglingTest:
    def __init__(self):
        self.__mangled = &#039;hello&#039;
    def get_mangled(self):
        return self.__mangledدر صورتی که mangled__ به صورت مستقیم فراخوانی کنیم با خطا AttributeError رو به رو میشیم.ManglingTest().get_mangled() #&#039;hello&#039;
ManglingTest().__mangled #AttributeError: &quot;&#039;ManglingTest&#039; object has no attribute &#039;__mangled&#039;&quot;یه موضوع جالب این که شما می تونید با تغییر نام متغیر با توجه به خروجی پایتون به مقدار مورد نظر دسترسی پیدا کنید. به طور مثال در کد زیر MangledGlobal__mangled_ که به صورت global تعریف شده در خروجی mangled__ تغییر نام به MangledGlobal__mangled_ خواهیم داشت و در نتیجه خروجی متغیر بدون خطا خواهیم داشت._MangledGlobal__mangled = 23
class MangledGlobal:
    def test(self):
        return __mangled
m = MangledGlobal().test() #23
print(m)4- Double Leading and Trailing Underscore یا  “__var__”متود ها (method) با دو underscore در پیش و دو underscore در پس، به dunder methods یا magic methods معروفن!این متود ها رزرو شده هستن و از پیش ساخته البته شما میتونید متودهای خودتون رو با همین شرایط بنویسید. به طور مثال:class PrefixPostfixTest:
    def __init__(self):
        self.__bam__ = 42
p = PrefixPostfixTest().__bam__

print(p) #425- Single Underscore یا  “_”اگه اسم متغیری مهم نباشه یا موقتی باشه از _ استفاده میشه. به طور مثال:for _ in range(5):
    print(&#039;Welcome to datasense {}&#039;.format(_))یک کاربرد دیگه برای unpack کردن هستش. وقتی که براتون مقدار یه لیست مهم نیست و و لازمه فقط مقدار رو به یه متغیری پاس بدید میتونید از استفاده کنید. به طور مثال:car = (&#039;red&#039;, &#039;auto&#039;, 12, 3812.4)
color, _, _, mileage = car</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Thu, 25 Dec 2025 15:32:44 +0330</pubDate>
            </item>
                    <item>
                <title>توابع خانواده ALL</title>
                <link>https://virgool.io/@rahtav68/%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%D8%AE%D8%A7%D9%86%D9%88%D8%A7%D8%AF%D9%87-all-ne4psdsq0phu</link>
                <description>توابع خانواده ALL، جز توابع گروه فیلترها به حساب می آیند. عملکرد کلی این توابع به این صورته که فیلترهای جدول یا ستون انتخابی یا غیر انتخابی رو حذف می کنند. به عبارت دقیق تر از هر نوع فیلتر که به صورت غیر صریح (implicit) یعنی توسط شما نوشته نشده باشه صرف نظر خواهد شد. از جمله مهم ترین توابع این خانواده عبارتند از :ALLALLEXCEPTALLSELECTEDو همچنین از جمله مهم ترین کاربردهای این توابع در محاسبات رایج، می توان به موارد زیر اشاره کرد:محاسبه درصد از کل (percentage of total)مقایسه های دوره ای (period comparisons)جمع های تجمعی (running totals)فیلترهای implicit و explicitدر این پست فیلتر implicit، به فیلترهایی گفته میشه که اطلاعات جداول با استفاده از قرار دادن ستون ها در ویژوال ها (visuals) و یا به واسطه رابطه بین جداول، فیلتر می شوند. به فیلترهای implicit فیلتر context هم گفته می شود.اما فیلتر explicit، فیلترهایی هستند که توسط نویسنده فرمول (DAX) نوشته می شود و توسط شما در فرمول تصریح می شود. به طور مثال اعمال فیلتر اعداد بیشتر از ۱۰ در بازه اعداد ۱ تا ۲۰ یک نوع قیلتر explicit است.نحوه عملکرد ALLساختار کلی تابع ALL، شامل آرگومان های “نام جدول” و “نام ستون” هستش که به شکل زیره:ALL( [&lt;table&gt; | &lt;column&gt;[, &lt;column&gt;[, &lt;column&gt;[,…]]]] )اگر دقت کنید همه آرگومان های تابع ALL اختیاری هستن و نیازی به ورود اطلاعات جدول و ستون نیست. ولی کم تر بر خواهید خورد که بدون استفاده از آرگومان از این تابع استفاده کنید و توصیه هم همیشه این است که به صورت صریح نام جدول یا ستون ها رو وارد کنید.همونطور که گفته شد این تابع در حقیقت یک حذف کننده فیلتره که فیلتر هایی به غیر از مواردی که شما به طور صریح در DAX نوشته اید رو حذف خواهد کرد. برای آشنایی بهتر با عملکرد و نحوه استفاده از این تابع با یک مثال به توضیح جزییات این تابع می پردازیم. فرض کنید جدولی با اطلاعات زیر در اختیار داریم.تابع ALL در صورتی که بر روی جدول مورد نظر اعمال شود باعث حذف فیلترهای غیر صریح (implicit) جدول و ارتباط های اون جدول میشه. در فرمول زیر، در صورت اعمال ALL بر روی جدول fact تمام فیلتر های جدول fact را که شامل دو مورد زیر می شود در محاسبات صرف نظر می شوند.فیلتر جدول DimDate که به واسطه رابطه ۱ به چند بر روی fact اعمال می شودفیلتر دسته بندی visual که در این مثال یک جدول استSum of all (Fact) =
CALCULATE ( SUM ( &#039;fact&#039;[Qty] ), ALL ( &#039;fact&#039; ) )همچنین با اعمال فرمول زیر بر روی جدول DimDate تمامی فیلترهای اعمال شده که شامل ستون های جدول DimDate و در مثال ما، ستون ماه می باشد حذف می شود.Sum of all (Dim Date) =
CALCULATE ( SUM ( &#039;fact&#039;[Qty] ), ALL ( DimDate ) )که خروجی به شکل زیر خواهد بود :شماره یک؛ در measure اول با نام Sum of all (Dim Date)، با توجه به داده هایی که در دو جدول داریم مجموع تعدادی مقادیر رو با حذف فیلترهای جدول DimDate محاسبه می کند. در خروجی این فرمول، مقادیر ستون Qty بدون اعمال فیلتر ستون Month جمع خواهند شد. به همین دلیل است که در تصویر بالا این مقدار برای همه ماه ها تکرار شده است و به طور مثال مقدار ۵۸ محصول B مقدار مجموع Qty همه ماه ها می باشد در صورتی که مقدار فروردین ماه آن ۱۹ می باشد. در نتیجه با اعمال ALL بر روی DimDate از اعمال فیلتر ماه بر روی محاسبات صرف نظر می شود.شماره دو؛ فرمول دوم (ستون دوم) با نام Sum of all (Fact) که در آن تابع ALL بر روی جدول fact اعمال گردیده باعث شده تا علاوه بر حذف فیلتر ماه که توسط رابطه یک به چند جدول DimDate اعمال می شود، تمامی فیلترهای ستون های جدول fact نیز حذف شوند. در نتیجه مجموع کل اعداد ستون Qty باز گردانده می شود و گروه بندی های ویژوال تاثیری بر روی محاسبات نمی گذارد.اگر مثال بالا رو با کمی تغییرات و اعمال یک فیلتر explicit انجام دهیم، فیلتر تصریحی ما حذف نمی شود و در محاسبات در نظر گرفته می شود. به عنوان نمونه با اعمال یک تغییر در فیلتر فرمول های بالا خروجی به صورت زیر خواهد شد:Sum (15 or more) = 
CALCULATE(SUM(&#039;fact&#039;[Qty]),&#039;fact&#039;[Qty] &gt;= 15)Sum of all (Explicit) Sales = 
CALCULATE([Sum (15 or more)], ALL(&#039;fact&#039;))در فرمول اول، تمام مقادیر بزرگتر از ۱۵ رو جمع زدیم که به طور مثال برای فروردین ماه مقدار ۱۹ خواهد شد (شماره ۱) و در فرمول دوم با توجه به محاسبات فرمول اول یعنی استفاده از measure اول در فرمول دوم و اعمال فیلتر ALL بر روی fact خواهیم دید که فیلتر مقادیر بزرگتر از ۱۵ (explicit) در این فرمول اعمال میشه و فیلترهای implicit که در مثال قبل آوریم از محاسبات حذف می شوند. در نتیجه مقدار ۱۱۵ در این محاسبات مجموع کل ستون Qty بدون در نظر گرفتن نوع کالا و گروه کالا و ماه می باشد ولی تنها مقادیر بزرگتر از ۱۵ در این ستون جمع شده اند.محاسبه Percent Of Totalهمونطور که گفته شد یکی از کاربردهای خانواده ALL، محاسبه Percent Of Total است که در این بخش به بررسی یک مثال ساده از نحوه محاسبه آن می پردازیم. از جمله قابلیت هایی که این تابع در محاسبات Percent Of Total دارا است، امکان محاسبه Percent Of Total در بین دسته های تقسیمی به طور مثال گروه کالا یا ماه می باشد.PercentTotal =
SUM ( &#039;fact&#039;[Qty] ) / CALCULATE ( SUM ( &#039;fact&#039;[Qty] ), ALL ( &#039;fact&#039;[Product] ) )در صورت اعمال فرمول بالا، درصد از کل با صرف نظر از اعمال فیلتر محصول بین گروه کالا ها محاسبه می شود. در GROUP-1 مقادیر ۲۵، ۵۸ و ۶۱ تقسیم بر ۱۴۴ که مجموع این سه عدد است می شود و در GROUP-2 مقادیر ۵۷ و ۲۱ به ترتیب تقسیم بر ۷۸ می شوند. و مشخص است که مجموع درصد ها در هر گروه کالا ۱۰۰ درصد خواهد شد.نحوه عملکرد ALLEXCEPTاین تابع هم همانند تابع ALL عمل میکنه با این تفاوت که ستون هایی که نمی خواهیم فیلتر از آن ها برداشته شوند رو در این تابع باید بنویسید. به طور مثال اگر با تابع ALL تعداد ۱۰ ستون از یک جدول ۱۵ ستونی رو انتخاب کنید با تابع ALLEXCEPT تنها نیازه ۵ ستون دیگر رو بنویسید. ساختار کلی تابع به این صورت است:ALLEXCEPT(&lt;table&gt;,&lt;column&gt;[,&lt;column&gt;[,…]])مثال Percent Of Total رو در صورتی که بخواهیم با ALLEXCEPT حل کنیم خواهیم داشت:PercentTotalExcept =
SUM ( &#039;fact&#039;[Qty] )
    / CALCULATE (
        SUM ( &#039;fact&#039;[Qty] ),
        ALLEXCEPT (
            &#039;fact&#039;,
            &#039;fact&#039;[ProductGroup]
        )
    )در این فرمول برعکس ALL که ستون Product انتخاب شد، تمامی ستون های به غیر از Product که می خواهیم گروه بندی براساس آن انجام شود و فیلترهای آن حذف نشود انتخاب می شوند.نحوه عملکرد ALLSELECTEDتابع ALLSELECTED کاملا مشابه ALL می باشد و تفاوت اصلی که ایجاد می کند در فیلترهایی است که توسط کاربر می تواند بر روی آن اعمال شود. ساختار کلی تابع به شکل زیر است:ALLSELECTED([&lt;tableName&gt; | &lt;columnName&gt;[, &lt;columnName&gt;[, &lt;columnName&gt;[,…]]]] )و اما تفاوت ALL و  ALLSELECTED:Sum of allselected (fact) = 
CALCULATE(SUM(&#039;fact&#039;[Qty]),ALLSELECTED(&#039;fact&#039;))تفاوت ALLSELECTED و ALL در تصاویر بالا کاملا مشهوده! این تابع مقادیری که توسط Slicer یا فیلترهای دیگر اعمال می شوند را در محاسبات لحاظ و در حقیقت فیلترها بر روی ستون های انتخاب شده عمل می کنند.محاسبه Running Totalجمع های تجمعی به جمع هایی گفته می شوند که براساس دسته های انتخاب شده جمع صورت می گیرد و مقادیر تجمیع و مقدار دوره قبل با دوره جاری جمع می شود. خروجی تابع ALL SELECTED می تواند تاثیر قابل ملاجظه ای بر محاسبات در این نوع جمع ها داشته باشد که در مثال زیر به بررسی این تفاوت پرداخته ایم:Running Total (all) =
CALCULATE (
    SUM ( &#039;fact&#039;[Qty] ),
    FILTER (
        ALL ( DimDate ),
        DimDate[YearMonth]
            &lt;= MAX ( DimDate[YearMonth] )
    )
)Running Total (allselected) =
CALCULATE (
    SUM ( &#039;fact&#039;[Qty] ),
    FILTER (
        ALLSELECTED ( DimDate ),
        DimDate[YearMonth]
            &lt;= MAX ( DimDate[YearMonth] )
    )
)در مثال بالا تغییر خاصی رو در خروجی مشاهده نمی کنید. ولی در صورتی که فیلتر ماه رو بر روی جدول اعمال کنیم، خواهیم داشت:در صورت استفاده از فیلتر بر روی جدول، تابع ALL با توجه به این که در محاسبات تمام فیلترها اعلامی رو حذف می کند در نتیجه انتخاب ماه هیچ تفاوتی در نتیجه ایجاد نمی کند. به عبارت دیگر در مثال بالا با عدم انتخاب ماه خرداد (۱۳۹۹۰۳) جمع تجمعی در فرمول حاوی ALLSELECTED بین مقادیر انتخاب شده صورت می گیرد و مقدار خرداد ماه در نظر گرفته نمی شود ولی در صورتی که در فرمول حاوی ALL به خاطر حذف شدن همه فیلترهای جدول fact، اعمال فیلتر تاثیری بر محاسبات ندارد.</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Mon, 22 Dec 2025 12:48:58 +0330</pubDate>
            </item>
                    <item>
                <title>پایتون – Context Managers</title>
                <link>https://virgool.io/@rahtav68/%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86-%E2%80%93-context-managers-gx1mk8vsyii9</link>
                <description>حتما پیش اومده در پایتون فایلی رو باز کنید و احتمالا اینکار رو با with انجام دادید. with در تعریف سادش، عبارتیه برای باز و بسته کردن آبجکت ها که به ما با خلاصه کردن عملکرد یک سری الگوهای مدیریت منابع کمک میکنه. به طور مثال برای باز و بسته کردن فایل ها می توان از این عبارت استفاده کرد و این اطمینان رو داشت که فایل های باز شده با این روش به صورت اتوماتیک بعد از اجرا کد بسته شوند. شکل کلی context manager به صورت زیر خواهد بود:with EXPRESSION as VARIABLE:
    BLOCKو به عنوان نمونه ای پر کاربرد می توان به باز و بسته کردن فایل ها اشاره کرد:with open(&#039;hello.txt&#039;, &#039;w&#039;) as f:
    f.write(&#039;hello, world!&#039;)در حقیقت کد بالا به صورت زیر در پایتون اجرا خواهد شد:f = open(&#039;hello.txt&#039;, &#039;w&#039;)
try:
    f.write(&#039;hello, world&#039;)
finally:
    f.close()try:یا به طور مثال در کلاس threading.Lock خواهیم داشت:some_lock = threading.Lock()

with some_lock:
    # Do something...نحوه نوشتن Context Manager (class-based)خود ما هم میتونیم این عملکرد رو در توابع و کلاس ها با استفاده از with داشته باشیم که به context manager معروفه!. context manager یک رویه هوشمندانه است که آبجکت ساخته شده توسط شما، دنبال می کنه. تمام کاری که لازمه برای ساخت یک context manager انجام بشه اینه که از متودهای از پیش ساخته شده (built-in method)، enter و exit  استفاده کنیم. به طور مثال در کد زیر با شروع with متود enter اجرا میشه و عبارت Salam, Rahman  چاپ خواهد شد و بعد از انجام کدهای بلاک داخل with که در مثال زیر پرینت عبارت Good Work هستش، پروسه کد ما با عبارت موجود در متود exit بسته خواهد شد.class Greeter:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print(f&quot;Salam, {self.name}&quot;)
        return self

    def __exit__(self, exc_type, exc_value, exc_tb):
        print(f&quot;Bye Bye, {self.name}&quot;)

with Greeter(&#039;Rahman&#039;):
    print(&#039;Good Work...!&#039;)مثال دیگه همون مثال بازکردن یک فایله که در کد زیر پس از باز کردن فایل مورد نظر در حالت write، مقدار مورد نظر رو در فایل چاپ می کنیم و فایل هم در انتها بسته خواهد شد.class ManagedFile:
    def __init__(self, name):
        self.name = name
    def __enter__(self):
        self.file = open(self.name, &#039;w&#039;)
        return self.file
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()که برای استفاده به صورت زیر عمل می کنیم:with ManagedFile(&#039;hello.txt&#039;) as f:
    f.write(&#039;hello, world!&#039;)
    f.write(&#039;bye now&#039;)روش دیگر (generator-based)البته برای استفاده و نوشتن context manager روش بالا تنها روش نیست و میشه از روش generator-based با استفاده از ماژول contextlib و decorator هم این عملکرد رو پیاده کرد.from contextlib import contextmanager

@contextmanager
def managed_file&#40;name&#41;:
    try:
        f = open(name, &#039;w&#039;)
        yield f
    finally:
        f.close()مثال Indentمثال دیگر از کاربرد context manager: کدی رو با context manager بنویسیم که خروجی، همانند شکل زیر داشته باشیم.hi!
    hello
        bonjour
heyیا شاید به این صورت:++++ hi!
++++++++ hello
++++++++++++ bonjour
++++ heyکه با نوشتن کد زیر نتیجه بالا حاصل شود.with Indenter() as indent:
    indent.print(&#039;hi!&#039;)
    with indent:
        indent.print(&#039;hello&#039;)
        with indent:
            indent.print(&#039;bonjour&#039;)
    indent.print(&#039;hey&#039;)و اما کدی که این خروجی رو خواهد داشت:class Indenter:
    def __init__(self):
        self.level = 0

    def __enter__(self):
        self.level+=1
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.level-=1
        
    def print(self, text):
        print(&#039;\t&#039;*self.level, text)مثال Code Block Timerدر این مثال مدت زمان اجرا کدی که در بلاک with قرار گرفته رو بدست خواهیم آورد:from dataclasses import dataclass, field
import time
from typing import Callable, ClassVar, Dict, Optional

class TimerError(Exception):
    &quot;&quot;&quot;A custom exception used to report errors in use of Timer class&quot;&quot;&quot;

@dataclass
class Timer:
    timers: ClassVar[Dict[str, float]] = dict()
    name: Optional[str] = None
    text: str = &quot;Elapsed time: {:0.4f} seconds&quot;
    logger: Optional[Callable[[str], None]] = print
    _start_time: Optional[float] = field(default=None, init=False, repr=False)

    def __post_init__(self) -&amp;gt; None:
        &quot;&quot;&quot;Add timer to dict of timers after initialization&quot;&quot;&quot;
        if self.name is not None:
            self.timers.setdefault(self.name, 0)

    def start(self) -&amp;gt; None:
        &quot;&quot;&quot;Start a new timer&quot;&quot;&quot;
        if self._start_time is not None:
            raise TimerError(f&quot;Timer is running. Use .stop() to stop it&quot;)

        self._start_time = time.perf_counter()

    def stop(self) -&amp;gt; float:
        &quot;&quot;&quot;Stop the timer, and report the elapsed time&quot;&quot;&quot;
        if self._start_time is None:
            raise TimerError(f&quot;Timer is not running. Use .start() to start it&quot;)

        # Calculate elapsed time
        elapsed_time = time.perf_counter() - self._start_time
        self._start_time = None

        # Report elapsed time
        if self.logger:
            self.logger(self.text.format(elapsed_time))
        if self.name:
            self.timers[self.name] += elapsed_time

        return elapsed_time

    def __enter__(self):
        &quot;&quot;&quot;Start a new timer as a context manager&quot;&quot;&quot;
        self.start()
        return self

    def __exit__(self, *exc_info):
        &quot;&quot;&quot;Stop the context manager timer&quot;&quot;&quot;
        self.stop()


with Timer():
    time.sleep(2)</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Sun, 14 Dec 2025 14:59:22 +0330</pubDate>
            </item>
                    <item>
                <title>پایتون – Recursion و فاکتوریل</title>
                <link>https://virgool.io/@rahtav68/%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86-%E2%80%93-recursion-%D9%88-%D9%81%D8%A7%DA%A9%D8%AA%D9%88%D8%B1%DB%8C%D9%84-kvq12ytsug4h</link>
                <description>recursion تکنیکی است که از آن برای حل مسایلی که نیاز است به اجزا پایینی و ریزتر یک مساله مراجعه کرد استفاده می شود. به عبارت دیگر، فرآیندی است که در پاسخ اجرا، خودش را فراخوانی می کند. از جمله مزایای استفاده از recursion کم کردن مقدار خط کدهایی است که قرار است نوشته شود استفاده می شود ولی در عین حال منابع زیادی برای محاسبات را به خود اختصاص می دهد.  در ادامه یه توضیح مثال های متفاوت از پیاده سازی و حل فاکتوریل با استفاده از پایتون می پردازیم.فاکتوریلفاکتوریل به ضرب اعداد طبیعی غیر منفی در هم می گویند که کاربرد گسترده ای در آنالیز ریاضی، احتمالات و جبر داره و البته همراه با قدمت طولانی.تابع فاکتوریل عبارت است از :با توجه به تابع بالا،  به طور مثال فاکتوریل عدد 5 برابر خواهد بود، با حاصل ضرب مقادیر 1، 2، 3، 4 و 5 (حاصل : 120). و فاکتوریل صفر برابر 1 می شود.و اما اگر بخواهیم با recursion به محاسبه مقدار فاکتوریل 5 بپردازیم خواهیم داشت :همونطور که در عکس بالا مشخصه برای محاسبه فاکتوریل 5 با recursion، باید فاکتوریل مقادیر کوچکتر از آن به ترتیب در هر مرحله محاسبه شود واین دقیقا همون خاصیت برگشت پذیری و اجرا دوباره recursion هستش. روش پایتونی برای محاسبه فاکتوریل برابر خواهد بود با :def factorial(n):
    if n==0:
        return 1
    else:
        return n*factorial(n-1)

print(factorial(5))که در صورتی که مقدار ورودی تابع صفر باشد عدد یک بازگردانده می شود و در غیر این صورت تا زمان رسیدن به 0 از مقدار ورودی n، به اندازه یک واحد (n-1) کم می شود و دوباره تابع اجرا می شود. که اگر مرحله به مرحله اجرا کنیم مراحل زیر را خواهیم داشت.استفاده از whileهمچنین می تونید با  while همین کد رو اجرا کنیدdef while_factorial(n):
    while  n &gt;= 1:
        return n * factorial(n - 1)
    return 1

print(while_factorial(5))ذخیره نتایج فاکتوریل و استفاده در محاسبات بعدیبا بالا رفتن n، محاسبه فاکتوریل زمانبرتر خواهد شد که می توانیم موارد را چون همه یکبار محاسبه می شوند ذخیره کنیم تا میزان محاسبات را در دفعات بعد کمتر کنیم. برای اینکار از یک دیکشنری استفاده می کنیم و در صورتی که n قبلا محاسبه شده باشد در مقدار value دیکشنری آن را استخراج می کنیم و نتیجه رو در n های بالاتر استفاده کنیم.def factorial(n, cache={}):
    if n &lt; 1:
        return 1
    elif n in cache:
        return cache[n]
    else:
        print(f&#039;calculating {n}!&#039;)
        result = n * factorial(n-1)
        cache[n] = result
        return result


print(&#039;Result of 3!:&#039;, factorial(3))
print(&#039;============================&#039;)
print(&#039;Result of 5!:&#039;, factorial(5))خروجی برابر خواهد بود:calculating 3!
calculating 2!
calculating 1!
Result of 3!: 6
============================
calculating 5!
calculating 4!
Result of 5!: 120مشاهده می کنیم که دیگه در محاسبه فاکتوریل 5 فاکتوریل 1 و 2 و 3 دیگه محسابه نمی شوند چون قبلا در دیکشنری ذخیره شدند.استفاده از lru_cacheبه طور پیش فرض در python برای cache کردن متودی وجود داره که می تونید به صورت decorator ازش در محاسبات استفاده کنید. به طور نمونه برای مثال factorial به صورت زیر خواهیم داشت:from functools import lru_cache

@lru_cache(2**3)
def factorial(n):
    print(f&#039;n={n}&#039;)
    return 1 if n&lt;2 else n * factorial(n-1)بهتره ورودی و تعداد cache مورد نظر شما در خروجی به صورت 2 به توان n باشه. همونطور که در خروجی میبینید وقتی به تعداد 8 در تعداد بازگشت و ذخیره 8 مقدار فاکتوریل اول می رسیم فقط مقدار بعدی یعنی 9 محاسبه می شود.</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Sat, 13 Dec 2025 13:16:07 +0330</pubDate>
            </item>
                    <item>
                <title>SUM به همراه CALCULATE یا بدون CALCULATE، مساله این است!</title>
                <link>https://virgool.io/@rahtav68/sum-%D8%A8%D9%87-%D9%87%D9%85%D8%B1%D8%A7%D9%87-calculate-%DB%8C%D8%A7-%D8%A8%D8%AF%D9%88%D9%86-calculate-%D9%85%D8%B3%D8%A7%D9%84%D9%87-%D8%A7%DB%8C%D9%86-%D8%A7%D8%B3%D8%AA-xnt4iriowmzl</link>
                <description>حتما پیش اومده که با نوشتن برخی فرمول های DAX، هر چند به ظاهر یکسان نتایج متفاوتی رو دریافت کرده باشید. برای این که درک بهتری از نوع عملکرد محاسبات در DAX داشته باشیم لازمه برخی از مفاهیم محاسباتی رو دقیق تر متوجه بشیم. از جمله این مفاهیم که در این پست به بررسی اون می پردازیم تفاوت انواع فیلتر و زمینه محاسباتی در DAX می باشد.CONTEXTCONTEXT به مجموعه اطلاعات و شرایطی گفته میشه که نوع و ساختار عملیات و پردازشی رو تعیین میکنه. در حقیقت محاسباتی که در DAX انجام میشه تحت تاثیر فیلترهای visual ها، روابط بین جداول، فیلتر های توابع و … هستن. این تعامل و عکس العمل ها باعث میشه بتونیم آنالیزهای پویا (DYNAMIC) ایجاد و استفاده کنیم. سه نوع CONTEXT در DAX وجود داره که عبارتند از :ROW CONTEXTFILTER CONTEXTQUERY CONTEXTROW CONTEXTاگر فرمولی به صورت CALCULATED COLUMN نوشته باشید؛ به تمامی مقادیر در همه ستون ها برای سطر جاری ROW CONTEXT گفته میشه. در صورتی که جدول با جدول دیگه ای هم در ارتباط باشه ROW CONTEXT شامل تمامی مقادیر جدول دیگه که در ارتباط با سطر جاری باشند هم می شود. پس همه چیز بر میگرده به سطر. فرض کنید جداولی دارید شامل اطلاعات زیر :ضرب مقدار کالا در قیمت کالا در مثال بالا در صورتی که به ازای سطر جاری از جدول Sales مقدار قیمت رو از جدول Product بیاوریم یک مثال ساده از ROW CONTEXT خواهد بود.به طور کلی میشه ویژگی ها و موارد زیر رو شامل ROW CONTEXT دونست :فرمول های Calculated Columnسطرهای جاریتوابع X-ENDING مثل SUMX یا RANKXFILTER CONTEXT / QUERY CONTEXTهر چند در طبقه بندی صورت گرفته توسط مایکروسافت QUERY CONTEXT مجزا بررسی شده ولی در محاسبات DAX می توان گفت این دو از نظر عملکردی کاملا مشابه یکدیگر عمل می کنند و تفاوت چندانی میان آن ها وجود نداره. زمانی که فیلتری رو از طریق آرگومان های تابع در فرمول اعمال می کنیم به این نوع فیلتر، FILTER CONTEXT گفته میشه. در محاسبات اولویت از بین سه نوع CONTEXT مورد بحث همیشه با FILTER CONTEXT خواهد بود. یعنی اگر در فرمولی به طور مثال از تابع FILTER استفاده کنید اولویت محاسباتی با این تابع خواهد بود. و اما اگر از روش های دیگری به غیر از نوشتن مستقیم شرط مورد نظرتون که باعث ایجاد یک زیر مجموعه از داده های اصلی میشه فیلتر و مجموعه داده شکل بگیره به اون QUERY CONTEXT گفته میشه.یعنی FILTER CONTEXT تنها شامل هر گونه فیلتر که از طریق آرگومان های توابع اعمال میشه خواهد بود و QUERY CONTEXT شامل مواردی چون :فیلترهای نمودارها و visual هاslicer هاتاثیر سطر و ستون Pivot Table و Tableفیلترهای متقاطعدر نتیجه اگر ستونی رو در Visual قرار میدید یا به طور مثال مجموع فروش بیشتر از ۲۰ عدد در هر سفارش رو محاسبه کنید با یک FILTER CONTEXT رو به رو خواهید بود. با این کار مجموعه ای جدید از داده ها رو شکل میدید که محاسبات بر روی اون انجام میشه.SUM به همراه CALCULATE یا بدون CALCULATE، مساله این است!قبل از این که این موضوع رو پیش ببریم با اصطلاحی آشنا میشیم به نام CONTEX TRANSITION. زمانی که از توابع CALCULATE یا CALCULATETABLE استفاده می کنیم اتفاقی که میفته اینه که ROW CONTEXT ها به FILTER CONTEXT تبدیل میشوند. این تبدیل تاثیر قابل توجهی بر روی محاسبات DAX خواهد داشت. اما به چه صورت؟مثال بخش قبل رو در نظر بگیرید اگر تابع SUM رو بر روی ستون Qty اعمال کنیم خواهیم داشت :در این مثال نتیجه جمع ستون Qty  یعنی ۱۵۰۰ برای همه سطرها تکرار شده. در حقیقت SUM جمع همه سطرها رو در FILTER CONTEXT و مجموعه داده موجود یعنی همین داده هایی که بدون هیچ فیلتری داریم مشاهده میکنیم حساب میکنه و به صورت سطر به سطر این مقدار برگردونده میشه. به عبارت دیگه جمعی که برای هر سطر محاسبه میشه جمع کل داده های ستونه.اما درصورت استفاده از CALCULATE نتیجه متفاوت خواهد شد :در این حالت تفاوتی که بوجود میاد تبدیل ROW CONTEXT به FILTER CONTEXT به ازای هر سطر به خاطر استفاده از CALCULATE هستش. یعنی با وجود این که هیچ گونه پارامتری در CALCULATE وجود نداره هر سطر به یک زیر مجموعه از داده ها تبدیل میشه و محاسبات بر روی اون سطر انجام میشه. به عبارت دیگه به ازای سطر جاری مثلا سطر اول، ROW CONTEXT که شامل همه مقادیر ستون های اون سطره به FILTER CONTEXT تبدیل میشه و اینجاست که سطر اول شما یک زیر مجموعه (SUBSET) داده یک سطری خواهد شد که جمع بر روی اون صورت میگیره. مثه این که به صورت دستی جدول رو فیلتر میکنید سطر به سطر اینکار رو انجام میدید و جمع رو در هر بار در ستون CALCULATE می نویسید.برای حذف این فیلتر ها و تحت تاثیر قرار دادن این فیلترها، استفاده از توابع گروه FILTER مثل ALL توصیه میشه!</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Mon, 01 Dec 2025 10:55:07 +0330</pubDate>
            </item>
                    <item>
                <title>محدودیت دسترسی به page ها در Power BI</title>
                <link>https://virgool.io/@rahtav68/%D9%85%D8%AD%D8%AF%D9%88%D8%AF%DB%8C%D8%AA-%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D8%A8%D9%87-page-%D9%87%D8%A7-%D8%AF%D8%B1-power-bi-gmtxild1tggq</link>
                <description>احتمالا با مفهوم RLS یا Row Level Security در power bi آشنایی دارید. با توجه به این که یک داشبورد میتونه شامل دسترسی های متفاوتی باشه احتمالا براتون پیش اومده که از این قابلیت استفاده کنید تا محدودیت دسترسی برای کاربران در استفاده از داده ها رو اعمال کنید. اما اگر بخواهیم این محدودیت رو براساس نه نوع داده ها و نه ردیف داده ها بلکه صفحات ها داشبورد ها اعمال کنیم نیازمند این خواهیم بود که خودمون یک سیستم راهبری (Navigation) ایجاد کنیم. در این مقاله به بررسی روشی برای ایجاد محدودیت گروه کاربران به صفحات داشبوردها در power bi یعنی page ها می پردازیم. هر چند که این قابلیت هنوز به صورت مستقیم توسط مایکروسافت در power bi قرار داده نشده ولی میشه با استفاده از ابزارهای متفاوتی این محدودیت و سطح دسترسی رو در power bi پیاده سازی کرد.قدم اول ایجاد یک جدول شامل ستون های تصویر زیر است. این جدول که شامل نام page های شما به همراه دسترسی گروه های کاربران به هر کدام از صفحات است. جدول ما با نام “Page Level Security” ساخته شد!در مثال ما، گزارش از 5 صفحه تشکیل شده که مدیران (Managers) به تمامی صفحات دسترسی خواهند داشت و کارشناسان (Experts) تنها به 3 صفحه Home و Sales و Returns دسترسی دارند. حتما توجه داشته باشید نام page ها در جدول کاملا مشابه نام صفحات در داشبورد شما باشند.به ازای هر صفحه یک Button ایجاد کنید. برای این کار از بخش Buttons نوع Blank رو انتخاب کنید. تنظیمات و ظاهر مورد نظر رو ایجاد کنید و چهار بار Button رو کپی کنید یا اگه از قبل هر 5 تا رو ایجاد کردید از format painter برای اعمال تغییرات ظاهری استفاده کنید.که نتیجه نهایی برای مثال ما به صورت زیر شد البته بعدا رنگش رو با فرمول تغییر میدیم پس زیاد وقت نذارید 😁حالا وقشته که کد رو بنویسیم!کد زیر رو به صورت یک measure ایجاد کنید البته لازمه به ازای هر button یک measure تعریف کنید.Button_Destination_1 = 
MAXX (
    FILTER (
        ADDCOLUMNS (
            &#039;Page Level Security&#039;,
            &quot;Rank&quot;, RANKX (&#039;Page Level Security&#039;,[Order],,ASC)
        ),
        [Rank] = 1
    ),
    &#039;Page Level Security&#039;[Page]
)تشریح فرمول:  در فرمول بالا به جدول Page Level Security ستونی مجازی به نام Rank اضافه می شود که براساس ستون order یک رتبه عددی به هر کدام از سطر ها (صفحات) تخصیص داده میشه. پس از این که این ستون مجازی ساخته شد، فیلتری توسط تابع Filter انجام میشه که در صورتی که ستون Rank برابر مقدار 1 بود نام صفحه از ستون Page برگردونده میشه. برای درک بهتر خروجی فرمول، خروجی هر measure به صورت زیر خواهد بود.فرمول button آخر هم به صورت زیر خواهد بود. دقت کنید Rank با توجه به شماره measure تغییر میکنه.Button_Destination_5 = 
MAXX (
    FILTER (
        ADDCOLUMNS (
            &#039;Page Level Security&#039;,
            &quot;Rank&quot;, RANKX (&#039;Page Level Security&#039;,[Order],,ASC)
        ),
        [Rank] = 5
    ),
    &#039;Page Level Security&#039;[Page]
)بر روی button اول کلیک کنید و در قسمت Button Text اولین measure رو انتخاب کنید.با تنظیم همه نتیجه به صورت تصویر زیر خواهد بود :اما تا این جا فقط نام هر button ایجاد شده و برای Navigation و ایجاد لینک لازمه measure ها رو به قسمت Action هم اضافه کنید. حتما یادتون باشه نوع Page Navigation انتخاب شده باشه.برای ایجاد دسترسی هم تنها کافیه که از قسمت Manage Roles ستون دسترسی ها رو برابر یک بذاریم به طور مثال برای Experts خواهیم داشت :حالا نوبت تست عملکرد موارد بالاست. به این منظور از قسمت View As نقش (Role) ایجادی رو انتخاب می کنیم.و خواهیم دید فیلتر به درستی داره عمل میکنه :و اما بخش buttons ها به چه صورت خواهد بود؟آنچه مدیران (Managers) خواهند دید:آنچه کارشناسان (Experts) خواهند دید:فقط فراموش نکنید که باید page های غیر page اصلی یا ابتدایی رو مخفی (hide) کنید.برای استفاده بر روی report server یا report service هم کافیه که هر role رو به گروه خودش یا کاربران مورد نظر اختصاص بدید. برای استفاده از حالت Dynamic RLS هم که دادن دسترسی در مدلسازی انجام میشه در ادامه به بررسی روش راحت تری برای Navigation خواهم پرداخت.روش داینامیک تر و ساده ترجداول و ارتباط هابرای پیاده سازی نیاز به سه جدول خواهیم داشت که به صورت زیر در رابطه خواهند بود:جدول کاربران (Users):جدولی که شامل اطلاعات کاربری کاربران است و در مثال ما شامل اطلاعات زیر می باشد. فقط دقت داشته باشید که UserName ها دارای DomainName می باشند که شما با نام دامین سازمان خود یا نام Machine خود جایگزین می کنید.جدول صفحات (Pages):جدولی شامل نام صفحات و همچنین ستونی که نشان دهنده جایگاه هر صفحه در slicer خواهد بود.این جدول شامل اطلاعات کاربران و نام صفحاتی که کاربران به آن ها دسترسی دارند می باشد. رابطه این جدول با جدول User ی; رابطه “چند به یک” می باشد که در حالت Single خواهد بود.ولی در رابطه با جدول Pages نیاز است که یک رابطه دو طرفه باشد (Both) که در صورت فیلتر شدن، صفحات مجاز برای مشاهده در slicer نشان داده شود. و همچنین برای اعمال فیلتر حتما تیک گزینه apply security filter in both directions رو بزنید.ایجاد slicerیک slicer ایجاد کنید و از جدول pages ستونن نام صفحات آن را پر کنید. تنها نکته ای که باید در نظر داشته باشید اینه که فیلتر رو بر روی single select قرار دهید…به منظور رفتن به صفحات دیگه از یک button استفاده می کنیم. در قسمت Action حالت page navigation رو با measure زیر تکمیل می کنیم.PageNavigation = SELECTEDVALUE(&#039;Pages&#039;[PageName])بسیلایجاد دسترسی و تستاین بخش هم که مثله همیشه ایجاد یک دسترسی در قسمت manage roles هستش و به صورت زیر تکمیل میشه:دو نکته آخر این که همه صفحات به غیر از صفحه home رو در حالت hidden قرار دهید و حتما مثله مقاله قبل دسترسی رو در RLS بخش Report Server یا Service ایجاد کنید.در صورت تست دسترسی برای کاربریه خودم که 3 صفحه مجاز به دیدن داره خواهیم داشت:البته در نسخه های جدید تر می تونید از قابلیت Navigation خود Power BI استفاده کنید که لازم به slicer یا shape نیست.</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Sun, 23 Nov 2025 15:22:35 +0330</pubDate>
            </item>
                    <item>
                <title>گرفتن خروجی csv از دیتابیس با نام داینامیک – ssis</title>
                <link>https://virgool.io/@rahtav68/%DA%AF%D8%B1%D9%81%D8%AA%D9%86-%D8%AE%D8%B1%D9%88%D8%AC%DB%8C-csv-%D8%A7%D8%B2-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3-%D8%A8%D8%A7-%D9%86%D8%A7%D9%85-%D8%AF%D8%A7%DB%8C%D9%86%D8%A7%D9%85%DB%8C%DA%A9-%E2%80%93-ssis-es8u8gcgbc8c</link>
                <description>راه های زیادی برای گرفتن خروجی از دیتابیس (در مثال ما mssql) وجود داره. اگر نیاز دارید نام خروجی شما با تاریخ همون روز همراه باشه! این آموزش رو دنبال کنید. در این مقاله با استفاده از ابزار SSIS از دیتابیس خروجی در قالب فایل csv خواهیم گرفت و چون لازمه خروجی ها در روز های متفاوت ایجاد و و  ذخیره شوند در نام هر فایل تاریخ روز را قرار می دهیم. و در آخر هم فایل هایی که بیشتر از n روز از ایجادشون گذشته رو حذف خواهیم کرد.1- در ابتدا یک پروژه Integration بسازید و نام و آدرس ذخیره شدن پروژه رو مشخص کنید.2- در قسمت Control Flow یک Data Flow Task ایجاد کنید.3- به قسمت Data Flow برید و برای گرفتن داده از دیتابیس (MS SQL Server)، OLE DB Source را ایجاد کنید و دابل کلیک کنید و سپس پس از ایجاد یک کانکشن یا استفاده از کانکشن موجودتون، کوئری یا نام جدول مورد نظر خود را وارد کنید.4- یرای ذخیره خروجی در قالب فایل csv یک Flat File Destination هم در صفحه قرار دهید و OLE DB Source را به آن متصل کنید و دابل کلیک کنید.5- گزینه Delimited رو انتخاب کنید.6- یک فایل csv در آدرس مورد نظر ایجاد کنید.  آدرس فایل رو در کادر File قرار دهید. (اگر لازم بود که به صورت UTF-8 ذخیره کنید تا متون فارسی به درستی نمایش داده بشوند)7- بر روی connection manager کلیک راست کنید و properties را انتخاب کنید.8- در ابتدا به منظور این که نام ستون در ردیف اول آورده شود گزینه زیر رو در پنجره باز شده True کنید.9- گزینه Expression رو پیدا کنید و بر روی … کلیک کنید.10- در پنجره باز شده Connection String رو پیدا کنید و بر روی … کلیک کنید.11- در پنجره باز شده آدرس فولدر و دایرکتوری که قصد دارید فایل در اون ذخیره بشه رو وارد کنید. فقط دقت کنید آدرس رو با دو back slash وارد کنید. مانند تصویر زیر! و همچنین در صورتی که فایل قراره بر روی یک فضای مشترک ذخیره بشه از آدرس دهی UNC استفاده کنید و مطمئن بشید کاربر اجرا کننده دسترسی لازم به آدرستون رو داشته باشه.پس از اجرا خواهیم داشت:تنها کاری که مونده انجام بدیم اینه در ساعت مورد نظر این package رو job کنیم و دقت کنید در صورتی که چند بار در روز آپدیت می کنید فایل overwrite خواهد شد. دراون صورت می تونید ساعت و دقیقه و ثانیه رو به اسم فایل اضافه کنید.حذف فایل های ایجاد شده در n روز پیشبرای پاک کردن فایل هایی که تاریخ ایجادشون بیشتر از n روز شده هم لازمه با استفاده از کد VB یا #C این کار رو انجام بدیم. که در این مثال من از VB استفاده می کنم که به نظر خودم خوندنش و نوشتنش راحت تره! البته می تونید دیتا رو در فرآیند دیگه ای ETL کنید و با تسک های خود SSIS آرشیو کنید و MOVE به فولدر دیگه!1- یک package ایجاد کنید و Script Task رو در قسمت Control Flow قرار دهید.2- دو متغیر زیر رو ایجاد کنید.در متغیر varDays تعداد روزهایی که لازمه به عقب برگردید رو وارد کنید. در مثال ما این مقدار 2 روز می باشد. همچنین قسمت Value از متغیر varPath رو با آدرس دایرکتوری مورد نظرتون پر کنید.3- به پنجره Script Task وارد شوید و Visual Basic رو انتخاب کنید سپس متغیر ها رو در قسمت ReadOnly وارد کنید و در نهایت Edit Script رو انتخاب کنید.4- پس از انتخاب Edit Script پنجره VSTA باز می شود. کد زیر رو در قسمت نشان داده شده در تصویر زیر قرار دهید و قبل از اون همه کد های نوشته بین Sub رو  پاک کنید.        Dim SourcePath As String

        Dim PurgeDays As Integer

        PurgeDays = CInt(Dts.Variables(&quot;User::varDays&quot;).Value)
        SourcePath = CStr(Dts.Variables(&quot;User::varPath&quot;).Value)
        Dim directory As DirectoryInfo = New DirectoryInfo(SourcePath)

        For Each f As FileInfo In directory.GetFiles()
            If (Now - f.CreationTime).Days &gt;= PurgeDays Then f.Delete()
        Nextدر نهایت خواهیم داشت:5- در قسمت import، کتابخونه IO رو به این صورت وارد کنید Imports System.IO. می تونید کتابخونه هایی که مورد استفاده نیستن رو هم پاک کنید.5- فایل رو Save کنید و پنجره رو ببندید و پس از بازگشت به پنجره قبل (Script Task) حتما OK رو بزنید در غیر این صورت کد ها ذخیره نمی شوند.6- برای تست هم می تونید مقدار متغیر varDays رو به 0 تغییر دهید و پس از اجرا تمام فایل ها آن روز به قبل پاک خواهند شد (یعنی همشون پس مراقب باشید اشتباها همه چی پاک نشه 😁)ج</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Fri, 21 Nov 2025 15:51:55 +0330</pubDate>
            </item>
                    <item>
                <title>تغییر measure با استفاده از slicer</title>
                <link>https://virgool.io/@rahtav68/%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-measure-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-slicer-dlklf9ukkmyp</link>
                <description>برخی داده ها به صورتی هستند که امکان نمایش آن ها در قالب یک یا چند visual یکسان وجود دارد. برای این که بتوان به صورت شیفتی..! از چند measure در چند visual استفاده کرد می توان measure های مورد نظر رو با استفاده از یک slicer فیلتر کرد و امکان جابه جایی بین چند measure رو به راحتی بدست آورد. در این پست کوتاه با انجام یک مثال ساده این مورد رو بررسی می کنیم.قدم اول؛ measure های مورد نظرتون رو ایجاد می کنیم. در مثال ما چهار measure با نام های زیر ایجاد شده:total salestotal sales in qtyavg sales in qtyyear running total in total saleمسلما measure های شما متفاوته…!قدم دوم؛ جدولی با نام MeasureTable  با استفاده از Enter Data می سازیم که نام شاخص های مورد نظر شما داخل آن ثبت شده. به صورت زیر:قدم سوم؛ measure زیر را که با استفاده از SWITCH و SELECTEDVALUE ساخته شده رو ایجاد می کتیم.Measure Selection = 
    SWITCH (
        SELECTEDVALUE (MeasureTable[measure]),
        &quot;فروش خالص&quot;, Data[total sales],
        &quot;فروش تعدادی&quot;, Data[total sales in qty],
        &quot;میانگین قیمت فروش&quot;, Data[avg sales in qty],
        &quot;فروش تجمعی&quot;, Data[year running total in total sale]
    )قدم آخر؛ و در پایان measure بالا (Measure Selection) رو در قسمت values نمودار ها یا جداول قرار می دهیم و یک slicer براساس ستون measure جدول MeasureTable ایجاد می کنیم. و دقت کنید slicer رو بر روی حالت single select قرار دهید.برای نازک کاری هم می تونید title ها رو داینامیک کنید و اونم کافیه یک measure شبیه measure زیر ایجاد کنید و در قسمت title نمودار یا جدول مورد نظر قرار دهید.Titles = SELECTEDVALUE(MeasureTable[measure])</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Wed, 19 Nov 2025 10:30:51 +0330</pubDate>
            </item>
                    <item>
                <title>داستان کوتاهی از SELECT INTO</title>
                <link>https://virgool.io/@rahtav68/%D8%AF%D8%A7%D8%B3%D8%AA%D8%A7%D9%86-%DA%A9%D9%88%D8%AA%D8%A7%D9%87%DB%8C-%D8%A7%D8%B2-select-into-caqlvgyszqmz</link>
                <description>روش های مختلفی برای ساخت یک جدول وجود داره به طور مثال استفاده از SSMS و ساخت گرافیکی جدول؛ یا روش دیگر استفاده از عبارت CREATE TABLE در T-SQL می باشد. اما در این مقاله نکاتی برای ساخت جدول و نحوه ایجاد ساختار جدول از روی جدولی دیگر را با استفاده از دستور SELECT INTO بررسی می کنیم. قبل از شروع در نظر داشته باشید لازمه دسترسی های لازم برای ساخت جدول رو داشته باشید.برای مثال این مقاله از پست Person در دیتابیسAdventureWorks2017 استفاده می کنیم. با استفاده از دستور زیر ستون های محدودی رو از جدول Person به جدولی به نام Test منتقل می کنیم در این حالت علاوه بر ایجاد ساختار جدول، داده های جدول نیز به جدول Test منتقل می شوند.SELECT
     [BusinessEntityID]
    ,[PersonType]
    ,[NameStyle]
    ,[Title]
    ,[FirstName]
    ,[MiddleName]
    ,[LastName]
    ,[Suffix]
     ,[EmailPromotion]
INTO dbo.Test
FROM [AdventureWorks2017].[Person].[Person];نتیجه اجرا دستور بالا:و data type و سایر ویژگی های ستون ها رو هم که مشاهده می کنید:اگر ستون ها ترکیب شوند و یا تغییراتی در فرمت آن ها داده شود نیز این تغییرات در ساختار جدول مقصد اعمال می شود. به طور مثال اگر دستور زیر که ترکیب FirstName و MiddleName و LastName می باشد رو پس از ایجاد مشاهده کنید طول مجاز کاراکترهای ستون FullName برابر خواهد بود با 152 که برابر است با طول 3 ستون ذکر شده با طول 50 کاراکتری و طول 2 spaceSELECT
     [BusinessEntityID]
    ,[PersonType]
    ,[NameStyle]
    ,[Title]
    ,[FirstName]
    ,[MiddleName]
    ,[LastName]
    ,FullName = [FirstName] + &#039; &#039; + ISNULL([MiddleName],&#039;&#039;) + &#039; &#039; + [LastName]
    ,[Suffix]
    ,[EmailPromotion]
INTO dbo.TestWithExpression
FROM [AdventureWorks2017].[Person].[Person];در صورتی که بخواهیم فقط ساختار جدول مبدا در جدول مقصد کپی بشه می تونیم در قسمتWHERE شرطی بنویسیم که هیچ خروجی برای انتقال نداشته باشد. به طور مثال 1=2 که مشخصا شرطی است که برقرار نخواهد شد باعث می شود هیچ خروجی در دستور SELECT برنگردد و تنها ساختار جدول ایجاد می شود.SELECT     [BusinessEntityID]
    ,[PersonType]
    ,[NameStyle]
    ,[Title]
    ,[FirstName]
    ,[MiddleName]
    ,[LastName]
    ,FullName = [FirstName] + &#039; &#039; + ISNULL([MiddleName],&#039;&#039;) + &#039; &#039; + [LastName]
    ,[Suffix]
    ,[EmailPromotion]
INTO dbo.TestNoData
FROM [AdventureWorks2017].[Person].[Person]
WHERE 1 = 0;</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Mon, 17 Nov 2025 09:24:22 +0330</pubDate>
            </item>
                    <item>
                <title>روش های انتقال داده از power bi به sql server</title>
                <link>https://virgool.io/@rahtav68/%D8%B1%D9%88%D8%B4-%D9%87%D8%A7%DB%8C-%D8%A7%D9%86%D8%AA%D9%82%D8%A7%D9%84-%D8%AF%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-power-bi-%D8%A8%D9%87-sql-server-ung8vv1roa6v</link>
                <description>در این پست روش های متفاوت انتقال داده از power bi به sql server رو با هم مرور کنیم. البته فعلا چهار روش ساده تر که بیشتر جنبه یک بار انتقال دیتا رو دارند معرفی می کنیم و روش های پیشرفته تر رو در آینده نزدیک!... هر روش بسته به نیاز میتونه مورد استفاده شما قرار بگیره ولی اگه به دنبال روش ساده تر و سریع تر هستید و وقت تست و بررسی روش های متفاوت رو ندارید؛ استفاده از Dax Studio رو برای انتقال داده پیشنهاد می کنم. برای مثال از دیتابیس AdventureworksDW2016CTP3 و جدول FactResellerSales و البته بخشی از ستون ها استفاده می کنم. دیتابیسی هم که قراره جداول رو انتقال بدم دیتابیس local خواهد بود.روش اول _ استفاده از Copy Table:در این روش می تونید کل داده های جدول رو copy کنید و در اکسل یا یک فایل متنی paste کنید. این روش سادست و البته برای مواردی بهتره استفاده بشه که تعداد جداول کم و همچنین تعداد رکوردها زیاد نباشه چون قراره کل رکورد ها به ram منتقل بشه پس حواستون باشه داده های زیاد داشته باشید و منابع کم به احتمال زیاد کامپیوترتون هنگ میکنه. برای این کار از قسمت Model در power bi جدول مورد نظر رو باز کنید و در قسمتی از جدول راست کلیک کنید و گزینه Copy table رو انتخاب کنید. برای انتقال به دیتابیس بهتره از فایل csv استفاده کنید که انتقال رو راحت تر میکنه و نیاز به سرویس ها و تنظیمات اکسل روی دیتابیس ندارید.دیتا رو پس از کپی به اکسل منتقل کنید و سپس با فرمت csv ذخیره کنید. و سپس با استفاده از import data از دیتابیس مورد نظر به دیتابیس منتقل کنید. هر چند پیشنهاد من SSIS هستش برای انتقال داده ها چون هم ساده تره و هم به خطاهای عدم مغایرت data type ها بر نمی خورید یا کمتر مواجه میشد. (خیلی هم شاید روش خوبی نباشه برای خروجی ها سریع خوبه و یکبار مصرفه در کل)روش دوم _ استفاده از SSIS:برای استفاده از این روش SSDT رو دانلود و نصب کنید. قراره پورت power bi رو پیدا کنیم و از اون طریق به power bi متصل بشیم.%LocalAppData%\Microsoft\Power BI Desktop\AnalysisServicesWorkspacesابتدا لازمه که فایل power bi رو ذخیره کنید و البته فایل باز باشه. در مرحله بعد در قسمت نوار آدرس File Explorer آدرس زیر رو قرار بدید.فقط دقت کنید اگه پوشه خالی هستش احتمالا شما فایل رو با نسخه Power BI RS باز کردید که اگه چند پوشه به عقب برگردید می تونید فولدر Power BI Desktop SSRS رو پیدا کنید و از اون مسیر ادامه بدید. در ادامه به پوشه AnalysisServicesWorkspacesXXXXXXXXX وارد بشید مدل در این پوشه قرار گرفته. البته شماره ای هم در انتهاش وجود داره. اگر بیشتر از یه پوشه وجود داره به خاطر اینه که شما چند فایل power bi باز کردید!به پوشه Data وارد بشید و فایل “msmdsrv.port.txt” رو پیدا کنید. پورت موردنظرمون در این فایل ذخیره شده.قدم بعدی ساخت پروژه Integration جدید در SSIS خواهد بود.اگر پیدا نکردید Integration Service رو لازم به نصب دارید!نام گذاری پروژه و مشخص کردن مسیر ذخیره مرحله بعد خواهد بودیک Data Flow Task در صفحه قرار دهید و از Other Sources تسک OLEDB Source رو در صفحه قرار دهید.با دابل کلیک و باز کردن پنچره OLEDB Source Editor یک connection جدید ایجاد کنید.در پنجره Connection Mangerقسمت Provider رو به Native OLEDB\Microsoft OLEDB Provider for Analysis Services 13.0 تغییر بدیدکادر Location رو با ip و port پر کنید برای مثال ما به این صورت خواهد شد localhost:1052 یا میتونید 127.0.0.1:1052 هم قرار بدید. دقت کنید شما به جای 1052 از پورتی که در مرحله قبل بدست آوردید استفاده کنید. همچنین این پورت به صورت رندم ایجاد میشه پس در موارد بعدی تغییر خواهد کردقسمت initial catalog رو هم از لیست با مقدار موجود پر کنیددر انتها test connection باید موفقیت آمیز باشهبا تایید connection به قسمت OLEDB Source Editor برگردید در این قسمت لیست تمام جداول موجود در مدل رو میتونید مشاهده کنیداما گزینه دیگه SQL Command که قابلیت استفاده از اسکریپت های DAX رو به ما میده . در مثال ما چون کل رکورد های جدول مد نظره به این صورت خواهد شد:evaluate &#039;FactResellerSales&#039;برای مقصد هم از OLEDB Destination استفاده می کنیم. connection رو با provider از نوع Native OLE DB\SQL Server Native Client ایجاد کنید و آدرس سرور و دیتابیس مورد نظر رو وارد کنید. به OLEDB Destination Editor بازگردید. یک جدول جدید ایجاد کنید.در صورتی که اسکریپت ساخته شده رو استفاده کنید متوجه خواهید شد که کار نمیکنه! لازمه کمی تمیزکاری انجام بدیم. برای تمیز کاری با replace قسمت های اضافی رو حذف می کنیم. که در نهایت اسکریپت زیر رو خواهیم داشت. نام جدول و فرمت ها رو هم می تونید اصلاح کنید البته فرآیند طولانی تری خواهد شد من به همین بسنده میکنم!قسمت Mapping رو هم اصلاح کنید و فیلدهای درست رو به هم متصل کنید.و در نهایت انتقال داده ها:روش سوم _ استفاده از Dax Studio:در ابتدا Dax studio رو نصب کنید. مثال رو در حال حاضر با نسخه DaxStudio_2_17_3 پیش خواهم برد. در این روش هم لازمه فایل مورد نظر power bi رو باز کنید. سپس از طریق connect در نرم افزار به مدل متصل بشید.گزینه Export Data را از Advanced انتخاب کنید.سرور مورد نظر و دیتابیس مقصد رو مشخص کنید.جداول مورد نظر برای انتقال رو انتخاب کنید.و انتقال داده! و جدولی به همان نام در دیتابیس مقصد ایجاد و داده ها ذخیره می شوند. به همین سادگی!روش چهارم _ استفاده از Python:در استفاده از این روش از script قسمت query editor استفاده می کنیم هر چند این کار لقمه رو دور سر پیچوندنه ولی توضیحش خالی از لطف نیست. برای این کار لازمه کتابخونه های زیر رو نصب کنید.pyodbcpandassqlalchemyکد رو زیاد توضیح نمیدم ولی خلاصش اینه که به وسیله to_sql از کتابخونه pandas قراره جدولی در دیتابیس ایجاد کنیم و داده ها رو insert کنیم. برای این کار لازمه با استفاده از pyodbc ارتباطی با دیتابیس mssql ایجاد کنیم. هر چند که می تونیم این انتقال رو با iterrows بر روی تک تک سطرها و انجام یک اسکریپت insert into انجام بدیم که نیازمند ساخت جدول قبل از ورود داده ها و map  همه ستون هاست.اما برای این که کار رو ساده تر کنیم با استفاده از sqlalchemy یک engine میسازیم و از اون طریق متصل میشیم و در حقیقت بخش زیادی از کنترل و مدیریت کار رو به این کتابخونه میسپریم. ابتدا در jupyter کد رو اجرا می کنم که داده هایی رو از فایلی خونده در df ذخیره می کنیم به صورت data frame و سپس داده ها رو به جدول مورد نظر بر روی دیتابیس منتقل می کنیم و در نهایت خروجی تعداد رکوردها رو برمی گردونیم.برای این که داده ها رو از power bi منتقل کنیم لازمه df رو به dataset تغییر بدیم به این صورت:import pyodbc
import pandas as pd
from sqlalchemy.engine import URL
from sqlalchemy import create_engine

connection_string = &quot;DRIVER={SQL Server};SERVER=localhost;DATABASE=test&quot;
connection_url = URL.create(&quot;mssql+pyodbc&quot;, query={&quot;odbc_connect&quot;: connection_string})

engine = create_engine(connection_url).connect()
dataset.to_sql(name=&#039;test_python_powerbi&#039;, con=engine, if_exists=&#039;replace&#039;, index=False)و یک python script رو اجرا بگیریم.و خروجی:</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Sun, 16 Nov 2025 15:49:32 +0330</pubDate>
            </item>
                    <item>
                <title>خواندن داده ها از کاربرگ اکسل براساس جایگاه آن در فایل – Power BI</title>
                <link>https://virgool.io/@rahtav68/%D8%AE%D9%88%D8%A7%D9%86%D8%AF%D9%86-%D8%AF%D8%A7%D8%AF%D9%87-%D9%87%D8%A7-%D8%A7%D8%B2-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%DA%AF-%D8%A7%DA%A9%D8%B3%D9%84-%D8%A8%D8%B1%D8%A7%D8%B3%D8%A7%D8%B3-%D8%AC%D8%A7%DB%8C%DA%AF%D8%A7%D9%87-%D8%A2%D9%86-%D8%AF%D8%B1-%D9%81%D8%A7%DB%8C%D9%84-%E2%80%93-power-bi-mzdq8hx6hrny</link>
                <description>در این پست به بررسی روشی برای خواندن داده ها از چند اکسل ولی با نام های متفاوت می پردازیم که تنها نقطه اشتراک این اکسل ها ترتیب قرارگیری کاربرگ (WorkSheet) ها هستند. در صورتی که نام کاربرگ ها متفاوت باشند در خواندن اکسل ها از یک پوشه (Folder) با خطا مواجه خواهیم شد و مجبور خواهیم بود تغییراتی در سورس دیتا انجام دهیم که به نظر چندان جالب و جذاب برامون نیست.مساله رو یک بار با هم مرور کنیم!فولدری داریم یا حتی فایل اکسل ثابتی که در دفعات متفاوت خواندن امکان تغییر نام sheet های آن وجود دارد. به طور مثال دو فایل زیر را با نام کاربرگی 02-04-1400 و 03-04-1400 در نظر بگیرید.در صورتی که از طریق Power Query فایل اول رو از پوشه زیر وارد محیط Power BI کنیمبدون هیچ خطایی و با انتخاب Sheet مورد نظر کار رو به انجام رسوندیم!اما اگر فایل دوم رو در همون مسیر قرار بدیمبا رفرش کردن با خطا زیر مواجه میشیم که داره به ما اخطار میده این فایل دوم Key یا کلید اصلی سورس که همون نام فایل Sheet مورد نظر فایل نمونه هستش (فایل اول) رو نداره!و اما راه حل!در فایل نمونه به جای استفاده از نام Sheet از ایندکس و موقعیت قرارگیری Sheet در فایل استفاده می کنیم. از قسمت Transform Sample File گزینه Advanced Editor رو انتخاب می کنیماگر به نام داخل براکت یعنی [Item=”1400-04-02″,Kind=”Sheet”] دقت کنید Power Query از این نام ثابت برای خواندن اطلاعات همه فایل ها داره استفاده می کنهبا پاک کردن این نام و استفاده از ایندکس از Power Query خواهیم خواست که دومین Sheet رو به ما از هر فایل برگردونه! برای اینکار هم تنها لازمه در داخل Source عدد یک را قرار دهیم به این صورت Source{1}[Data]اما چرا 1 ؟؟ Sheet های اکسل از ایندکس صفر شروع می شوند یعنی اولین Sheet شما 0 و دومی 1 و به همین ترتیب ادامه پیدا می کنهو خطا برطرف شد:</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Sat, 15 Nov 2025 09:01:03 +0330</pubDate>
            </item>
                    <item>
                <title>چگونگی تبدیل تاریخ UTC به Local در SQL</title>
                <link>https://virgool.io/@rahtav68/%DA%86%DA%AF%D9%88%D9%86%DA%AF%DB%8C-%D8%AA%D8%A8%D8%AF%DB%8C%D9%84-%D8%AA%D8%A7%D8%B1%DB%8C%D8%AE-utc-%D8%A8%D9%87-local-%D8%AF%D8%B1-sql-j8wrmjn2mmgg</link>
                <description>در بسیاری از دیتابیس ها که باهاش سر و کار داریم تاریخ ها به صورت UTC ذخیره شده. UTC یا Coordinated Universal Time یک معیار زمان برای تعیین ساعت‌ها و اختلافات زمانی در نقاط مختلف زمینه (با گرینویچ فرق داره!). هر منطقه زمانی از کره زمین با توجه به اختلاف مثبت یا منفی‌ با UTC تعیین میشه که این مقدار با TZ نشون داده میشه. به طور مثال در ایران با توجه به ساعت تابستانی یا همون daylight saving time؛ که در زمان نوشتن این مقاله تصمیم به حذفش گرفته شده از سال 1402؛ در نیمه اول سال به خاطر یک ساعت به جلو کشیده شدن ساعت ها در ابتدای بهار 4.5 ساعت و در نیمه دوم سال 3.5 ساعت با UTC اختلاف وجود داره. البته با علامت مثبت یعنی 3.5 ساعت از زمان UTC ساعت ایران جلوتره!چطور تاریخ UTC رو به Local تبدیل کنیم؟ اینکار با استفاده از توابع کدنویسی شده یا همون CLR امکان پذیره ولی روش دیتابیسی (مد نظر sql server) یا کوئری نویسی شده چیه؟پاسخ: با نوشتن عبارت زیر و جایگزینی ستون تاریخ مورد نظر با [Column_Name] این امکان تبدیل به وجود میاد.CONVERT(
  datetime, 
  SWITCHOFFSET(
    [Column_Name], 
    DATEPART(
      TZOFFSET, [Column_Name] AT TIME ZONE &#039;Iran Standard Time&#039;
    )
  )
)و اما توضیحات :در مجموع تبدیل زمان طی این مراحل انجام شد:اضافه کردن TZ به تاریخ UTC با استفاده از AT TIME ZONEاستخراج TZ از تاریخ با استفاده از DATEPARTتبدیل زمان UTC به زمان Local با استفاده از SWITCHOFFSET و TZ استخراج شدهقدم اول: برای بدست آوردن قسمت TZOFFSET، با استفاده از AT TIME ZONE به تاریخ TZOFFSET رو اضافه می کنیم. در مثال زیر به تاریخ UTC با توجه به تاریخ سیستم یا سرور مقدار TZOFFSET اضافه میشه که در این جا با توجه به تاریخ که در نیمه اول سال هست 04:30+ خواهد بود.قدم دوم: تابع DATEPART که مقدار عدد صحیح بخشی از تاریخ ورودی به تابع رو بر می گردونه به طور مثال:با استفاده از TZ یا TZOFFSET امکان استخراج مقدار TIME ZONE رو از تاریخ با استفاده از این تابع داریم. در صورتی که تاریخ در نیمه اول سال باشه 270 و در صورتی که در نیمه دوم سال باشه 210 برگردونده میشه. (البته الان با ثابت شدن که با Daylight saving time شناخته میشه این اختلاف در نیمه اول و دوم سال وجود نداره)قدم سوم: تبدیل این زمان و تاریخ به TZOFFSET مورد نظر هست که تابع SWITCHOFFSET به ما در این کار کمک می کنه.اگر همه این تکه ها رو به هم متصل کنیم خواهیم داشت:SELECT 
  GETDATE() AS LocalDateTime, 
  GETUTCDATE() AS UTCDateTime, 
  CONVERT(
    datetime, 
    SWITCHOFFSET(
      GETUTCDATE(), 
      DATEPART(
        TZOFFSET, 
        GETUTCDATE() AT TIME ZONE &#039;Iran Standard Time&#039;
      )
    )
  ) AS Convertedالبته برای تبدیل فرمت میتونیم با استفاده از Convert و datetime تاریخ رو به فرمت موردنظرمون تبدیل می کنیم.و با استفاده از تابع FORMAT تاریخ رو به شمسی هم تبدیل کنیم:FORMAT(
  CONVERT(
    datetime, 
    SWITCHOFFSET(
      GETUTCDATE(), 
      DATEPART(
        TZOFFSET, 
        GETUTCDATE() AT TIME ZONE &#039;Iran Standard Time&#039;
      )
    )
  ), 
  &#039;yyyy-MM-dd HH:mm&#039;, 
  &#039;fa-ir&#039;
)</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Thu, 13 Nov 2025 10:14:12 +0330</pubDate>
            </item>
                    <item>
                <title>یک measure بر روی نتیجه measure دیگه بنویسیم!</title>
                <link>https://virgool.io/@rahtav68/%DB%8C%DA%A9-measure-%D8%A8%D8%B1-%D8%B1%D9%88%DB%8C-%D9%86%D8%AA%DB%8C%D8%AC%D9%87-measure-%D8%AF%DB%8C%DA%AF%D9%87-%D8%A8%D9%86%D9%88%DB%8C%D8%B3%DB%8C%D9%85-sxsma6ideape</link>
                <description>در این پست یک مثال ساده از نحوه نوشتن measure بر روی خروجی measure دیگر رو بررسی می کنیم. سناریو مثال به این صورته که سنجه ای (همون measure 😁) برای محاسبه نسبت تعداد فروش یک محصول به تعداد مورد انتظار فروش از اون محصول (همون هدف فروش یا target) رو داریم. قراره اون محصولاتی که این سنجه در اون ها بیشتر از 70 درصد شدن رو بشماریم. با رسم شکل مثال رو کامل تر توضیح میدم به تصویر زیر دقت کنید!در تصویر بالا ستون آخر حاصل تقسیم QtySale بر QtyTarget به درصد هستش و قراره این ستون رو به صورت measure بنویسیم و مقادیری که بیشتر از 70 درصد هستند رو بشماریم (رنگ سبز ها 😊)مثال داده های بالا رو وارد power bi می کنیم و سنجه ای به این صورت برای محاسبه درصد تحقق می نویسیم.perc = CALCULATE(SUM(data[QtySale]) / CALCULATE(SUM(data[QtyTarget])))که به این صورت میشه:و در نهایت measure دوم رو برای محاسبه مقادیر بالای 70 درصد:over_70_count = 
var tbl =  SUMMARIZE (
    data,
    data[Product],
    &quot;condition&quot;,
        IF ( data[perc]  &gt; 0.7, 1, 0 )
)
var cnt = SUMX(tbl, [condition])
return cntاما در کد بالا چه میگذره! در این کد در حقیقت داریم جدول عکس بالا رو ایجاد و در متغیری به نام tbl ذخیره می کنیم (البته با دو ستون نام محصول و condition!). این کار با استفاده از summarize انجام شده و البته ستونی به عنوان condition هم اضافه کردیم که شرطی رو پیاده می کنه که اگه مقدار measure اول بیشتر از 0.7 یا 70 درصد بود مقادیر برابر 1 و درغیر این صورت صفر بشه. همین جدول رو میشه به صورت New Table هم خروجی گرفت؛ برای تست انجام بدید! و در نهایت مجموع عددهای 1 در ستون condition رو با SUMX جمع میزنیم که تعداد رو به ما نشون میده.و پس از کمی تنظیمات ظاهری به این خروجی می رسیم:</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Wed, 12 Nov 2025 11:43:52 +0330</pubDate>
            </item>
                    <item>
                <title>استخراج برگ های (leaf) یک رابطه سلسله مرتبی در SQL</title>
                <link>https://virgool.io/@rahtav68/%D8%A7%D8%B3%D8%AA%D8%AE%D8%B1%D8%A7%D8%AC-%D8%A8%D8%B1%DA%AF-%D9%87%D8%A7%DB%8C-leaf-%DB%8C%DA%A9-%D8%B1%D8%A7%D8%A8%D8%B7%D9%87-%D8%B3%D9%84%D8%B3%D9%84%D9%87-%D9%85%D8%B1%D8%AA%D8%A8%DB%8C-%D8%AF%D8%B1-sql-hbmjcyj722tf</link>
                <description>در این پست نحوه لیست کردن برگ های یک سلسه مراتب یا بهتره بگیم یک رابطه parent و child رو در SQL بررسی می کنیم. leaf در یک ساختار درختی به اون node گفته میشه که زیرمجموعه یا child نداره. به طور مثال در تصویر زیر 2، 5، 7 و 8 leaf هستند.برای این که با استفاده از کوئری این مقادیر رو استخراج کنیم، لازمه مقادیری از فیلد (ستون) child رو که هیچ گونه ردی در فیلد parent ندارند رو جدا کنیم. مثال تصویر بالا رو در صورتی که به صورت ستون های parent و child پیاده کنیم خواهیم داشت:DECLARE @t TABLE (id INT NOT NULL, REFID int NULL);

INSERT @t VALUES 
	(1, NULL), 
	(2, 1),  
	(3, 1),  
	(4, 3), 
	(5, 3),
    (6, 4), 
	(7, 4), 
	(8, 6);

SELECT * FROM @tتوضیح خروجی بالا: برای پر کردن جدول بالا همه شمارها رو از تصویر بالا در ستون id که همون child هست قرار میدیم و در صورتی که id  مورد نظر parent و عددی بالای اون داشت در ستون refid قرار میدیم و البته  برای id شماره 1 که هیچ گونه parent نداره NULL قرار میدیم.روش اولاستفاده از LEFT OUTER JOIN هستش که جدول رو با خودش JOIN می زنیم (بین ستون id از اولی و refid ازدومی) و در صورتی که در رکوردی از جدول دوم NULL پیدا شد یعنی اون id هیچ گونه child یا زیرمجموعه ای نداره! و leaf  هستشSELECT 
	t1.*	
FROM
	@t AS t1
LEFT OUTER JOIN
	@t AS t2
ON
	t1.id = t2.refid
WHERE
	t2.id IS NULLروش دوماستفاده از SUB QUERY در WHERE هستش که تمامی مقادیر id رو به ستون refid در قسمت شرط پاس میدیم و در صورتی که مقداری برگشتی وجود نداشت (با استفاده از NOT EXISTS چک می کنیم) اون id  leaf خواهد بودSELECT 
	id,
    refid
FROM   
	@t AS t_out
WHERE  
	NOT EXISTS (SELECT t_in.refid FROM  @t AS t_in WHERE t_in.refid = t_out.id) خروجی در هر دو حالت به صورت زیر خواهد بود:</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Tue, 11 Nov 2025 10:15:10 +0330</pubDate>
            </item>
                    <item>
                <title>عملگر IN در فرمول نویسی DAX</title>
                <link>https://virgool.io/@rahtav68/%D8%B9%D9%85%D9%84%DA%AF%D8%B1-in-%D8%AF%D8%B1-%D9%81%D8%B1%D9%85%D9%88%D9%84-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-dax-cpszg0gfzkyq</link>
                <description>عملگر IN :عملگر IN در حقیقت همون OR هستش که به جای نوشتن چندین OR بر روی یک جدول یا ستون میتونیم از IN به عنوان جایگزین استفاده کنیم. همچنین IN خودش برگرفته شده و معادل (syntactic sugar) تابع CONTAINSROW هستش. در ادامه به بررسی این عملگر پر کاربرد می پردازیم.تعریفی اگر بخواهیم داشته باشیم از این عملگر و تابع مرتبطش به این صورت خواهد بود که در صورتی که تابع CONTAINSROW، حداقل یک شرط برقرار داشته باشد خروجی True بر میگردونه (دقیقا همون منطق or). الگو این تابع به این صورت هستش (syntax):CONTAINSROW(&lt;Values&gt;, &lt;Column&gt; [, &lt;Column&gt; [, …] ] )و در مقابل روش ساده تر نوشتن این تابع با IN به این صورت خواهید بود:&lt;Values&gt; IN &lt;Column&gt;اما دقت داشته باشید که این Values، جدولی از مقادیر خواهد بود.به طور مثال فرض کنید داده هایی داریم به صورت زیر:در صورتی که بخواهیم فقط رنگ های قرمز یا سبز در ستون Color مقادیرشون رو نمایش بدیم و جمع بزنیم اولین روش برای اجرا استفاده از عملگر || خواهد بود که به معنای OR می باشد.RedOrGreen_OR_Operator = 
CALCULATE (
    SUM(&#039;Table&#039;[Amount]),
    &#039;Table&#039;[Color] = &quot;Red&quot; ||&#039;Table&#039;[Color] = &quot;Green&quot;
)در مقابل استفاده از CONTAINSROW خواهد بود که در ابتدا مقادیر شرط که در مثال ما Red و Green هستند رو دریافت می کنه و در آرگومان دوم ستون مورد نظری که شرط در اون برقرار میشه یعنی Color.RedOrGreen_CONTAINSROW = 
CALCULATE (
    SUM(&#039;Table&#039;[Amount]),
    CONTAINSROW({&quot;Red&quot;, &quot;Green&quot;}, &#039;Table&#039;[Color] 
))معادل فرمول های بالا با IN به این صورت نوشته می شود:RedOrGreen_IN = 
CALCULATE (
    SUM(&#039;Table&#039;[Amount]),
    &#039;Table&#039;[Color] IN {&quot;Red&quot;, &quot;Green&quot;}
)و البته اگه عبارت بالا رو با خطای عمدی در قسمت IN وارد کنید، خطای زیر بر میگرده که نشون میده در حقیقت IN از تابع CONTAINSROW داره استفاده میکنه!The number of arguments is invalid. Function CONTAINSROW 
must have a value for each column in the table expressionخروجی:فقط دقت کنید مقادیر شرط رو داخل براکت {} باید بنویسید که در انتها توضیح کامل تر رو در این باره خواهم داد.عملگر NOT IN :و اما در صورتی که بخواهیم شرط رو به صورت عکس بنویسیم فرمول ها بالا با NOT به غیر مقادیر شرط تغییر می کنند یعنی:Not_RedOrGreen_OR_Operator = 
CALCULATE (
    SUM(&#039;Table&#039;[Amount]),
    NOT &#039;Table&#039;[Color] = &quot;Red&quot; ||&#039;Table&#039;[Color] = &quot;Green&quot;
)

Not_RedOrGreen_CONTAINSROW = 
CALCULATE (
    SUM(&#039;Table&#039;[Amount]),
    NOT CONTAINSROW({&quot;Red&quot;, &quot;Green&quot;}, &#039;Table&#039;[Color] 
))

Not_RedOrGreen_IN = 
CALCULATE (
    SUM(&#039;Table&#039;[Amount]),
     NOT &#039;Table&#039;[Color] IN {&quot;Red&quot;, &quot;Green&quot;}
)تابع CONTAINS :فقط جهت اطلاع: اگه دقت کرده باشید یا با تابع CONTAINS کار کرده باشید (احتمالا در ایجاد Dynamic RLS) متوجه شباهت هایی بین CONTAINS و CONTAINSROW شدید. تابع CONTAINSROW هم خودش ترکیبی خلاصه شده از تابع CONTAINS هست. در تابع CONTAINS یک ارگومان با نام جدول هم داریم که در این مثال اگر بخواهیم فرمول بالا رو با این تابع بازنویسی کنیم خواهیم داشت:RedORGreen_CONTAINS = CALCULATE (
    SUM(&#039;Table&#039;[Amount]),
    FILTER (
        ALL ( &#039;Table&#039;[Color] ),
        CONTAINS (
            DATATABLE ( &quot;Color&quot;, STRING, { { &quot;Red&quot; }, { &quot;Green&quot; } } ),
            [Color], &#039;Table&#039;[Color]
        )
    )
)گرفتن مقادیر شرط از جدولی دیگر :البته که شما می تونید مقادیر شرط خودتون رو از جدول دیگری دریافت کنید و الزامی به نوشتن شرایط در کد نیست. به طور مثال اگر جدولی با نام Conditions داشته باشیم که این دو رنگ داخل آن باشند فرمول بالا رو به صورت زیر می تونیم بازنویسی کنیم:RedORGreen_From_Other_Table = 
CALCULATE (
    SUM(&#039;Table&#039;[Amount]),
    &#039;Table&#039;[Color] IN VALUES(Conditions[Colors])
)CALCULATE ( از این روش می تونید برای محاسباتی که امکان استفاده از روابط (relations) وجود نداره هم استفاده کنید.چندین ستون و شرط :برای این که چندین ستون و چندین سطر شرط رو داشته باشیم تنها کافیه مقادیر رو به صورت row constructor یعنی داخل پرانتز بنویسیم. در مثال بالا در صورتی که به داده ها ستونی به نام Group اضافه کنیم شرط به این صورت خواهد شد:    ( &#039;Table&#039;[Color], &#039;Table&#039;[Group] )
        IN {
            ( &quot;Red&quot;, &quot;Group2&quot; ),
            ( &quot;Blue&quot;, &quot;Group2&quot; )
        }که لازمه شرط رو با استفاده از تابع FILTER بنویسیم.Multiple_Columns = CALCULATE (
    SUM(&#039;Table&#039;[Amount]),
    FILTER(ALL( &#039;Table&#039;[Color], &#039;Table&#039;[Group] ), 
    ( &#039;Table&#039;[Color], &#039;Table&#039;[Group] )
        IN {
            ( &quot;Red&quot;, &quot;Group2&quot; ),
            ( &quot;Blue&quot;, &quot;Group2&quot; )
        } 
))البته در مثال بالا ستون های مورد نظر رو به ALL اضافه کردم تا در نمایش مقادیر رو بتونیم مشاهده کنیم و نه جمعش رو! (شما بر حسب نیاز باید تغییر بدید)Table Constructor و Row Constructor :در صورتی که بخواهیم جدولی ایجاد کنیم در کد DAX، برای این کار مجموعه ای با استفاده از براکت {} می نویسیم که به اون table constructor گفته میشه. این مجموعه شامل سطرهایی هستند که با استفاده از پرانتز از هم جدا شدندکه به اون row constructor گفته میشه. به این صورت:{
    ( &quot;Red&quot;, &quot;Group1&quot; ),
    ( &quot;Blue&quot;, &quot;Group2&quot; )
}مثال بالا؛ مقادیر دو ستون Color و Group و دو سطر رو تشکیل میدهند که با پرانتز و کامل از هم جدا شدند.اما در صورتی که این جدول یک ستون داشته باشه می تونیم از پرانتزها صرف نظر کنیم.{ ( &quot;Red&quot; ), ( &quot;Blue&quot; ) }که عبارت زیر برابر عبارت بالا می باشد:{ &quot;Red&quot;, &quot;Blue&quot; }نتیجه این موضوع اینه که عبارت تک شرط که در مثال ابتدایی نوشتیم و تنها شامل یک ستون بود خلاصه این عبارته و اگر جایی دیدید به این صورت فرمول نوشته شده! بدونید این الگو اصلی table constructor هستش.یعنی عبارت زیرRedOrGreen_IN = 
CALCULATE (
    SUM(&#039;Table&#039;[Amount]),
    &#039;Table&#039;[Color] IN {&quot;Red&quot;, &quot;Green&quot;}
)با این عبارت معادل هستشRedOrGreen_IN_table_constructor = 
CALCULATE (
    SUM(&#039;Table&#039;[Amount]),
    &#039;Table&#039;[Color] IN {(&quot;Red&quot;), (&quot;Green&quot;)}
)</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Mon, 10 Nov 2025 14:09:44 +0330</pubDate>
            </item>
                    <item>
                <title>Field Parameter در Power BI (نحوه داینامیک کردن نمودار Power BI)</title>
                <link>https://virgool.io/@rahtav68/field-parameter-%D8%AF%D8%B1-power-bi-%D9%86%D8%AD%D9%88%D9%87-%D8%AF%D8%A7%DB%8C%D9%86%D8%A7%D9%85%DB%8C%DA%A9-%DA%A9%D8%B1%D8%AF%D9%86-%D9%86%D9%85%D9%88%D8%AF%D8%A7%D8%B1-power-bi-xemmuwrlovki</link>
                <description>با استفاده از Filed Parameter این امکان برای شما فراهم می شود که مقادیر و سنجه ها رو بتوانید به صورت داینامیک از visual حذف یا به آن اضافه کنید. این قابلیت انعطاف بیشتری برای ساخت نمودارها با چندین بعد مختلف و یا محاسبات مختلف رو برای شما فراهم می کند. روش دیگر برای این کار استفاده از switch است که پستی جدا بررسی خواهیم کرد. البته که این روش بسیار ساده تر و عملکرد بهتری دارد.از تب Modeling گزینه New Parameter و سپس گزینه Field رو انتخاب کنید.در پنجره باز شده نامی برای پارامتر خود انتخاب کنید و ستون های مورد نظر رو انتخاب کنید.خروجی جدولی خواهد بود که با این عبارت ساخته شد که از آن می توانید در visual خود استفاده کنید.FieldParameter = {
    (&quot;Product&quot;, NAMEOF(&#039;data&#039;[Product]), 0),
    (&quot;ProductCategory&quot;, NAMEOF(&#039;data&#039;[ProductCategory]), 1),
    (&quot;Color&quot;, NAMEOF(&#039;data&#039;[Color]), 2)
}نکته:  البته قابلیت ویرایش رو هم دارید. می تونید نام ستون یا ترتیب قرارگیریش رو تغییر بدید یا ستون دیگری اضافه یا حذف کنید.حالا همین فرآیند رو برای Measure ها انجام می دهیم.</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Fri, 07 Nov 2025 11:21:49 +0330</pubDate>
            </item>
                    <item>
                <title>varchar و nvarchar</title>
                <link>https://virgool.io/@rahtav68/varchar-%D9%88-nvarchar-hxf2p2o5diy8</link>
                <description>در مواجه با این دو data type پر استفاده SQL Server، اولین سوالی که براتون پیش میاد اینه که کدوم رو باید استفاده کنم؟ برای پاسخ به این سوال باید تفاوت های این دو data type رو بدونیم. و این که استفاده از هر کدوم چه مزیتی برای مدل ما میتونه داشته باشه؟ آیا ارتباطی با تفاوت های زبانی وجود داره؟ مثلا اگه از زبان فارسی در دیتابیس استفاده میشه آیا الزاما باید از nvarchar استفاده کرد؟Character Encodingهمونطور که میدونیم یا لازمه بدونیم! کامپیوترها زبانی که ما صحبت می کنیم رو به این صورت که ما متوجه میشویم متوجه نمیشوند! که برای ارتباط با کامپیوترها، کاراکترهای متنی و عددی و غیره لازمه به 0 و 1 و به صورت باینری تبدیل شوند. character encoding روش یا فرآیندیه که این تبدیل رو انجام میده. یعنی مقادیر عددی یا الفبای زبان های مختلف و هر نوع کاراکتر دیگه رو به شکل های منحصر به فردی (unique) تبدیل میکنه و در نهایت به مقادیر باینری که امکان ذخیره سازی بر روی قطعات Storage رو داشته باشند.اما استاندارد های (character set) متفاوتی برای این تبدیل ها وجود داره که از ابتدایی ترین اون ها و البته رایج ترین این مجموعه های کاراکتری، ASCII هستش که شامل مقادیر عددی و حروف الفبای انگلیسی و کاراکترهای رایج دیگه مثل ! و ؟ و @ و غیره هست. این charset شامل 128 کاراکتر هستش که از 0 تا 127 شماره گذاری شدند. که در SQL Server رایج ترین code page مورد استفاده 1252 هستش.code page: به معنی مقادیر عددی unique هست که برای تبدیل کاراکترها استفاده میشه که رایج ترین استاندارد مورد استفاده 437 و 1252 هست.اما با وجود این حجم از کاراکترها و زبان ها که به طور مثال زبان چینی که حدود 5000 کاراکتر رو شامل میشه، نیاز به استفاده از charset های جامع تر از مثلا ASCII ها بود. که همین موضوع باعث شد در سال 1991 سازمانی غیر انتفاعی با نام Unicode Consortium بنیان گذاشته بشه که مسئولیت نگه داری و تنظیم این مقادیر و encoding scheme های مرتبط رو عهده دار بشه حتی ایموجی ها 😁!Unicode: مخفف Universal Character Encoding و البته در برخی منابع Universally Coded Character Set ذکر شده.از جمله معروف ترین این encoding schemes ها که Unicode Consortium مسئولیت نگه داری و تنظیم اون رو بر عهده داشته UTF-8 هست. که UTF مخفف Unicode Transformation Format و 8 به معنای اینه که هر byte یا 8 bit نشان دهنده یه کارکتر هست. این charset شامل 1,112,064 character point هستش.اما این خلاصه ای از معرفی مفاهیم Encoding و Unicode بود تا بتونیم وارد بحث اصلی بشیم.varcharهمونطور که از اسمش مشخصه این data type به صورت متغیر مقادیر رو ذخیره سازی میکنه. یعنی فضای ثابتی رو برخلاف char اشغال نمیکنه و با توجه به طول رشته فضا در نظر میگیره. varchar، امکان ذخیره سازی اعداد، کاراکترهای متنی و special characters ها رو داره. از نسخه 2008 به بعد، SQL Server، امکان ذخیره سازی تا ماکزیمم 8000 کاراکتر (maximum length) رو در این data type داره. البته با استفاده از max در قسمت طول کاراکتر امکان ذخیره سازی تا 2GB داده وجود داره ولی نوع ذخیره سازی به صورت LOB خواهد بود. varchar فضایی معادل 1 byte برای هر کاراکتر اشغال میکنه و البته به علاوه 2 byte برای اطلاعات طول رشته. در صورتی که طول رشته وارد نشه، داده ها truncate خواهند شد و به صورت پیش فرض فقط کاراکتر اول ذخیره میشه. از نسخه 2019 SQL Server قابلیتی به این data type اضافه شده که امکان پشتیبانی از کاراکترهای UTF-8 در صورت فعال بودن collation های از نوع UTF-8 در اون وجود داره. که این قابلیت جدید امکان ذخیره سازی مقادیر Unicode رو میده که مزیت اصلی اون کم کردن مشکلات و دردسرهای مربوط به convert های داده هاست. و البته استفاده کمتر از فضای ذخیره سازی که برای کاراکترهای Unicode نیز در این حالت برابر 1byte خواهد بودو باعث بهبود عملکرد ذخیره سازی میشه.nvarcharاما از جمله تفاوت هایی که nvarchar در مقابل varchar داره طول ماکزیمم 4000 کاراکتر برای رشته هست. مورد دیگه ذخیره سازی دو برابری nvarchar هستش که هر کاراکتر در nvarchar معادل 2byte فضا اشغال میکنه. همچنین مورد استفاده nvarchar برای کاراکترهای Unicode هست که varchar اون ها رو شامل نمیشه، حداقل در نسخه های قبل از 2019. از نسخه SQL Server 2012 امکان ذخیره سازی UTF-16 در این data type فراهم شده.اما چه زمان از nvarchar استفاده کنیم؟این سوال به دو عامل بستگی داره اولی collation و دومی نسخه SQL Server مورد استفاده.زمانی از nvarchar استفاده می کنیم که collation مورد استفاده از کاراکترهایی که قراره ذخیره سازی شوند، پشتیبانی نمیکنه. مثلا چندین زبان مختلف داره استفاده میشه یا کاراکترهایی مثل ایموجی یا notation های خاصی که در collation تنظیم شده پشتیبانی نمیشهاما اگر از نسخه SQL Server 2019 به بعد استفاده می کنیم، با تنظیم collation مناسب از نوع utf-8 امکان استفاده از varchar برای کاراکترهای Unicode وجود خواهد داشت. در غیر این صورت یعنی اگر از نسخه های قدیمی تر از 2019 استفاده می کنیم nvarchar چاره کار خواهد بود. البته با در نظر گرفتن مورد اول!نکته لازم به توضیحه که در varchar مکانیزم ذخیره سازی 8 بیتیه (8bit) که که مشابه و قابلیت سازگاری (combability) با ASCII رو داره. این سیستم 8 بیتی البته که منابع مصرفی کمتری رو  نسبت به UTF-16 استفاده می کنه.برای فهمیدن collation هر دیتابیس میتونید از script زیر استفاده کنید:SELECT 
	name, 
	collation_name 
FROM 
	sys.databasesبرای پیدا کردن و جستجو در collation ها هم از script زیر:SELECT 
	name, 
	description 
FROM 
	sys.fn_helpcollations()
WHERE
	name LIKE &#039;%UTF%&#039;</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Thu, 06 Nov 2025 09:39:47 +0330</pubDate>
            </item>
                    <item>
                <title>تبدیل مقادیر ستون به مقادیر جدا شده با کاما و برگردان سطر به سطر در SQL</title>
                <link>https://virgool.io/@rahtav68/%D8%AA%D8%A8%D8%AF%DB%8C%D9%84-%D9%85%D9%82%D8%A7%D8%AF%DB%8C%D8%B1-%D8%B3%D8%AA%D9%88%D9%86-%D8%A8%D9%87-%D9%85%D9%82%D8%A7%D8%AF%DB%8C%D8%B1-%D8%AC%D8%AF%D8%A7-%D8%B4%D8%AF%D9%87-%D8%A8%D8%A7-%DA%A9%D8%A7%D9%85%D8%A7-%D9%88-%D8%A8%D8%B1%DA%AF%D8%B1%D8%AF%D8%A7%D9%86-%D8%B3%D8%B7%D8%B1-%D8%A8%D9%87-%D8%B3%D8%B7%D8%B1-%D8%AF%D8%B1-sql-zznx2a7z4ips</link>
                <description>عنوان عجیبیه ولی هر طور بخواهم تغییرش بدم به نظرم منظور رو نمی رسونه! برای همین خروجی که مد نظرمون هست رو در شکل زیر مشاهده می کنید. می خواهیم مقادیر این ستون ها رو با استفاده از کاما (ویرگول) از هم جدا کنیم و به صورت سطری برابر id مورد نظرشون قرار بدیم. هر چند که این کار با استفاده از زبان های برنامه نویسی چندان سخت نخواهد بود ولی در sql به چه صورت میشه این خروجی رو پیاده کرد هدف این پست خواهد بود. در ادامه به استفاده از دو روش پرکاربردتر و بهینه تر و البته ساده تر از مجموعه روش های موجود می پردازیم (پیشنهاد خودم روش دومه 😁). البته روش های دیگه نوشتن CLR یا Recursive CTE یا دستورهای while و cursor منظورمه! راستی با حضور افتخاریه STRING_SPLITو اما داده ها عبارت است از جدول زیر:DECLARE @t TABLE (id INT NOT NULL, value varchar(20) NULL);
 
INSERT @t VALUES 
	(1, &#039;A&#039;), 
	(1, &#039;B&#039;),  
	(1, &#039;C&#039;),  
	(2, &#039;X&#039;), 
	(2, &#039;Y&#039;),
    (2, &#039;Z&#039;), 
	(3, &#039;I&#039;), 
	(3, &#039;II&#039;);
 
SELECT * FROM @tروش اول: استفاده از XML PATHجواب:SELECT DISTINCT
	T.id, T.Result
FROM 
	@t AS t_out
CROSS APPLY
	(
		SELECT 
			id,
			STUFF(
				(
					SELECT 
						&#039;,&#039; + value
					FROM @t AS t_in
					WHERE t_in.id=t_out.id
					FOR XML PATH (&#039;&#039;)
				)
				,1
				,1
				,&#039;&#039;
			) AS Result
	) AS Tاگر کوئری بالا رو بخواهیم قدم به قدم پیاده کنیم، قدم اول اضافه کردن کاما به همه مقادیر ستون value خواهد بود:SELECT 
	&#039;,&#039; + value
FROM @t AS t_inخروجی:,A
,B
,C
,X
,Y
,Z
,I
,IIمرحله بعد تبدیل رکورد ها به یک مقدار هستش که با استفاده از XML PATH می تونیم اون رو تبدیل به یک عبارت XML کنیم.SELECT 
	&#039;,&#039; + value
FROM @t AS t_in
FOR XML PATH (&#039;&#039;)نوبت حذف کاما اول هستش! که برای این کار از تابع STUFF استفاده می کنیم. این تابع چهار آرگومان به شکل زیر می گیره. آرگومان اول متن، ارگومان دوم شماره کاراکتر شروع در متن و آرگومان سوم طول کاراکترهای مورد نظر برای جایگزینی و آرگومان آخر هم متن جایگزینه. در مثال ما آرگومان اول یعنی متن، خروجی بالا خواهد بود. و از کاراکتر شماره 1 شروع می کنیم. و به همون اندازه 1 کاراکتر ادامه می دهیم. و با ” که متنی بدون هیچ کاراکتره جایگزین می کنیم. و این باعث حذف کاما اول خواهد شد.STUFF ( character_expression , start , length , replaceWith_expression )SELECT STUFF(&#039;,A,B,C,X,Y,Z,I,II&#039;, 1, 1, &#039;&#039;)SELECT 
	STUFF(
		(
			SELECT 
				&#039;,&#039; + value
			FROM @t AS t_in
			FOR XML PATH (&#039;&#039;)
		)
		,1
		,1
		,&#039;&#039;
	) AS Resultخروجی:A,B,C,X,Y,Z,I,IIحالا با استفاده از CROSS APPLY خروجی بالا رو به id مورد نظر خودش اختصاص می دهیم.SELECT 
	T.id, T.Result
FROM 
	@t AS t_out
CROSS APPLY
	(
		SELECT 
			id,
			STUFF(
				(
					SELECT 
						&#039;,&#039; + value
					FROM @t AS t_in
					WHERE t_in.id=t_out.id
					FOR XML PATH (&#039;&#039;)
				)
				,1
				,1
				,&#039;&#039;
			) AS Result
	) AS Tخروجی:و اما در نهایت لازمه تکراری ها رو حذف کنیم. و کوئری نهایی به این صورت خواهد بود:SELECT DISTINCT
	T.id, T.Result
FROM 
	@t AS t_out
CROSS APPLY
	(
		SELECT id, STUFF((SELECT &#039;,&#039; + value FROM @t AS t_in WHERE t_in.id=t_out.id FOR XML PATH (&#039;&#039;)),1,1,&#039;&#039;) AS Result
	) AS Tروش دوم: استفاده از STRING_AGGروش دوم بسیار ساده تر و کوتاه تر از روش اوله. البته از این تابع به جای XML PATH استفاده می کنیم تا مقادیر رو به صورت Comma Separated بدست بیاریم.جهت اطلاع: این تابع از SQL نسخه 2017 به بعد قابل استفاده و در دسترسه.جواب:SELECT id, STRING_AGG(value, &#039;, &#039;) AS Result 
FROM @t
GROUP BY idخروجی:و تنها نکته این که برای استفاده از STRING_AGG باید مقادیر دیگر در GROUP BY قرار بگیرند. این تابع هم مثل SUM و COUNT و مابقی توابع AGGREGATION عمل میکنهمعرفی STRING_SPLITبد نیست یک معرفی کوتاهی هم از تابع STRING_SPLIT داشته باشیم که دقیقا برعکس عمل STRING_AGG رو انجام میده فرض کنید داده های زیر رو داریم:DECLARE @t TABLE (id INT NOT NULL, Result varchar(20) NULL);
 
INSERT @t VALUES 
	(1, &#039;A, B, C&#039;), 
	(2, &#039;X, Y, Z&#039;),  
	(3, &#039;I, II&#039;);
 
SELECT * FROM @tبرای استفاده از این تابع آن را در قسمت FROM قرار می دهیم به این صورت:SELECT * FROM STRING_SPLIT(&#039;A, B, C&#039;, &#039;,&#039;)خروجی:و اما جواب! برای گسترده کردن (expand) سطرها لازمه از CROSS APPLY استفاده کنیم:SELECT tbl.id, T.value
FROM @t AS tbl
CROSS APPLY STRING_SPLIT(tbl.Result, &#039;,&#039;) AS Tخروجی:فقط دقت کنید value نام ستونی است که خود تابع ایجاد میکنه! و همچنین برای separator هم تنها از یک کارکتر میتونید استفاده کنید یعنی: nvarchar(1), varchar(1), nchar(1), char(1)</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Wed, 05 Nov 2025 17:46:00 +0330</pubDate>
            </item>
                    <item>
                <title>پاس دادن پارامتر از داخل گزارش به کوئری در حالت Direct Query</title>
                <link>https://virgool.io/@rahtav68/%D9%BE%D8%A7%D8%B3-%D8%AF%D8%A7%D8%AF%D9%86-%D9%BE%D8%A7%D8%B1%D8%A7%D9%85%D8%AA%D8%B1-%D8%A7%D8%B2-%D8%AF%D8%A7%D8%AE%D9%84-%DA%AF%D8%B2%D8%A7%D8%B1%D8%B4-%D8%A8%D9%87-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%D8%AF%D8%B1-%D8%AD%D8%A7%D9%84%D8%AA-direct-query-ad5oh0ica13j</link>
                <description>در این پست به بررسی امکان پاس دادن پارامتر ورودی به کوئری مورد نظر در حالت Direct Query می پردازیم. در این حالت شما این امکان رو در Power BI دارید که کوئری مورد نظرتون رو به صورت داینامیک شرط گذاری کنید. و در ازای ورود و انتخاب مقدار یا مقادیر مورد نظرتون داده های مورد انتظار رو دریافت کنید. برای ساده تر شدن راهکار از یک مثال ساده استفاده می کنیم. برای ایجاد این مثال از کد زیر می تونید استفاده کنید:USE test
GO

DROP TABLE test_table
GO

CREATE TABLE [dbo].[test_table](
	Title VARCHAR(10) NOT NULL,
	Qty INT NOT NULL
)
GO


INSERT INTO test_table VALUES
(&#039;A&#039;, 100),
(&#039;B&#039;, 200),
(&#039;C&#039;, 300),
(&#039;D&#039;, 400),
(&#039;E&#039;, 500)جهت یادآوری: لازمه دیتابیس مورد نظر خودتون رو انتخاب کنید.اما مزایای روش Direct Query:در این روش بر خلاف روش import داده ای ذخیره نمیشه همین هم Direct Query رو روش خوبی برای کار با حجم زیاد داده کردهآخرین و به روز ترین داده ها رو در اختیار داریدمحدودیت حجم 1 گیکاباتی در این روش وجود نداردبرای استفاده از این روش کافیه گزینه Direct Query رو از قسمت Data connectivity mode انتخاب کنید.مثال اول: پارامتر تک مقداردر این مثال شرط شما، شامل یک مقدار خواهد بود. به این صورت که با انتخاب Title مورد نظر از داده ها، یک Native Query به سمت دیتابیس می رود و خروجی به ازای انتخاب شما برگردانده می شود.قدم اول: ورود داده در حالت Direct Queryابتدا کوئری مورد نظر رو به صورت یک شرط ثابت و در حالت Direct Query وارد می کنیم.SELECT * FROM test_table WHERE Title=&#039;A&#039;خروجی:در صورتی که پنجره Advanced Editor رو چک کنید، عبارت M زیر را خواهیم داشت:let
    Source = Sql.Database(&quot;localhost&quot;, &quot;test&quot;, [Query=&quot;SELECT * FROM test_table WHERE Title=&#039;A&#039;&quot;, CreateNavigationProperties=false])
in
    Sourceقدم دوم: ایجاد Parameterبرای ساخت پارامتر از تب Home گزینه Manage Parameters  یا New Parameter رو انتخاب می کنیم.در پنچره باز شده پارامتری با نام Parameter و مقدار پیش فرض A می سازیم.قدم سوم: استفاده از Parameter در M Languageقبل از استفاده از پارامتر، صرفا جهت خوانا بودن عبارت M، کوئری نوشته شده رو با استفاده از متغیری به نام query به Source پاس می دهیم به این صورت:let
    query = &quot;SELECT * FROM test_table WHERE Title=&#039;A&#039;&quot;,
    Source = Sql.Database(&quot;localhost&quot;, &quot;test&quot;, [Query=query, CreateNavigationProperties=false])
in
    Sourceنکته: دقت کنید کاما آخر خط فراموش نشود در غیر این صورت با خطا مواجه می شوید.برای وارد کردن پارامتر ایجاد شده عبارت مقابل query رو به این شکل تغییر می دهیم:query = &quot;SELECT * FROM test_table WHERE Title=&quot; &amp; &quot;&#039;&quot; &amp; Parameter &amp; &quot;&#039;&quot;در این عبارت پارامتر ساخته شده یعنی Parameter، جایگزین مقدار A که به صورت ثابت در شرط وجود داشت می شود. دقت کنید! که لازمه Single Quotation ها رو هم در عبارت لحاظ کنید.در این عبارت پارامتر ساخته شده یعنی Parameter، جایگزین مقدار A که به صورت ثابت در شرط وجود داشت می شود. دقت کنید! که لازمه Single Quotation ها رو هم در عبارت لحاظ کنید.قدم چهارم و آخر: نحوه Bind فیلد مورد نظر به پارامتربرای استفاده از پارامتر، لازمه فیلدی که حاوی مقادیر برای شرط هست رو به پارامتر bind کنیم. برای این کار جدولی دیگه شامل صرفا Title ها هم ایجاد کردم و ستون این جدول رو به Parameter وصل می کنم. برای اتصال، از تب Model بر روی ستون مورد نظر کلیک کنید و از قسمت Properties و بخش Advanced و Bind to parameter، از لیست باز شده نام پارامتر ساخته شده رو انتخاب کنید.اگر slicer ای از مقادیر ستون bind شده ایجاد کنیم، به ازای هر انتخاب شرط کوئری به مقدار انتخابی تغییر میکنه و داده های مورد نظر از دیتابیس گرفته میشه.دو نکته در این مثال قابل توجه است:در صورت اجرا و انتخاب مقدار، هشداری به صورت زیر دریافت خواهید کرد. که برای رفع اون لازمه تیک Require user approval for new native database queries از پنچره Options و قسمت Security رو بردارید. البته به موارد امنیتی دقت کنید و قبل از استفاده حتما در موردش تحقیق کنید!اما نکته دوم این که در مثال اول فقط یک مقدار به عنوان پارامتر قابل پاس دادن است. اگر مجموعه از مقادیر رو انتخاب کنید با خطا رو به رو می شوید. این خطا نشان می دهد که امکان استفاده از عملگر &amp; رو در عبارت M ندارید. یعنی لیست رو نمی تونیم به صورت متن به کوئری پیوست کنیم.مثال دوم: پارامتر چند مقداره و Select Allروند کار برای این مثال کاملا مشابه مثال اول است با این تفاوت که عبارت M نوشته شده به گونه ای است که در صورت انتخاب چند گزینه و همچنین Select All، با استفاده از شرط هایی که گذاشته شده امکان تغییر کوئری به سه صورت فراهم می شود.پارامتر رو با نام ParameterMultiple نعریف می کنم و مقدار پیش فرض رو برابر SelectAll قرار می دهم.عبارت M رو به صورت زیر در حالت های مختلف می نویسیم:let
    selected_values = if Type.Is(Value.Type(ParameterMultiple), List.Type) then 
                        Text.Combine({&quot;&#039;&quot;, Text.Combine(ParameterMultiple, &quot;&#039;,&#039;&quot;) , &quot;&#039;&quot;})
                      else  
                        Text.Combine({&quot;&#039;&quot; , ParameterMultiple , &quot;&#039;&quot;}),
    query = if ParameterMultiple = &quot;__SelectAll__&quot; or List.Contains(ParameterMultiple, &quot;__SelectAll__&quot;) then
                &quot;SELECT * FROM test_table&quot; 
            else 
                &quot;SELECT * FROM test_table WHERE Title IN(&quot; &amp; selected_values &amp; &quot;)&quot;,
    Source = Sql.Database(&quot;.&quot;, &quot;test&quot;, [Query=query, CreateNavigationProperties=false])
in
    Sourceدر صورتی که پارامتر به از نوع لیست بود، لازمه مقادیر لیست به متن تبدیل شوند و هر کدوم از مقادیر با کاما از هم جدا شوند. به طور مثال در صورت انتخاب A و B از slicer، مقادیر پارامتر که از جنس لیست می باشند رو به متن تبدیل و با کاما جدا می کنیم یعنی خروجی به این صورت می شود ” ‘A’, ‘B’ “.در صورتی که جنس پارامتر از نوع لیست نبود و به صورت متن بود مشابه مثال اول یک مقدار پاس داده می شود که تنها لازم است Single Quotation در دو طرف آن قرار داد. یعنی خروجی به این صورت میشود ” ‘A’ “در صورتی که پارامتر برابر SelectAll بود کوئری بدون شرط برقرار می شود.در صورتی که مقدار SelectAll انتخاب نشده بود عبارت مقابل selected_values که در شماره 1 و 2 بوجود آمدند به کوئری ضمیمه می شوند. دقت کنید که در کوئری این قسمت از IN برای گرفتن چند شرط استفاده شده.و آخرین نکته، این که گزینه Multi-select و  Select All رو حتما فعال کنید.</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Tue, 04 Nov 2025 07:03:34 +0330</pubDate>
            </item>
                    <item>
                <title>تابع RANKX در فرمول نویسی DAX</title>
                <link>https://virgool.io/@rahtav68/%D8%AA%D8%A7%D8%A8%D8%B9-rankx-%D8%AF%D8%B1-%D9%81%D8%B1%D9%85%D9%88%D9%84-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-dax-x1znbd2fpoc6</link>
                <description>رتبه بندی از جمله محاسبات پر کاربرد در گزارش هاست. گزارش هایی مثل رتبه بندی فروشندگان یا بدست آوردن پر فروش ترین محصولات. در بین توابع DAX از تابع RANKX به این منظور استفاده میشه. از این تابع برای برگرداندن  شماره و رتبه مقادیر مورد نظر در لیستی از اعداد استفاده میشه. قراره در این پست به بررسی این تابع بپردازیم و البته چند نکته هم در مورد نحوه استفاده بهتر از این تابع رو با هم بررسی کنیم!الگو یا syntax تابع RANKXRANKX(&lt;table&gt;, &lt;expression&gt;[, &lt;value&gt;[, &lt;order&gt;[, &lt;ties&gt;]]])آرگومان اول table: نام جدول مورد نظر برای محاسباتآرگومان دوم expression: هر نوع تابع DAX یا measure یا عبارت محاسباتی که یک مقدار یعنی scalar برگردونهبرای محاسبه رتبه فقط همین دو مقدار اول لازم هستند و مابقی آرگومان ها اختیاری هستند که البته جلوتر به مثال های کامل تر از نحوه استفاده از اون ها می پردازیم.فرض کنید جدولی با داده های زیر داریم که شامل نام محصول و تعداد مبلغ فروش باشه:در جدول بالا قراره رتبه رو براساس Qty حساب کنیم. در ابتدا روش هایی که به جواب غلط میرسونند ما رو تست می کنیم تا به روش صحیح دست پیدا کنیم!در صورتی که فرمول رو به صورت زیر بنویسیم با خطایی مواجه میشیم که دقیقا همون شرطیه که در expression توضیح دادیم. یعنی خروجی حتما و حتما لازمه scalar باشه.rank_0 = RANKX(&#039;Table&#039;, &#039;Table&#039;[Qty])البته دقت کنید که اگر همین فرمول رو به عنوان CALCULATED COLUMN اضافه کنیم به درستی محاسبات صورت می گیره چون در جدول سطر به سطر فیلتر داریم.پس با این توضیحات اگه به آرگومان دوم فقط یک مقدار عددی رو اضافه کنیم خطایی دریافت نمی کنیم (مسلما جواب درستی نیست) به این صورت:rank_1 = RANKX(&#039;Table&#039;, 1)در نتیجه اگه در این مثال به آرگومان دوم مقدار جمع رو اضافه کنیم که خروجیش فقط یک مقداره و جمع مقدار محصولمون هست پس احتمالا به نتیجه می رسیم! 🤔rank_2 = RANKX(&#039;Table&#039;, SUM(&#039;Table&#039;[Qty]))و اما نشد!روش صحیح برای MEASURE و اما چرا؟مشکلی که وجود داره اینه که SUM محاسبات رو به صورت Row Context انجام میده و اگه شما به عنوان expression اون رو در داخل این تابع و measure وارد کنید به نتیجه نمی رسید.برای این که عملکرد درستی داشته باشه لازمه تبدیلش کنیم به Filter Context. دو روش برای این کار داریم:روش اول؛ استفاده از CALCULATE که این تبدیل رو انجام میدهrank_3 =
RANKX (
    ALL ( &#039;Table&#039; ),
    CALCULATE (
        SUM ( &#039;Table&#039;[Qty] )
    )
)روش دوم؛ استفاده از یک measure دیگه که دقیقا در پس زمینه CALCULATE رو داره انجام میده!SumQty = SUM(&#039;Table&#039;[Qty])rank_4 = RANKX(ALL(&#039;Table&#039;), [SumQty])و اما از تابع ALL برای نمایش و برگردوندن لیست محصولات در جدول استفاده می کنیم که البته میتونید از ALLSELECTED هم استفاده کنید. و در اصل با استفاده از ALL فیلترهای اون جدول یا ستون رو که به صورت Filter Context هستند حذف می کنیم.  و البته اینکار رو انجام میدیم تا بتونیم مقادیر رو در اون سطح نمایش بدیم. جهت اطلاع: اگه شما از VALUES استفاده کنید به جای ALL یا ALLSELECTED در حقیقت داده ای که بر میگرده جدولی با یک سطر خواهد بود و فقط مقدار 1 رو بر می گردونه.حذف RANK از قسمت TOTALایردای که در خروجی های بالا وجود داره، بودن عدد 1 در قسمت Total هستش که برای حذف کردنش می تونیم از تابع ISINSCOPE استفاده کنیم. تابع ISINSCOPE ( ‘Table&#039;[Product] ) در صورتی که اون سطح از داده در بازه مورد نظر باشه true بر می گردونه پس چون Total در مجموعه Product نیست پس BLANK خواهد شد.rank_5 = 
IF (
    ISINSCOPE ( &#039;Table&#039;[Product] ),
    RANKX (
        ALL ( &#039;Table&#039; ),
        [SumQty]
    )
)و اما آرگومان سوم valueاحتمالا اگه از RANKX استفاده کرده باشید پیش اومده که این بخش رو اصلا پر نکنید و به آرگومان های بعدی برید! ولی این آرگومان دقیقا چه کاربردی میتونه داشته باشه؟!آرگومان سوم value: این مقدار اختیاریه و شامل هر نوع عبارت DAX میشه که مثل قسمت دوم تابع، مقدار scalar برگردونه. اگه خالی گذاشته بشه و تکمیل نشه مقدار از آرگومان قبلی جایگزینش میشه! مثلا رتبه مقدار 50 در مثال 3 بود (تصویر بالا) با وارد کردن مقدار 50 در این قسمت رتبه 50 یعنی 3 برگردونده میشه.rank_6 = 
IF (
    ISINSCOPE ( &#039;Table&#039;[Product] ),
    RANKX (
        ALL ( &#039;Table&#039; ),
        [SumQty],
        10
    )
)این مقدار با توجه به بازه عددی، کمترین مقدار اون بازه رو بر میگردونه. مثلا برای 65 کوچکترین مقدار به 65 از لیست اعدادی که رتبه بندی کردیم 60 هست و رتبه 60 هم 2 هستش پس خروجی 2 خواهد شد.برای نشون دادن نمونه ای از کاربرد این آرگومان به مثال بالا جدول زیر رو اضافه کنیم که بازه بندی برای رتبه ها خواهد بود. و البته ستون Rank هم برای نمایش بهتر خروجی اضافه کردم که نیازی نبود.و الان در بین بازه های بالا که 4 دسته خواهد بود هر مقداری از جدول مثال قبل که در این بازه باشد رتبه اون مقدار از جدول Bins پیدا میشه و بازگردونده میشه.rank_7 = 
IF (
    ISINSCOPE ( &#039;Table&#039;[Product] ),
    RANKX (
        ALL ( Bins ),
        Bins[Value],
        [SumQty],
    )
)همین مثال رو بر روی ستون Sales Amount هم انجام دهیم خواهیم داشت:rank_8 = 
IF (
    ISINSCOPE ( &#039;Table&#039;[Product] ),
    RANKX (
        ALL ( BinsAmount ),
        BinsAmount[Value],
        CALCULATE(SUM(&#039;Table&#039;[Sales Amount])),
    )
)آرگومان چهارم orderهمونطور که از اسمش مشخصه و توضیح لازم نداره این آرگومان ترتیب رو برای رتبه بندی مشخص میکنه.مقدار 0 یا False یا DESC: به معنای ترتیب نزولیه یعنی از بزرگترین مقدار به کوچکترین (پیش فرض)مقدار 1 یا True یا ASC: به معنای صعودی بودنه که از کوچکترین به بزرگترین خواهد بودrank_9 = 
IF (
    ISINSCOPE ( &#039;Table&#039;[Product] ),
    RANKX (
        ALL ( &#039;Table&#039; ),
        [SumQty],
        , ASC
    )
)آرگومان پنجم tiesSkip (پیش فرض): اگر مقدار تکراری در ارقام باشه اون مقادیر تکراری همه یک رتبه خواهند داشت و رتبه رقم بعدی مجموع رتبه قبلی به علاوه تعداد اون ارقام تکراریه.rank_10 = 
IF (
    ISINSCOPE ( &#039;Table&#039;[Product] ),
    RANKX (
        ALL ( &#039;Table&#039; ),
        [SumQty],
        , ASC
        , Skip
    )
)و البته order نزولی هم باشه به همین صورت خواهد بود.Dense: در این حالت مقادیر تکراری رتبه یکسانی خواهند داشت و رتبه رقم بعدی بعد از این ارقام تکراری برابر رتبه قبلی به علاوه 1 هستش یعنی دیگه پرشی اتفاق نمیفته و ادامه رتبه های قبلی ادامه پیدا میکنه.rank_11 = 
IF (
    ISINSCOPE ( &#039;Table&#039;[Product] ),
    RANKX (
        ALL ( &#039;Table&#039; ),
        [SumQty],
        , ASC
        , Dense
    )
)</description>
                <category>رحمان</category>
                <author>رحمان</author>
                <pubDate>Mon, 03 Nov 2025 17:30:45 +0330</pubDate>
            </item>
            </channel>
</rss>