مقادیر بدون مقدار(Blank) در ستونهای تاریخ و توابع هوش زمانی (Time Intelligence)در DAX
مدیریت تاریخهای ناقص (missing dates) در یک مدل معنایی میتواند چالشبرانگیز باشد، بهویژه زمانی که با توابع هوش زمانی (Time Intelligence) در DAX کار میکنیم. ممکن است تاریخها به دلایل مختلفی ثبت نشده باشند: ورود ناقص اطلاعات (missing data)، خطاهای سیستمی، استفاده از مقادیر خاص مانند 0000 به عنوان جایگزین، یا تاریخهایی که در آیندهای دور تنظیم شدهاند. خواهیم دید که استفاده از مقدار بدون مقدار (blank) بهترین روش برای مدیریت تاریخهای ناقص است، هرچند باید هنگام استفاده از عبارات شرطی DAX که روی این تاریخها اجرا میشوند، دقت لازم را داشته باشید. همچنین بررسی خواهیم کرد که چگونه میتوان این مقادیر بدون مقدار (blank) را در گزارشهای Power BI پنهان کرد، بهخصوص اگر نمایش آنها در نمودارها و فیلترها (slicerها) مطلوب نباشد.
درک تاریخهای ناقص (missing dates)
وجود تاریخهای ناقص (missing dates) لزوماً خطا محسوب نمیشود و میتواند شرایطی طبیعی در دادهها باشد. برای مثال، در جدول فروش (Sales) که اغلب در مثالهای Contoso استفاده میشود، دو ستون تاریخ داریم: تاریخ سفارش (Order Date) و تاریخ تحویل (Delivery Date). در حالی که تاریخ سفارش همیشه موجود است، تاریخ تحویل تا زمانی که سفارش ارسال نشود، ثبت نمیشود. در بیشتر مثالهایی که در مقالاتمان استفاده میکنیم، فقط ردیفهایی از جدول فروش داریم که مربوط به سفارشهای ارسالشده هستند – بنابراین هر دو تاریخ در آنها وجود دارد.
اما در فایل نمایشی (demo)، تمامی سفارشهایی که از آوریل ۲۰۲۴ به بعد دریافت شدهاند، هنوز ارسال نشدهاند، در نتیجه مقدار ستون تاریخ تحویل برای آنها بدون مقدار (blank) است.

در بهروزرسانیهای بعدی همین مدل، ممکن است برخی از آن تراکنشها دارای تاریخ تحویل معتبر شوند؛ اما وجود ردیفهایی که برای یک یا چند ستون تاریخ، بدون مقدار (blank) هستند، شرایطی است که باید آن را بهعنوان وضعیتی عادی در این مدل در نظر بگیریم.
میتوان موقعیتی مشابه را در مورد اشتراکها، قراردادها یا هر موجودیت تجاری دیگری مشاهده کرد؛ جایی که وجود مقدار برای همهی ردیفهای یک ستون تاریخ، الزامآور نیست. با این حال، وجود مقدار بدون مقدار (blank) در مدل معنایی، اثرات جانبی مختلفی دارد که در ادامه به آنها خواهیم پرداخت. پیش از آن، میخواهیم بررسی کنیم که چرا نگهداشتن مقدار بدون مقدار (blank) نسبت به سایر گزینهها انتخاب بهتری است.
جایگزینهایی برای تاریخهای بدون مقدار (blank)
اگر یک مقدار بدون مقدار (blank) در سمت “چند” از یک رابطهی یکبهچند وجود داشته باشد، در سمت “یک” این رابطه، یک ردیف اضافی بدون مقدار (blank) ایجاد خواهد شد. برای جدول Date، این به این معناست که یک تاریخ بدون مقدار (blank) خواهیم داشت – که از نظر تجربه کاربری ممکن است ایدهآل نباشد. البته این مشکل را در ادامه بررسی خواهیم کرد. اما پیش از آن، بیایید گزینههای جایگزین ممکن را ارزیابی کنیم.
اولین گزینه میتواند استفاده از یک مقدار جایگزین خاص باشد. اگر تاریخ بهصورت عدد صحیح یا رشتهای در قالب YYYYMMDD ذخیره شده باشد، مثلاً تاریخ ۲۳ مارس ۲۰۲۴ بهشکل 20240323 نمایش داده میشود. در این حالت، یک تاریخ ناقص (missing date) میتواند با مقدار 00000000 نمایش داده شود که خود عددی نامعتبر برای روز و ماه است. اگر چنین مقداری در جدول تاریخ (Date) وجود داشته باشد، باید برای ستونهایی مثل سال، ماه و روز مقادیر خاصی داشته باشد (مثلاً بدون مقدار (blank)، صفر یا هر مقدار دیگری).
با این حال، اگر جدول Date ستونی به نام Date داشته باشد – که در توابع هوش زمانی DAX هم مورد استفاده قرار میگیرد – این ستون باید یا یک تاریخ واقعی باشد، یا مقدار بدون مقدار (blank) که توسط سطر خالی حاصل از رابطهی نامعتبر ایجاد شده است. در واقع، در این حالت مشکل را فقط به ستونی دیگر منتقل میکنیم، بدون اینکه تمام مسائل را حل کرده باشیم.
اگر قصد استفاده از توابع هوش زمانی DAX را دارید، باید یک ستون تاریخ واقعی در اختیار داشته باشید، و نمیتوانید مقدار جایگزین خاص را بدون داشتن یک تاریخ واقعی، در آن ستون استفاده کنید.
اگر از مقدار بدون مقدار (blank) استفاده میکنید، در واقع از هیچ جایگزینی استفاده نکردهاید – و این دقیقاً همان چیزی است که باید انجام دهید!
گزینهی دیگر استفاده از یک تاریخ خیلی دور در آینده است؛ مثلاً آخرین روز سال ۲۱۹۹ در مثال زیر.

در این حالت، جدول Date میتواند یک ردیف مربوط به آن تاریخ خاص داشته باشد. در غیر اینصورت، دوباره یک ردیف بدون مقدار (blank) در جدول Date خواهید داشت. بنابراین اگر فرض کنیم که نمیخواهید ردیف بدون مقدار (blank) در جدول Date وجود داشته باشد، باید با برخی از اثرات جانبی دیگر روبهرو شوید:
اگر آن تاریخ خاص، تنها تاریخ موجود در همان سال باشد، فرضیات موردنیاز برای توابع هوش زمانی DAX را نقض میکند (در این توابع فرض میشود که تمام سالها باید بهطور کامل شامل تمام تاریخها باشند، در غیر اینصورت نتایج ممکن است نادرست باشند).
اگر جدول Date از طریق یک جدول محاسباتی ایجاد شده باشد که بهصورت خودکار بازهی تاریخها را تشخیص میدهد (مانند تابع
CALENDARAUTO
)، در آن صورت جدول Date شامل تمام سالها و تمام روزها تا آن تاریخ دور در آینده خواهد بود.
برای مثال، اگر تاریخی از سال ۲۱۹۹ تعیین شود، جدول Date بیش از ۶۵۰۰۰ ردیف خواهد داشت. حالا تصور کنید اگر از تاریخ 9999 استفاده شود، جدول Date بیش از ۶۴ میلیون ردیف خواهد داشت – که حداقل میتوان گفت شرایطی نامناسب است!
بنابراین، استفاده از مقدار بدون مقدار (blank) انتخاب بهتری است: این مقدار بهدرستی وضعیت واقعی را نمایش میدهد (یعنی نبود اطلاعات، چون آن تاریخ هنوز تعریف نشده) و با رفتار جدول Date هماهنگ است – چراکه یک ردیف بدون مقدار (blank) اضافه خواهد شد، بدون آنکه تقویم را با تاریخهای استفادهنشده آلوده کند.
تاریخهای بدون مقدار (blank dates) و جدول Date
بهترین روش در طراحی یک مدل معنایی، استفاده از تنها یک جدول تاریخ (Date) است که با تمامی ستونهای تاریخ که ممکن است کاربران بخواهند آنها را تجمیع کنند، رابطهی یکبهچند داشته باشد. در مثال ما، جدول Date با ستون تاریخ سفارش (Order Date) (با رابطه فعال) و تاریخ تحویل (Delivery Date) (با رابطه غیرفعال) در جدول Sales مرتبط است.

در این مدل، دو معیار (measure) به نامهای Sales Amount و Delivered Sales وجود دارد که میتوان آنها را در یک تصویرسازی (visual) بهصورت ترکیبی نمایش داد:


مقدار فروش تحویلنشده (Delivered Sales) برابر با 14,632.93 است که به یک سال بدون مقدار (blank year) نسبت داده شده است. جدول Date هیچ ردیفی با سال بدون مقدار (blank) ندارد؛ این ردیف، همان ردیف بدون مقدار (blank row) است که در اثر وجود یک رابطه نامعتبر (invalid relationship) اضافه شده است (رجوع شود به: Blank row in DAX).
وجود یک تاریخ بدون مقدار (blank) در ستون Sales[Delivery Date]
باعث ایجاد وضعیت “نامعتبر” در رابطه شده است. این وضعیت لزوماً برای مدل بد نیست، زیرا واقعاً جایگزین مناسبی برای نمایش یک تراکنش که هنوز تحویل داده نشده، وجود ندارد.
با این حال، باید آمادهی مدیریت حضور این مقدار بدون مقدار (blank) باشیم، چراکه ممکن است باعث سردرگمی کاربر شود و در صورت نادیده گرفتن آن در محاسبات DAX، نتایجی نادقیق تولید کند.
برای مثال، در رابط کاربری، هر Slicer از جدول Date (مانند Year یا Month)، یک ردیف بدون مقدار (blank row) را نیز شامل خواهد شد. در گزارش زیر، انتخاب گزینهی بدون مقدار (blank) از Year یا Month باعث میشود گزارشی خالی نمایش داده شود، زیرا هیچ فروشی بدون تاریخ سفارش (Order Date) وجود ندارد.
با اینکه در این گزارش، هیچکدام از معیارها (measures) از ستون Delivery Date استفاده نمیکنند و رابطه بین Delivery Date و جدول Date نیز غیرفعال است، ولی وجود مقدار بدون مقدار (blank) در ستون Sales[Delivery Date]
باعث تولید ردیف بدون مقدار در جدول Date میشود – به دلیل وجود آن رابطهی غیرفعال.

اگر بخواهید ردیف بدون مقدار (blank row) را از دید کاربران گزارش پنهان کنید، دو راهحل پیش رو دارید: یا با افزودن فیلتر در گزارش (report filters) این کار را انجام دهید، یا با تغییر در مدل داده (modifying the model).
پنهان کردن مقادیر بدون مقدار (blanks) در جدول Date در گزارشها
شما میتوانید ردیفهای بدون مقدار (blank rows) را از یک slicer حذف کنید، فقط کافیست یک شرط فیلتر به خود slicer یا به کل صفحه (page filter) اضافه کنید.
اگر فیلتر را فقط روی ویژوال slicer اعمال کنید، این تغییر هیچ اثر جانبیای روی سایر ویژوالهای گزارش نخواهد داشت.

رویکرد دیگری که میتوان در پیش گرفت این است که تاریخ بدون مقدار (blank) را از صفحه گزارش پنهان کنید، بدون آنکه نیازی به ویرایش هر ویژوال (visual) داشته باشید. در این حالت، ایدهی خوبی است که مقدار بدون مقدار (blank) را از ستون Date[Date]
فیلتر کنید تا این تغییر به تمام سایر ستونهای جدول Date سرایت کند، بدون آنکه مقادیر بدون مقدار (blank) مشروع در سایر ستونها (مثل نام تعطیلات) پنهان شوند.

با این حال، با فیلتر کردن صفحه، تمامی ویژوالها فیلتر میشوند، حتی زمانی که هیچ انتخابی در هیچ slicer انجام نشده باشد. بنابراین، این تغییر بر روی معیارهایی که از رابطهی تاریخ تحویل (Delivery Date) استفاده میکنند، تأثیر میگذارد؛ مثل معیار Delivered Sales: ما تمام فروشهایی که تاریخ تحویل ندارند را در معیار Delivered Sales پنهان میکنیم.
برای مثال، در گزارش زیر، تفاوت بین Sales Amount و Delivered Sales برابر با مقدار سفارشات تحویلنشده است که در معیار Undelivered Sales نمایش داده میشود.

مژر Undelivered Sales بهسادگی تراکنشهایی که تاریخ تحویل بدون مقدار (empty Delivery Date) دارند را فیلتر میکند. در این حالت، از تابع USERELATIONSHIP
استفاده نمیشود و همیشه رابطهی فعال با Sales[Order Date] برقرار است.

ایجاد یک مژر (measure) واقعاً ضروری نیست مگر اینکه بخواهید آن مقدار را در یک ویژوال همراه با سایر معیارهایی که از روابط موجود استفاده میکنند، نمایش دهید.
پنهان کردن مقادیر بدون مقدار (blanks) از جدول Date در مدل
برای تکمیل، گزینهی دیگری را هم توضیح میدهیم که اثرات منفی زیادی دارد. در حالی که ما این تکنیک را توصیه نمیکنیم، میخواهیم توضیح دهیم که چرا ممکن است در چندین روش به ضرر شما تمام شود.
چون ردیف بدون مقدار (blank row) به جدول Date اضافه شده بهدلیل یک رابطه نامعتبر عادی، میتوانیم نوع رابطه را به یک رابطه محدود (limited relationship) تغییر دهیم که نمیتواند وضعیت نامعتبر داشته باشد. به عنوان مثال، بهجای استفاده از روابط یکبهچند (one-to-many) بین جدول Date و جدول Sales، میتوانیم از روابط با کاردینالیتی چندبهچند (many-to-many) با فیلتر تکجهته از جدول Date به جدول Sales استفاده کنیم.

با اعمال این ویژگیها به هر دو رابطه بین جدول Date و جدول Sales، مدل زیر بهدست میآید.

با استفاده از این روابط محدود (limited relationships)، هیچکدام از slicerها ردیف بدون مقدار (blank) را بهدلیل ردیف بدون مقدار (blank row) نمایش نمیدهند، صرفنظر از انتخاب هر فیلتر در جدول Date در هر قسمتی از گزارش.
با این حال، مقدار کل Delivered Sales دقیقاً برابر با Sales Amount (7,305,938.94) است و با مجموع Delivered Sales به تفکیک سال (7,291,306.01 در گزارشی که قبلاً نشان دادیم) مطابقت ندارد.

این رفتار همان چیزی است که از یک رابطه محدود انتظار داریم: زمانی که فیلتر روی جدول Date اعمال نمیشود (همانطور که برای ردیف کل است)، تمام مقادیر ستون Sales[Delivery Date]
شامل میشود. این اتفاق علیرغم استفاده از تابع USERELATIONSHIP
در معیار Delivered Sales است، زیرا هیچ فیلترهایی به جدول Sales منتقل نمیشود. پیشتر، یک فیلتر “is not blank” در گزارش از جدول Date به جدول Sales منتقل میشد و فروشهای تحویلنشده را از مجموع حذف میکرد. اگر بخواهیم رفتار قابل پیشبینی (predictable) را برای Delivered Sales بازگردانیم، میتوانیم تعریف آن را به Delivered Sales (fix) تغییر دهیم و یک فیلتر به ستون Sales[Delivery Date]
اضافه کنیم:

با این مژر، میتوانیم نتیجهی مورد انتظار را با استفاده از رابطهی کاردینالیتی چندبهچند (many-to-many cardinality) بهدست آوریم.

با این حال، استفاده از روابط کاردینالیتی چندبهچند (many-to-many cardinality) برای بهدست آوردن این نتیجه بهشدت توصیه نمیشود به دلایل زیر:
نیاز به یک معیار برای فیلتر کردن مقادیر بدون مقدار (blank) از مجموع، همانطور که در Delivered Sales (fix) انجام دادیم.
کاهش عملکرد به دلیل رفتار غیرجمعپذیر روابط محدود (limited relationships) – این مسئله ممکن است بهویژه زمانی که با محاسبات هوش زمانی (time intelligence) ترکیب شود، مشکلساز باشد.
عدم اعتبارسنجی دادهها در جدول Sales: هر تاریخ ناقص در جدول Date پنهان میشود و فقط در ردیف Total نمایش داده میشود. به عبارت دیگر، شما ممکن است مشکلات بالقوهای در کیفیت دادهها را پنهان کنید.
تاریخهای بدون مقدار (Blank dates) در عبارات DAX
زمانی که یک تاریخ بدون مقدار (blank) در جدول Date وجود داشته باشد، آن تاریخ معادل 0 برای ستونهای عددی مانند Year و Month Number است و معادل تاریخی قبل از سال 1900 برای ستونهای تاریخ خواهد بود. بنابراین، باید هنگام مقایسه هر ستونی از جدول Date با استفاده از عملگرهایی مانند <
، <=
، >=
یا >
دقت کنید. شما باید سعی کنید هر مقدار بدون مقدار (blank) را از خود ستون نادیده بگیرید.
برای مثال، معیار Cumulated Delivery را در نظر بگیرید:

این مژر باید مجموع کل فروشهای تحویلشده را از اولین تراکنش تحویلشده موجود نمایش دهد. بنابراین، مقدار برای سال 2021 باید مشابه Delivered Sales باشد، در حالی که مقدار برای سال 2022 باید مجموع هر دو سال 2021 و 2022 را نمایش دهد. با این حال، مقدار از همان ابتدا اشتباه است، زیرا در سال 2021 عددی بزرگتر از حد انتظار را نمایش میدهد.

دلیل این امر این است که مقدار تحویلنشده (not delivered) دارای تاریخ بدون مقدار (blank) است: نتیجه پیشفرض شرط ‘Date'[Date] <= LastDateVisible
همیشه شامل مقدار بدون مقدار میشود، زیرا این تاریخ بهعنوان “کمتر از” هر تاریخ دیگری در نظر گرفته میشود. راهحل نیاز به یک فیلتر اضافی دارد.
برای مثال، استفاده از معیار Delivered Sales (fix) که برای روابط چندبهچند توضیح دادیم بهجای Delivered Sales، ردیف بدون مقدار (blank row) را برای این محاسبه نادیده خواهد گرفت. با این حال، هدف این مقاله این است که شما را آگاه کند که زمانی که جدول Date فیلتر میشود، باید از عواقب احتمالی ردیف بدون مقدار (blank row) آگاه باشید. بنابراین، یک شرط صریح در شرطگذاری (predicate) یک روش امنتر برای بهدست آوردن مقدار تجمعی است، همانطور که در معیار Cumulated Delivery (fix) نشان داده شده است:

گزارش نشان میدهد که مقدار Cumulated Delivery (fix) اکنون با Delivered Sales برای سال 2021 و همچنین ردیف Total همخوانی دارد.

قاعده کلی این است که همیشه باید یک شرط NOT ISBLANK ( <date> ) اضافه کنید زمانی که از یک عملگر مقایسه با تاریخ استفاده میکنید. حتی اگر این برای عملگرهای مقایسه < و <= ضروری باشد، ممکن است ترجیح دهید رویکرد “احتیاط بهتر از پشیمانی است” را اتخاذ کنید و این شرط NOT ISBLANK ( <date> ) را همچنین برای عملگرهای مقایسه > و >= نیز اعمال کنید. بدین ترتیب، میتوانید کمتر نگران تغییرات آینده در این شرایط باشید.
تاریخهای بدون مقدار (Blank dates) در تکرارگرها (Iterators)
زمانی که از یک تکرارگر روی جدول Date استفاده میکنید، باید در نظر داشته باشید که ردیف بدون مقدار (blank row) زمانی که یک ارجاع به جدول مینویسید، مانند معیار Delivery Commissions، گنجانده نمیشود (این معیار بهینه نشده است، اما بهمنظور نشان دادن نحوه عملکرد تکرارگرها با ردیفهای بدون مقدار نوشته شده است):

همانطور که در هر جدول با ردیف بدون مقدار (blank row) ایجاد شده توسط یک رابطه نامعتبر، یک ارجاع ساده به جدول ردیف اضافی بدون مقدار را شامل نمیشود. بنابراین، مقدار Delivered Sales مربوط به سفارشات تحویلنشده در نسخه قبلی معیار گنجانده نمیشود. معیار Delivery Commissions (fix) زیر با استفاده از VALUES برای تکرار روی جدول Date ردیف بدون مقدار را شامل میشود:

گزارش نهایی مقدار بزرگتری برای Delivery Commissions (fix) نسبت به Delivery Commissions نشان میدهد، زیرا این معیار شامل فروشهای تحویلنشده میشود بدون نیاز به محاسبه جداگانه.

به خاطر داشته باشید که این نسخه کار میکند زیرا Delivered Sales شامل یک استثنای صریح برای فروشهای تحویلنشده نیست – همان شرط NOT ISBLANK ( Sales[Delivery Date] ) که برای دیگر سناریوها بحث کردیم. با این حال، هدف از این مثال جلب توجه شما به گزینههای مختلفی بود که هنگام تکرار یک جدول که ممکن است ردیف اضافی بدون مقدار (blank row) داشته باشد، در اختیار دارید – ما از یک مثال غیرواقعی از یک معیار استفاده کردیم تا از معرفی یک مورد استفاده متفاوت جلوگیری کنیم. در حالی که مشکل ساده است و وقتی ردیف بدون مقدار قابل مشاهده است – همانطور که در گزارش قبلی نشان داده شد، مسائل پیچیدهتر میشود زمانی که تاریخ در گزارش درگیر نباشد، اما تکرارگر هنوز از آن استفاده میکند. برای مثال، در گزارش زیر، سخت است که بگوییم Delivery Commissions درست نیست و Delivery Commissions (fix) عدد صحیح را نشان میدهد، زیرا شواهد واضحی از اینکه چه چیزی باعث تفاوت بین دو معیار میشود وجود ندارد – شواهدی که زمانی که ردیف بدون مقدار در گزارش حضور دارد، واضحتر است.

مفهوم BLANK در توابع هوش زمانی DAX
میخواهیم اشاره کنیم که BLANK معنی خاصی در برخی از توابع هوش زمانی DAX دارد که نباید با بدون مقدار (blank) که میتوانیم در داخل یک ستون تاریخ مشاهده کنیم، اشتباه گرفته شود.
به طور کلی، ردیف بدون مقدار (blank row) در یک جدول تاریخ هیچگاه توسط یک تابع هوش زمانی DAX بازگشت نمیشود. با این حال، تابع BLANK میتواند به عنوان یک آرگومان برای توابع DATESBETWEEN و DATESINPERIOD استفاده شود.
وقتی که تابع BLANK با DATESBETWEEN استفاده میشود، معنی خاص آن “بدون مرز” است. زمانی که برای آرگومان StartDate استفاده شود، شامل تمام تاریخها از اولین تاریخ موجود در جدول تاریخ میشود و ردیف بدون مقدار نادیده گرفته میشود. زمانی که برای آرگومان EndDate استفاده شود، شامل تمام تاریخها تا آخرین تاریخ موجود در جدول تاریخ میشود. مثال زیر تمام تاریخها را در جدول تاریخ تا 3 ژانویه 2017 نشان میدهد:

این مثال دیگر تمام تاریخها را در جدول تاریخ از 12 دسامبر 2020 نشان میدهد:

برای DATESINPERIOD، مقدار بدون مقدار معنی خاصی برای StartDate ندارد: فقط معادل یک تاریخی است که قبل از سال 1900 است، بنابراین میتوان از آن برای دریافت اولین تاریخها از ابتدای جدول تاریخ استفاده کرد، بدون اینکه نیازی به دانستن زمان شروع جدول تاریخ باشد. به عنوان مثال، پرسوجوی زیر اولین سه روز جدول تاریخ را باز میگرداند:

مدیریت تاریخهای بدون مقدار (blank dates) در DAX نیاز به دقت دارد تا نتایج صحیح و معنیدار حاصل شود. در عبارات DAX، به یاد داشته باشید که تاریخهای بدون مقدار را در مقایسهها نادیده بگیرید تا از مشکلات مرتبط با تاریخهای گمشده جلوگیری کنید. همچنین به یاد داشته باشید که تاریخهای بدون مقدار میتوانند معنی کسبوکاری مهمی داشته باشند: روشهای جایگزین مانند تاریخهای ویژه یا سایر جایگذاریها میتوانند عوارض منفی مشابهی داشته باشند، بنابراین بهتر است نحوه مدیریت تاریخهای بدون مقدار را درک کنید تا اینکه بخواهید به هر قیمتی از آنها اجتناب کنید.
دیدگاهتان را بنویسید