مطالب
تهیه گزارشات Crosstab به کمک LINQ

در گزارشات Crosstab، ردیف‌های یک گزارش، تبدیل به ستون‌های آن می‌شوند؛ به همین جهت به آن‌ها Pivot tables هم می‌گویند.
برای مثال فرض کنید که قصد دارید گزارش تعداد ساعت کارکرد را به ازای هر پروژه در طول چند ماه تعیین کنید. گزارش متداول از این نوع اطلاعات، یک لیست بلند بالای بی‌مفهوم است. این گزارش تشکیل شده از صدها رکورد به ازای کارکنان مختلف در پروژه‌های مختلف و ... هیچ ارزش آماری خاصی ندارد. یک گزارش بدوی است. زمانیکه این گزارش را تبدیل به حالت crosstab می‌کنیم، اولین ستون فقط یک شماره پروژه خواهد بود و ستون‌های بعدی، مثلا نام ماه‌ها و مقادیر آن‌ها هم جمع کارکرد افراد بر روی یک پروژه مشخص.

مثال اول) تهیه گزارش Crosstab جمع هزینه‌های واحدهای مختلف به تفکیک ماه

کلاس هزینه‌های زیر را در نظر بگیرید که به کمک آن می‌توان به ازای هر واحد یا دپارتمان در تاریخ‌های متفاوت، هزینه‌ای را مشخص ساخت:

using System;

namespace Pivot.Sample1
{
public class Expense
{
public DateTime Date { set; get; }
public string Department { set; get; }
public decimal Expenses { set; get; }
}
}

با توجه به این کلاس، یک منبع داده آزمایشی جهت تهیه گزارشات، می‌تواند به صورت زیر باشد:

using System;
using System.Collections.Generic;

namespace Pivot.Sample1
{
public class ExpenseDataSource
{
public static IList<Expense> ExpensesDataSource()
{
return new List<Expense>
{
new Expense { Date = new DateTime(2011,11,1), Department = "Computer", Expenses = 100 },
new Expense { Date = new DateTime(2011,11,1), Department = "Math", Expenses = 200 },
new Expense { Date = new DateTime(2011,11,1), Department = "Physics", Expenses = 150 },

new Expense { Date = new DateTime(2011,10,1), Department = "Computer", Expenses = 75 },
new Expense { Date = new DateTime(2011,10,1), Department = "Math", Expenses = 150 },
new Expense { Date = new DateTime(2011,10,1), Department = "Physics", Expenses = 130 },

new Expense { Date = new DateTime(2011,9,1), Department = "Computer", Expenses = 90 },
new Expense { Date = new DateTime(2011,9,1), Department = "Math", Expenses = 95 },
new Expense { Date = new DateTime(2011,9,1), Department = "Physics", Expenses = 100 }
};
}
}
}

و اگر این لیست را به همین شکلی که هست نمایش دهیم، خروجی زیر را خواهیم داشت:


که ... خروجی مطلوبی نیست. در اینجا ما فقط 9 رکورد داریم؛ اما در عمل به ازای هر روز، یک رکورد می‌تواند وجود داشته باشد و این لیست طولانی، هیچ ارزش آماری خاصی ندارد. می‌خواهیم سرستون‌های گزارش ما مطابق جدول زیر باشند:


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

using System.Collections;
using System.Linq;

namespace Pivot.Sample1
{
public class PivotTable
{
public static IList ExpensesCrossTab()
{
return ExpenseDataSource
.ExpensesDataSource()
.GroupBy(t =>
new
{
Year = t.Date.Year,
Month = t.Date.Month
})
.Select(myGroup =>
new
{
//Year = myGroup.Key.Year,
Month = myGroup.Key.Month,
ComputerDepartment = myGroup.Where(x => x.Department == "Computer").Sum(x => x.Expenses),
MathDepartment = myGroup.Where(x => x.Department == "Math").Sum(x => x.Expenses),
PhysicsDepartment = myGroup.Where(x => x.Department == "Physics").Sum(x => x.Expenses)
})
.ToList();
}
}
}

که اینبار خروجی زیر را تولید می‌کند.


اگر علاقمند باشید که مثال فوق را در برنامه‌ی LINQPad آزمایش کنید، این فایل را دریافت نموده و در آن برنامه باز نمائید.


مثال دوم) تهیه لیست Crosstab حضور و غیاب افراد در طول یک هفته

کلاس StudentStat را جهت ثبت اطلاعات حضور یک دانشجو، می‌توان به شکل زیر تعریف کرد:

using System;

namespace Pivot.Sample2
{
public class StudentStat
{
public int Id { set; get; }
public string Name { set; get; }
public DateTime Date { set; get; }
public bool IsPresent { set; get; }
}
}

و بر همین اساس یک منبع داده فرضی جهت انجام گزارشات می‌تواند به نحو زیر تهیه شود:

using System;
using System.Collections.Generic;

namespace Pivot.Sample2
{
public class StudentsStatDataSource
{
public static IList<StudentStat> CreateMonthlyReportDataSource()
{
var result = new List<StudentStat>();
var rnd = new Random();

for (int day = 1; day < 6; day++)
{
for (int student = 1; student < 6; student++)
{
result.Add(new StudentStat
{
Id = student,
Date = new DateTime(2011, 11, day),
IsPresent = rnd.Next(-1, 1) == 0 ? true : false,
Name = "student " + student
});
}
}

return result;
}
}
}

خروجی این گزارش هم در این حالت ساده با 5 دانشجو و فقط 5 روز، 25 رکورد خواهد بود:


که ... این هم آنچنان از لحاظ آماری مطلوب و مفهوم نیست. می‌خواهیم سطرهای این گزارش همانند لیست واقعی حضورغیاب، فقط از نام افراد تشکیل شود و همچنین ستون‌ها مثلا شماره یا نام روزهای یک هفته یا ماه باشند. مثلا به شکل زیر:


برای رسیدن به این خروجی Crosstab، مثلا می‌توان از کوئری LINQ زیر کمک گرفت که بر اساس شماره دانشجویی اطلاعات را گروه بندی کرده است:

using System.Collections;
using System.Linq;

namespace Pivot.Sample2
{
public class PivotTable
{
public static IList StudentsStatCrossTab()
{
return StudentsStatDataSource
.CreateWeeklyReportDataSource()
.GroupBy(x =>
new
{
x.Id
})
.Select(myGroup =>
new
{
myGroup.Key.Id,
Name = myGroup.First().Name,
Day1IsPresent = myGroup.Where(x => x.Date.Day == 1).First().IsPresent,
Day2IsPresent = myGroup.Where(x => x.Date.Day == 2).First().IsPresent,
Day3IsPresent = myGroup.Where(x => x.Date.Day == 3).First().IsPresent,
Day4IsPresent = myGroup.Where(x => x.Date.Day == 4).First().IsPresent,
Day5IsPresent = myGroup.Where(x => x.Date.Day == 5).First().IsPresent,
PresentsCount = myGroup.Where(x => x.IsPresent).Count(),
AbsentsCount = myGroup.Where(x => !x.IsPresent).Count()
})
.ToList();
}
}
}

و این کوئری خروجی زیر را تولید می‌کند که از هر لحاظ نسبت به لیست قبلی مفهوم‌تر است:


فایل LINQPad این مثال را می‌توانید از اینجا دریافت کنید.

مطالب
نمایش Subgrid در jqGrid
اگر به مثال «فرمت کردن اطلاعات نمایش داده شده در jqGrid» دقت کنید، لینکی را جهت نمایش یک popup جزئیات رکورد انتخاب شده قرار دادیم. شاید طراحی بهتر به این صورت باشد که یک دکمه‌ی + در کنار ردیف قرار دهیم. با کلیک کاربر بر روی این دکمه، جزئیات این ردیف، از سرور دریافت شده و به صورت یک زیر گرید نمایش داده شود. در ادامه همان مثال را با همان ساختار داده‌ای و کدهای سمت سرور، جهت کار با subgrids بازنویسی خواهیم کرد.



فعال سازی Subgrid

در اینجا مواردی را که باید جهت فعال سازی subgrid به تعاریف اولیه‌ی jqGrid اضافه کرد، مشاهده می‌کنید:
            $('#list').jqGrid({
                caption: "آزمایش یازدهم",
                // ... 
                jsonReader: {
                // ... 
                    subgrid: { root: "Rows", repeatitems: true, cell: "RowCells" }
                },
                // ... 
                subGrid: true,
                subGridModel: [{
                    name: ['شرکت', 'آدرس', 'کد پستی', 'شهر', 'کشور', 'تلفن', 'وب سایت'],
                    width: [100, 100, 100, 100, 100, 100, 100],
                    align: ['center', 'center', 'center', 'center', 'center', 'center', 'center'],
                    params: ['@(StronglyTyped.PropertyName<Product>(x=>x.Name))']
                }],
                subGridOptions:{ 
                    reloadOnExpand : false //load only once
                },
                subGridUrl: '@Url.Action("GetGetSupplierData", "Home")'
            });
چون قصد داریم در سمت سرور، از همان ساختار JqGridData خودمان برای بازگشت اطلاعات subgrid استفاده کنیم، نیاز است خاصیت subgrid مربوط به jsonReader را اندکی ویرایش کنیم تا jqGrid بداند که بجای cell قرار است RowCells دریافت کند.
با تنظیم subGrid: true نمایش ستون + داری که در تصویر فوق مشخص است، انجام می‌شود.
subGridModel بیانگر ساختار اطلاعاتی است که قرار است نمایش داده شوند.
آرایه name، نام سر ستون‌ها را مشخص می‌کند.
 آرایه width، عرض ستون‌های زیرگرید را مقدار دهی خواهد کرد.
آرایه align محل و سمت قرارگیری هر یک از مقادیر سلول‌ها را تعیین می‌کند.
 آرایه params اختیاری است. زمانیکه کاربر بر روی یک + ستون subgrid، برای باز شدن این زیرگرید کلیک می‌کند، صرفا Id ردیف به سرور ارسال می‌شود. اگر در این بین می‌خواهید، خاصیت خاصی از گرید اصلی نیز به سرور ارسال شود، آرایه params را مقدار دهی کنید. برای نمونه در اینجا Name ردیف انتخاب شده نیز به ارسال ارسال خواهد شد (برگه شبکه شکل فوق).
subGridOptions یک سری تنظیمات اضافه را به همراه دارد. اگر می‌خواهید اطلاعات زیرگرید فقط یکبار بارگذاری شود و با هربار کلیک کاربر از سرور دریافت نگردد، خاصیت reloadOnExpand آن‌را false کنید.
 subGridUrlآدرسی که تامین کننده اطلاعات JSON زیرگرید می‌باشد.

در این حالت، کدهای سمت سرور بازگشت اطلاعات زیر گرید به شکل زیر می‌باشد:
        public ActionResult GetGetSupplierData(int id, string name)
        {
            var list = ProductDataSource.LatestProducts;
            var products = list.Where(x => x.Id == id).ToList();
            if (!products.Any())
                return Json(null, JsonRequestBehavior.AllowGet);

            var productsData = new JqGridData
            {
                Rows = (products.Select(product => new JqGridRowData
                {
                    Id = product.Id,
                    RowCells = new List<string> 
                               {
                                    product.Supplier.CompanyName,
                                    product.Supplier.Address,
                                    product.Supplier.PostalCode,
                                    product.Supplier.City,
                                    product.Supplier.Country,
                                    product.Supplier.Phone,
                                    product.Supplier.HomePage
                                }
                })).ToList()
            };
            return Json(productsData, JsonRequestBehavior.AllowGet);
        }
همانطور که ملاحظه می‌کنید، حالت ساده شده‌ی JqGridData بازگشت داده می‌شود. زیرا در حالت نمایش زیرگرید، مباحث مرتب سازی اطلاعات و همچنین paging فعال نیستند و نیازی به اطلاعات آن‌ها نیست.


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید
jqGrid11.zip
نظرات مطالب
امکان انجام محاسبات سمت کلاینت در EF Core
یک نکته‌ی تکمیلی: کدامیک از متدهای کار با رشته‌ها در سمت کلاینت پردازش می‌شوند و کدام‌ها در سمت سرور؟
در مثال‌های زیر، هر جائیکه قسمت where عبارت SQL حذف شده‌، یعنی Client-Side Evaluation رخ داده‌است:
- با اضافه شدن نوع مقایسه، محاسبه‌ی سمت کلاینت رخ می‌دهد:
var test1 = context.Blogs
.Where(blog => String.Compare(blog.Url, "A", StringComparison.Ordinal) > 0)
.ToList();
// SELECT [blog].[BlogId], [blog].[Url]
// FROM [Blogs] AS [blog]
- بدون مشخص سازی نوع مقایسه، محاسبه‌ی سمت سرور را خواهیم داشت:
var test2 = context.Blogs
.Where(blog => String.Compare(blog.Url, "B") > 0)
.ToList();
// SELECT [blog].[BlogId], [blog].[Url]
// FROM [Blogs] AS [blog]
// WHERE [blog].[Url] > N'B'
- در مورد متد Equals هم دقیقا به همین صورت است. با اضافه شدن نوع مقایسه، محاسبه‌ی سمت کلاینت رخ می‌دهد: 
var test3 = context.Blogs
.Where(blog => blog.Url.Equals("C", StringComparison.OrdinalIgnoreCase))
.ToList();
// SELECT [blog].[BlogId], [blog].[Url]
// FROM [Blogs] AS [blog]
- بدون مشخص سازی نوع مقایسه، محاسبه‌ی سمت سرور را خواهیم داشت: 
var test3_1 = context.Blogs
.Where(blog => blog.Url.Equals("C_1"))
.ToList();
// SELECT [blog].[BlogId], [blog].[Url]
// FROM [Blogs] AS [blog]
// WHERE [blog].[Url] = N'C_1'
- StartsWith و EndsWith در سمت سرور محاسبه می‌شوند:
var test4 = context.Blogs
.Where(blog => blog.Url.StartsWith("D"))
.ToList();
// SELECT [blog].[BlogId], [blog].[Url]
// FROM [Blogs] AS [blog]
// WHERE [blog].[Url] LIKE N'D' + N'%' AND (LEFT([blog].[Url], LEN(N'D')) = N'D')
- اما اگر از عبارات SQL آن‌ها راضی نیستید، از متد EF.Functions.Like استفاده کنید:
var test5 = context.Blogs
.Where(blog => EF.Functions.Like(blog.Url, "S_i%"))
.ToList();
// SELECT [blog].[BlogId], [blog].[Url]
// FROM [Blogs] AS [blog]
// WHERE [blog].[Url] LIKE N'S_i%'
- متدهای ToUpper و ToLower سمت سرور محاسبه می‌شوند (هرچند اگر از SQL Server استفاده می‌کنید، Collation پیش‌فرض آن غیرحساس به حروف کوچک و بزرگ است و در اکثر مواقع نیازی به یک چنین متدهایی نیست):
var test6 = context.Blogs
.Where(blog => blog.Url.ToUpper() == "E")
.ToList();
// SELECT [blog].[BlogId], [blog].[Url]
// FROM [Blogs] AS [blog]
// WHERE UPPER([blog].[Url]) = N'E'
- اما حالت‌های Invariant دار آن‌ها خیر و در سمت کلاینت محاسبه می‌شوند:
var test7 = context.Blogs
.Where(blog => blog.Url.ToUpperInvariant() == "F")
.ToList();
// SELECT [blog].[BlogId], [blog].[Url]
// FROM [Blogs] AS [blog]
مطالب
بررسی Bad code smell ها: متد حسود یا Feature envy
متد حسود یا Feature envy در دسته بندی «کدهایی بیش از اندازه، وابسته به هم» قرار می‌گیرد. چنین متدی بیش از آنکه از فیلدها و خصوصیات کلاس خود استفاده کند، از فیلدها و خصوصیات شیء دیگری از نوعی دیگر، استفاده می‌کند.  
یکی از اشکالات کدهای بیش از اندازه وابسته به هم، دشواری در نگهداری و تغییر کد است. به‌طوری‌که در زمان تغییر بخشی از کد، نیاز است بخش‌های مرتبط نیز مورد بررسی قرار گیرند. همچنین وابستگی بیش از اندازه کلاس‌ها به یکدیگر قابلیت جداسازی و استفاده مجدد کلاس‌ها را کاهش خواهد داد.
معمولا در نتیجه‌ی جابجایی مسئولیت‌ها، در بخش‌های مختلف کد، شاهد چنین کد بد بویی هستیم. یکی از پر تکرارترین شرایط، زمانی است که تعدادی از متغیرهای متد، به کلاس داده خاص خود منتقل شوند.
 به طور مثال: 
در این کد یک کلاس برای ایجاد یک آیتم صورت حساب نوشته شده است که یک آیتم از قرارداد را به عنوان ورودی دریافت و سپس اقدام به ایجاد یک آیتم صورت حساب می‌کند.
public class OrderItem 
{ 
    public int Quantity { get; set; } 
    public decimal UnitPrice { get; set; } 
    public decimal Discount { get; set; } 
} 
public class InvoiceItemGenerator 
{ 
    private readonly OrderItem _orderItem; 
    public InvoiceItemGenerator(OrderItem orderItem) 
    { 
        _orderItem = orderItem; 
    } 
    public dynamic Generate() 
    { 
        dynamic invoiceItem = new ExpandoObject(); 
        invoiceItem.Amount = _orderItem.Quantity * _orderItem.UnitPrice - _orderItem.Discount; 
        return invoiceItem; 
    } 
}
اگر به متد Generate دقت کرده باشید متوجه خواهید شد که این متد از خصوصیات شی OrderItem استفاده بیش از اندازه می‌کند و در حال انجام محاسبه‌ای است که ظاهرا بهتر بود جای دیگری باشد.  
در این مثال خاص دو راه حل برای این موضوع وجود دارد: 
اول: انتقال منطق محاسبه مبلغ نهایی آیتم، به کلاس OrderItem. مانند:
public class OrderItem 
{ 
    public int Quantity { get; set; } 
    public decimal UnitPrice { get; set; } 
    public decimal Discount { get; set; } 
    public decimal GetFinalAmount() 
    { 
        return Quantity * UnitPrice - Discount; 
    } 
} 
public class InvoiceItemGenerator 
{ 
    private readonly OrderItem _orderItem; 
    public InvoiceItemGenerator(OrderItem orderItem) 
    { 
        _orderItem = orderItem; 
    } 
    public dynamic Generate() 
    { 
        dynamic invoiceItem = new ExpandoObject(); 
        invoiceItem.Amount = _orderItem.GetFinalAmount(); 
        return invoiceItem; 
    } 
}
دوم: انقال کل منطق محاسبه قیمت به کلاسی مثلا با نام InvoiceItemAmountCalculator که در نوشته‌های پیشین نمونه‌ای از آن را مشاهده کردیم. در واقع در این روش استراتژی محاسبه قیمت را به صورت کلاسی مجزا پیاده سازی می‌کنیم. 
به طور کلی روش‌های رفع چنین بوی بدی به همین دو نوع برخورد ختم خواهد شد. ایجاد یک کلاس استراتژی از نظر اصل Single responsibility مفید است؛ ولی ممکن است کد را در دام «درخت ارث بری موازی» بیندازد. 

جمع بندی 

این کد بد بو نیز یکی از پرتکرارترین کدهای بد بوی قابل مشاهده در پروژه‌های نرم افزاری است. یکی از نتایج مستقیم این کد بد بو، وجود کدهای تکراری فراوان برای انجام روال‌های تقریبا یکسان است که با رفع این بو به خوبی برطرف می‌شوند. همچنین رفع این بوی بد به افزایش قابلیت نگهداری کد نیز کمک بسیار زیادی می‌کند. 
مطالب
آشنایی با Window Function ها در SQL Server بخش پنجم
در این بخش فانکشن د‌یگری از توابع تحلیلی به نام CUME_DIST را بررسی می‌نماییم.
  •      CUME_DIST: 
      بوسیله تابع CUME_DIST می‌توان ارزیابی نمود، در یک گروه، چه درصد از مقادیر،مساوی یا کوچکتر از مقدار سطر جاری می‌باشند، به این تابع cumulative distribution نیز گفته می‌شود.
   Syntax تابع CUME_DIST به صورت زیر است:
CUME_DIST( )
    OVER ( [ partition_by_clause ] order_by_clause )
شرح Syntax:
1- Partition By Clause : بوسیله پارامتر فوق می‌توانید، نتیجه پرس جو (Query)،خود را دسته بندی نمایید.
2- order by clause : همانطور که از نامش مشخص است، جهت مرتب نمودن خروجی Query می‌باشد.
معمولا شرح عملکرد توابع تحلیلی، کمی مشکل است. بنابراین برای درک، عملکرد تابع CUME_DIST چند مثال را بررسی می‌کنیم.
در ابتدا بوسیله Script زیر یک جدول ایجاد و 10 رکورد در آن درج می‌کنیم:
Create Table TestCUME_DIST
(SalesOrderID int not null,
OrderQty smallint not null,
ProductID int not null
 );
 GO
Insert Into TestCUME_DIST
       Values (43663,1,760),(43667,3,710),(43667,1,773),
  (43667,1,775),(43667,1,778),(43669,1,747),
  (43670,1,709),(43670,2,710),(43670,2,773),(43670,1,776)

مثال اول: Script زیر را اجرا می‌کنیم، سپس خروجی آن را بررسی می‌نماییم:
SELECT SalesOrderID, OrderQty,
       CUME_DIST() OVER(ORDER BY SalesOrderID) AS [CUME_DIST]
FROM TestCUME_DIST ORDER BY [CUME_DIST] DESC
پس از اجرا خروجی بصورت زیر خواهد بود:

در ادامه اجازه دهید،مقادیری که در فیلد CUME_DIST بدست آمده است را بصورت تصویری بررسی کنیم.
مقادیر سطر اول تا چهارم:

*** برای بدست آوردن CUME_DIST سطر پنجم نیز خواهیم داشت:

Rows=(c1+c2)/c3 بنابراین خواهیم داشت: 0/6=10/(5+1)=Rows

مثال دوم : ابتدا Script زیر را اجرا نمایید:

SELECT SalesOrderID, OrderQty, ProductID,
       CUME_DIST() OVER(PARTITION BY SalesOrderID ORDER BY ProductID ) AS [CUME_DIST]
FROM TestCUME_DIST
WHERE SalesOrderID IN (43670, 43669, 43667, 43663)
ORDER BY SalesOrderID DESC, [CUME_DIST] DESC
خروجی : 

   همانگونه که ملاحظه می‌کنید، در این مثال، خروجی، براساس SalesOrderID به چهار گروه تقسیم می‌شود و عملیات مرتب سازی روی فیلد ProductID انجام  می گیرد، بنابراین CUME_DIST، روی هر گروه بر روی فیلد ProductID محاسبه می‌شود.

گروه اول : نحوه محاسبه Cume_DIST سطر اول:

سوال:چه تعداد از مقادیر ProductID آن برابر 776 میباشد؟

جواب: فقط مقدار سطر اول، بنابراین خواهیم داشت                   C1=1

سوال: چه تعداد از مقادیر کوچکتر از ProductID=776 می‌باشد؟

جواب: مقدار سه سطر، در واقع مقادیر سطر دوم،سوم و چهارم کوچکتر از مقدار سطر اول می‌باشند،                          c2=3

سوال: تعداد کل سطرهای گروه اول چه مقدار می‌باشد؟

جواب: 4سطر 

بنابراین برای بدست آوردن CUME_DIST سطر اول خواهیم داشت:

1=4/(1+3)=Rows 

محاسبه سطر دوم از گروه اول بدون شرح:

0/75=4/(1+2)=Rows  

امیدوارم مفید واقع شده باشد.

مطالب
آموزش MDX Query - قسمت پانزدهم – اعمال شرط بر روی خروجی عمل واکشی

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

Select
From [Adventure Works]

همان طور که مشاهده می‌کنید، خروجی یک عدد می‌باشد. بدون نام ستون یا ردیف؟!

به‌خاطر بیاورید که هر Cube در SSAS دارای یک Measure پیش فرض بود که در صورت عدم اعلام نام یک Measure در کوئری، SSAS به صورت پیش فرض مقدار این Measure را بر می‌گرداند. خوب؛ نام ستون و سطر چرا ذکر نشده است؟ به دلیل عدم اعلام صریح نام سطر و ستون در کوئری بالا، SSAS نام ستون و سطر خاصی را نمی‌تواند نمایش دهد.

با بررسی کوئری زیر به درک بیشتری از شاخص (Measure) پیش فرض Cube دست پیدا خواهید کرد. 

Select
From [Adventure Works]
Where
( [Measures].[Reseller Sales Amount] )

خروجی همچنان مانند بالا می‌باشد اما در این حالت اعلام شده است که از کدام شاخص باید واکشی انجام شود. دلیل خروجی مشابه، یکسان بودن شاخص پیش فرض و شرط اعلام شده می‌باشد. به بیان دیگر [Measures].[Reseller Sales Amount] در [Adventure Works] به عنوان شاخص پیش فرض معرفی شده است و با اجرای کوئری زیر عملا شرط واکشی برای یک شاخص متفاوت اعمال شده است.

Select
From [Adventure Works]
Where [Measures].[Internet Sales Amount]

کوئری زیر را اجرا کنید:

Select
[Measures].[Internet Sales Amount] on columns
From [Adventure Works]

تنها تفاوت دو کوئری بالا ، در آوردن نام ستون می باشد . زیرا در هر دو سرجمع ، یک شاخص واکشی می گردد .

کوئری زیر را اجرا کنید:

select
[Measures].[Internet Sales Amount] on columns
From [Adventure Works]
Where [Measures].[Internet Sales Amount]

این کوئری با خطا مواجه می شود زیرا در  آن کوئری در یک  Axis  و در شرط ، اعمال انتخاب شاخص شده است که این مورد فقط می‌ بایستی در یکی از  این دو قسمت رخ دهد

و همچنین در صورت انتخاب دو شاخص متفاوت نیز با خطا برخورد خواهیم کرد.

Select
[Measures].[Internet Sales Amount] on columns
From [Adventure Works]
Where [Measures].[Reseller Sales Amount]

به عبارت دیگر نمی‌توان در خواست فیلتر کردن کوئری را برروی شاخص 1 داد؛ در صورتیکه می‌خواهیم شاخص 2 را واکشی کنیم. اعمال شرط برای واکشی اطلاعات از  شاخص، پیش فرض نوشتن این شرط لازم نمی‌باشد؛ زیرا این شاخص به صورت پیش فرض انتخاب شده‌است.  

select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
 }on columns
From [Adventure Works]
Where [Measures].[Reseller Sales Amount]

بنابراین کوئری بالا و کوئری زیر یکسان عمل خواهند کرد:

select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
 }on columns
From [Adventure Works]

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

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where [Customer].[Customer Geography].[Country].[Canada]

با اعمال شرط کشور کانادا، عملا خروجی فروش نمایندگان فروش در کانادا بر اساس دسته بندی محصولات واکشی می‌گردد. کمی به خروجی دقت نمایید. مبلغ سرجمع برابر مبلغ کل فروش اینترنتی می‌باشد که در کوئری‌های قبلی بدست آوردیم؟!

خروجی این کوئری مشکوک به نظر می رسد . زیرا سرجمع مبالغ فروش نمایندگان فروش برای کانادایی ها برابر کل فروش نمایندگان فروش می باشد .آیا کانادایی ها تمام خرید را انجام داده اند؟ خیر .

دلیل این اشکال در این است که هیچ گونه ارتباطی بین بعد مشتری و شاخص پیش فرض در سیستم وجود ندارد .

مشکل کوئری بالا در این کوئری با تغییر بعد در قسمت اعمال شرط برطرف شده؛ اکنون خروجی حقیقی مشاهده می شود .

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
}on columns
From [Adventure Works]
Where [Sales Territory].[Sales Territory].[Country].[Canada]

حال اگر بخواهیم دو شرط را به صورت همزمان داشته باشیم به صورت زیر عمل خواهیم کرد :

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where (
[Customer].[Customer Geography].[Country].[Canada],
[Measures].[Internet Sales Amount]
)

در کوئری بالا سرجمع فروش اینترنتی توسط مشتریان کانادایی بدست آمده است.

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

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns,
[Measures].[Internet Sales Amount] On rows
From [Adventure Works]
Where (
[Customer].[Customer Geography].[Country].[Canada]
)

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

Select
{
[Product].[Product Categories].[Category],[Product].[Product Categories]
} on columns
From [Adventure Works]
Where
(
[Customer].[Customer Geography].[Country].[Canada],
[Customer].[Customer Geography].[Country].[Australia],
[Measures].[Internet Sales Amount]
)

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

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where
(
{
[Customer].[Customer Geography].[Country].[Canada],
[Customer].[Customer Geography].[Country].[Australia]
},
[Measures].[Internet Sales Amount]
)

که همان کوئری بالا می باشد با این تفاوت که از {}  استفاده شده است

درابتدا میزان فروش نمایندگان فروش در انگلستان را بدست می‌آوریم:

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where
[Sales Territory].[Sales Territory].[Country].[United Kingdom]

و برای بدست آوردن فروش اینترنتی تمام کشور ها به جز انگلستان بر اساس دسته بندی محصولات کوئری زیر را خواهیم نوشت :

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where
[Sales Territory].[Sales Territory].[Country]
-
[Sales Territory].[Sales Territory].[Country].[United Kingdom]

البته از تابع Except  هم می‌توان به صورت زیر استفاده کرد

Select
{
[Product].[Product Categories].[Category],
[Product].[Product Categories]
} on columns
From [Adventure Works]
Where
except(
[Sales Territory].[Sales Territory].[Country],
[Sales Territory].[Sales Territory].[Country].[United Kingdom]
)

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