commit 13958704987e2b633a6af9f81819c6d58107a776 Author: christianwade Date: Thu Dec 1 13:39:44 2016 -0800 AsPartitionProcessing diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c9a181 --- /dev/null +++ b/.gitignore @@ -0,0 +1,242 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +[Xx]64/ +[Xx]86/ +[Bb]uild/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml + +# TODO: Un-comment the next line if you do not want to checkin +# your web deploy settings because they may include unencrypted +# passwords +#*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# LightSwitch generated files +GeneratedArtifacts/ +ModelManifest.xml + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ diff --git a/AsPartitionProcessing/AdventureWorksDW.bak b/AsPartitionProcessing/AdventureWorksDW.bak new file mode 100644 index 0000000..34cc4c6 Binary files /dev/null and b/AsPartitionProcessing/AdventureWorksDW.bak differ diff --git a/AsPartitionProcessing/AsPartitionProcessing.AdventureWorks/AsPartitionProcessing.AdventureWorks.smproj b/AsPartitionProcessing/AsPartitionProcessing.AdventureWorks/AsPartitionProcessing.AdventureWorks.smproj new file mode 100644 index 0000000..d26bf54 --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing.AdventureWorks/AsPartitionProcessing.AdventureWorks.smproj @@ -0,0 +1,42 @@ + + + + Development + 2.0 + {7274efcc-e7df-40ad-83df-1dfd5b954e67} + Exe + MyRootNamespace + MyAssemblyName + false + bin\ + AsPartitionProcessing.AdventureWorks + localhost + Developer + Version_11_0 + AW Tabular Model SQL 2014 + Model + Default + False + InMemory + Default + + + bin\ + localhost + Developer + Version_11_0 + AdventureWorks + Model + Default + False + InMemory + Default + + + + + Code + + + + \ No newline at end of file diff --git a/AsPartitionProcessing/AsPartitionProcessing.AdventureWorks/Model.bim b/AsPartitionProcessing/AsPartitionProcessing.AdventureWorks/Model.bim new file mode 100644 index 0000000..cf4cd59 --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing.AdventureWorks/Model.bim @@ -0,0 +1,6009 @@ +{ + "name": "SemanticModel", + "id": "SemanticModel", + "compatibilityLevel": 1200, + "model": { + "culture": "en-US", + "dataSources": [ + { + "name": "AdventureWorksDW", + "connectionString": "Provider=SQLNCLI11;Data Source=localhost;Integrated Security=SSPI;Persist Security Info=false;Initial Catalog=AdventureWorksDW", + "impersonationMode": "impersonateServiceAccount", + "annotations": [ + { + "name": "ConnectionEditUISource", + "value": "SqlServer" + } + ] + } + ], + "tables": [ + { + "name": "Customer", + "columns": [ + { + "name": "Customer Id", + "dataType": "string", + "sourceColumn": "CustomerAlternateKey" + }, + { + "name": "Title", + "dataType": "string", + "sourceColumn": "Title" + }, + { + "name": "First Name", + "dataType": "string", + "sourceColumn": "FirstName" + }, + { + "name": "Middle Name", + "dataType": "string", + "sourceColumn": "MiddleName" + }, + { + "name": "Last Name", + "dataType": "string", + "sourceColumn": "LastName" + }, + { + "name": "Name Style", + "dataType": "boolean", + "sourceColumn": "NameStyle" + }, + { + "name": "Birth Date", + "dataType": "dateTime", + "sourceColumn": "BirthDate" + }, + { + "name": "Marital Status", + "dataType": "string", + "sourceColumn": "MaritalStatus" + }, + { + "name": "Suffix", + "dataType": "string", + "sourceColumn": "Suffix" + }, + { + "name": "Gender", + "dataType": "string", + "sourceColumn": "Gender" + }, + { + "name": "Email Address", + "dataType": "string", + "sourceColumn": "EmailAddress" + }, + { + "name": "Yearly Income", + "dataType": "decimal", + "sourceColumn": "YearlyIncome", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Children", + "dataType": "int64", + "sourceColumn": "TotalChildren", + "summarizeBy": "none" + }, + { + "name": "Number of Children At Home", + "dataType": "int64", + "sourceColumn": "NumberChildrenAtHome", + "summarizeBy": "none" + }, + { + "name": "Education", + "dataType": "string", + "sourceColumn": "EnglishEducation" + }, + { + "name": "Occupation", + "dataType": "string", + "sourceColumn": "EnglishOccupation" + }, + { + "name": "Owns House", + "dataType": "boolean", + "sourceColumn": "HouseOwnerFlag", + "formatString": "\"TRUE\";\"TRUE\";\"FALSE\"" + }, + { + "name": "Address Line 1", + "dataType": "string", + "sourceColumn": "AddressLine1" + }, + { + "name": "Address Line 2", + "dataType": "string", + "sourceColumn": "AddressLine2" + }, + { + "name": "Date Of First Purchase", + "dataType": "dateTime", + "sourceColumn": "DateFirstPurchase" + }, + { + "name": "Commute Distance", + "dataType": "string", + "sourceColumn": "CommuteDistance" + }, + { + "name": "CustomerKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "CustomerKey" + }, + { + "name": "GeographyKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "GeographyKey" + }, + { + "name": "Total Cars Owned", + "dataType": "int64", + "sourceColumn": "NumberCarsOwned", + "summarizeBy": "none" + }, + { + "name": "Phone", + "dataType": "string", + "sourceColumn": "Phone" + } + ], + "partitions": [ + { + "name": "Customer", + "source": { + "query": "SELECT [dbo].[DimCustomer].* \t\tFROM [dbo].[DimCustomer]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization" + }, + { + "name": "QueryEditorSerialization" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "DimCustomer" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[DimCustomer].* FROM [dbo].[DimCustomer] " + } + ] + }, + { + "name": "Date", + "dataCategory": "Time", + "columns": [ + { + "name": "Date", + "dataType": "dateTime", + "isKey": true, + "sourceColumn": "FullDateAlternateKey" + }, + { + "name": "Day Number Of Week", + "dataType": "int64", + "sourceColumn": "DayNumberOfWeek", + "summarizeBy": "none" + }, + { + "name": "Day Of Month", + "dataType": "int64", + "sourceColumn": "DayNumberOfMonth", + "summarizeBy": "none" + }, + { + "name": "Day Of Year", + "dataType": "int64", + "sourceColumn": "DayNumberOfYear", + "summarizeBy": "none" + }, + { + "name": "Month Name", + "dataType": "string", + "sourceColumn": "EnglishMonthName", + "sortByColumn": "Month" + }, + { + "name": "Month", + "dataType": "int64", + "sourceColumn": "MonthNumberOfYear", + "summarizeBy": "none" + }, + { + "name": "Calendar Quarter", + "dataType": "int64", + "sourceColumn": "CalendarQuarter", + "summarizeBy": "none" + }, + { + "name": "Calendar Year", + "dataType": "int64", + "sourceColumn": "CalendarYear", + "summarizeBy": "none" + }, + { + "name": "Calendar Semester", + "dataType": "int64", + "sourceColumn": "CalendarSemester", + "summarizeBy": "none" + }, + { + "name": "Fiscal Quarter", + "dataType": "int64", + "sourceColumn": "FiscalQuarter", + "summarizeBy": "none" + }, + { + "name": "Fiscal Year", + "dataType": "int64", + "sourceColumn": "FiscalYear", + "summarizeBy": "none" + }, + { + "name": "Fiscal Semester", + "dataType": "int64", + "sourceColumn": "FiscalSemester", + "summarizeBy": "none" + }, + { + "name": "DateKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "DateKey", + "summarizeBy": "none" + }, + { + "name": "Day Name Of Week", + "dataType": "string", + "sourceColumn": "EnglishDayNameOfWeek", + "sortByColumn": "Day Number Of Week" + }, + { + "name": "Week Of Year", + "dataType": "int64", + "sourceColumn": "WeekNumberOfYear", + "summarizeBy": "none" + } + ], + "partitions": [ + { + "name": "Date", + "source": { + "query": "SELECT [dbo].[DimDate].* \t\tFROM [dbo].[DimDate]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization" + }, + { + "name": "QueryEditorSerialization" + } + ] + } + ], + "measures": [ + { + "name": "Days In Current Quarter", + "expression": "COUNTROWS( DATESBETWEEN( 'Date'[Date], STARTOFQUARTER( LASTDATE('Date'[Date])), ENDOFQUARTER('Date'[Date])))" + }, + { + "name": "Days In Current Quarter to Date", + "expression": "COUNTROWS( DATESQTD( 'Date'[Date]))" + } + ], + "hierarchies": [ + { + "name": "Calendar", + "levels": [ + { + "name": "Year", + "ordinal": 0, + "column": "Calendar Year" + }, + { + "name": "Semester", + "ordinal": 1, + "column": "Calendar Semester" + }, + { + "name": "Quarter", + "ordinal": 2, + "column": "Calendar Quarter" + }, + { + "name": "Month", + "ordinal": 3, + "column": "Month Name" + }, + { + "name": "Day", + "ordinal": 4, + "column": "Day Of Month" + } + ] + }, + { + "name": "Fiscal", + "levels": [ + { + "name": "Month", + "ordinal": 3, + "column": "Month Name" + }, + { + "name": "Day", + "ordinal": 4, + "column": "Day Of Month" + }, + { + "name": "Fiscal Year", + "ordinal": 0, + "column": "Fiscal Year" + }, + { + "name": "Fiscal Semester", + "ordinal": 1, + "column": "Fiscal Semester" + }, + { + "name": "Fiscal Quarter", + "ordinal": 2, + "column": "Fiscal Quarter" + } + ] + }, + { + "name": "Production Calendar", + "levels": [ + { + "name": "Year", + "ordinal": 0, + "column": "Calendar Year" + }, + { + "name": "Week", + "ordinal": 1, + "column": "Week Of Year" + }, + { + "name": "Week Day", + "ordinal": 2, + "column": "Day Name Of Week" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "DimDate" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[DimDate].* FROM [dbo].[DimDate] " + } + ] + }, + { + "name": "Geography", + "columns": [ + { + "name": "City", + "dataType": "string", + "sourceColumn": "City" + }, + { + "name": "State Province Code", + "dataType": "string", + "sourceColumn": "StateProvinceCode" + }, + { + "name": "State Province Name", + "dataType": "string", + "sourceColumn": "StateProvinceName" + }, + { + "name": "Country Region Code", + "dataType": "string", + "sourceColumn": "CountryRegionCode" + }, + { + "name": "Country Region Name", + "dataType": "string", + "sourceColumn": "EnglishCountryRegionName" + }, + { + "name": "Postal Code", + "dataType": "string", + "sourceColumn": "PostalCode" + }, + { + "name": "GeographyKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "GeographyKey" + }, + { + "name": "SalesTerritoryKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "SalesTerritoryKey" + } + ], + "partitions": [ + { + "name": "Geography", + "source": { + "query": "SELECT [dbo].[DimGeography].* \t\tFROM [dbo].[DimGeography]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization" + }, + { + "name": "QueryEditorSerialization" + } + ] + } + ], + "hierarchies": [ + { + "name": "Geography", + "levels": [ + { + "name": "Country Region", + "ordinal": 0, + "column": "Country Region Name" + }, + { + "name": "State Province", + "ordinal": 1, + "column": "State Province Name" + }, + { + "name": "City", + "ordinal": 2, + "column": "City" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "DimGeography" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[DimGeography].* FROM [dbo].[DimGeography] " + } + ] + }, + { + "name": "Product", + "columns": [ + { + "name": "Product Id", + "dataType": "string", + "sourceColumn": "ProductAlternateKey" + }, + { + "name": "Weight Unit Code", + "dataType": "string", + "sourceColumn": "WeightUnitMeasureCode" + }, + { + "name": "Size Unit Code", + "dataType": "string", + "sourceColumn": "SizeUnitMeasureCode" + }, + { + "name": "Product Name", + "dataType": "string", + "sourceColumn": "EnglishProductName", + "isDefaultLabel": true + }, + { + "name": "Standard Cost", + "dataType": "decimal", + "sourceColumn": "StandardCost", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Color", + "dataType": "string", + "sourceColumn": "Color" + }, + { + "name": "Safety Stock Level", + "dataType": "int64", + "sourceColumn": "SafetyStockLevel", + "summarizeBy": "none" + }, + { + "name": "Reorder Point", + "dataType": "int64", + "sourceColumn": "ReorderPoint", + "summarizeBy": "none" + }, + { + "name": "List Price", + "dataType": "decimal", + "sourceColumn": "ListPrice", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Size", + "dataType": "string", + "sourceColumn": "Size" + }, + { + "name": "Size Range", + "dataType": "string", + "sourceColumn": "SizeRange" + }, + { + "name": "Weight", + "dataType": "double", + "sourceColumn": "Weight", + "summarizeBy": "none" + }, + { + "name": "Days To Manufacture", + "dataType": "int64", + "sourceColumn": "DaysToManufacture" + }, + { + "name": "Product Line", + "dataType": "string", + "sourceColumn": "ProductLine" + }, + { + "name": "Dealer Price", + "dataType": "decimal", + "sourceColumn": "DealerPrice", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Class", + "dataType": "string", + "sourceColumn": "Class" + }, + { + "name": "Style", + "dataType": "string", + "sourceColumn": "Style" + }, + { + "name": "Model Name", + "dataType": "string", + "sourceColumn": "ModelName" + }, + { + "name": "Description", + "dataType": "string", + "sourceColumn": "EnglishDescription" + }, + { + "name": "Product Start Date", + "dataType": "dateTime", + "sourceColumn": "StartDate" + }, + { + "name": "Product End Date", + "dataType": "dateTime", + "sourceColumn": "EndDate" + }, + { + "name": "Product Status", + "dataType": "string", + "sourceColumn": "Status" + }, + { + "type": "calculated", + "name": "Product SubCategory Name", + "dataType": "string", + "isDataTypeInferred": true, + "expression": "RELATED('Product SubCategory'[Product Subcategory Name])" + }, + { + "type": "calculated", + "name": "Product Category Name", + "dataType": "string", + "isDataTypeInferred": true, + "expression": "RELATED('Product Category'[Product Category Name])" + }, + { + "name": "ProductKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ProductKey" + }, + { + "name": "ProductSubcategoryKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ProductSubcategoryKey" + }, + { + "name": "Is Finished Goods", + "dataType": "boolean", + "sourceColumn": "FinishedGoodsFlag" + }, + { + "name": "LargePhoto", + "dataType": "binary", + "sourceColumn": "LargePhoto", + "isDefaultImage": true + } + ], + "partitions": [ + { + "name": "Product", + "source": { + "query": "SELECT [dbo].[DimProduct].* \t\tFROM [dbo].[DimProduct]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization" + }, + { + "name": "QueryEditorSerialization" + } + ] + } + ], + "hierarchies": [ + { + "name": "Category", + "levels": [ + { + "name": "Category", + "ordinal": 0, + "column": "Product Category Name" + }, + { + "name": "SubCategory", + "ordinal": 1, + "column": "Product SubCategory Name" + }, + { + "name": "Model", + "ordinal": 2, + "column": "Model Name" + }, + { + "name": "Product", + "ordinal": 3, + "column": "Product Name" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "DimProduct" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[DimProduct].* FROM [dbo].[DimProduct] " + } + ] + }, + { + "name": "Product Category", + "columns": [ + { + "name": "Product Category Name", + "dataType": "string", + "sourceColumn": "EnglishProductCategoryName" + }, + { + "name": "ProductCategoryKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ProductCategoryKey", + "summarizeBy": "none" + } + ], + "partitions": [ + { + "name": "Product Category", + "source": { + "query": "SELECT [dbo].[DimProductCategory].* \t\tFROM [dbo].[DimProductCategory]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization" + }, + { + "name": "QueryEditorSerialization" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "DimProductCategory" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[DimProductCategory].* FROM [dbo].[DimProductCategory] " + } + ] + }, + { + "name": "Product Subcategory", + "columns": [ + { + "name": "Product Subcategory Name", + "dataType": "string", + "sourceColumn": "EnglishProductSubcategoryName" + }, + { + "name": "ProductSubcategoryKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ProductSubcategoryKey", + "summarizeBy": "none" + }, + { + "name": "ProductCategoryKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ProductCategoryKey", + "summarizeBy": "none" + } + ], + "partitions": [ + { + "name": "Product Subcategory", + "source": { + "query": "SELECT [dbo].[DimProductSubcategory].* \t\tFROM [dbo].[DimProductSubcategory]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization" + }, + { + "name": "QueryEditorSerialization" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "DimProductSubcategory" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[DimProductSubcategory].* FROM [dbo].[DimProductSubcategory] " + } + ] + }, + { + "name": "Currency", + "columns": [ + { + "name": "CurrencyKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "CurrencyKey" + }, + { + "name": "Currency Code", + "dataType": "string", + "sourceColumn": "CurrencyAlternateKey" + }, + { + "name": "CurrencyName", + "dataType": "string", + "sourceColumn": "CurrencyName" + } + ], + "partitions": [ + { + "name": "Currency", + "source": { + "query": "SELECT [dbo].[DimCurrency].* \t\tFROM [dbo].[DimCurrency]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "QueryEditorSerialization" + }, + { + "name": "TableWidgetSerialization" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "DimCurrency" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[DimCurrency].* FROM [dbo].[DimCurrency] " + } + ] + }, + { + "name": "Employee", + "columns": [ + { + "name": "EmployeeKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "EmployeeKey", + "summarizeBy": "none" + }, + { + "name": "ParentEmployeeKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ParentEmployeeKey", + "summarizeBy": "none" + }, + { + "name": "Employee Id", + "dataType": "string", + "sourceColumn": "EmployeeNationalIDAlternateKey" + }, + { + "name": "SalesTerritoryKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "SalesTerritoryKey" + }, + { + "name": "First Name", + "dataType": "string", + "sourceColumn": "FirstName" + }, + { + "name": "Last Name", + "dataType": "string", + "sourceColumn": "LastName" + }, + { + "name": "Middle Name", + "dataType": "string", + "sourceColumn": "MiddleName" + }, + { + "name": "NameStyle", + "dataType": "boolean", + "sourceColumn": "NameStyle" + }, + { + "name": "Title", + "dataType": "string", + "sourceColumn": "Title" + }, + { + "name": "Hire Date", + "dataType": "dateTime", + "sourceColumn": "HireDate" + }, + { + "name": "Birth Date", + "dataType": "dateTime", + "sourceColumn": "BirthDate" + }, + { + "name": "Login", + "dataType": "string", + "sourceColumn": "LoginID" + }, + { + "name": "Email", + "dataType": "string", + "sourceColumn": "EmailAddress" + }, + { + "name": "Phone", + "dataType": "string", + "sourceColumn": "Phone" + }, + { + "name": "Marital Status", + "dataType": "string", + "sourceColumn": "MaritalStatus" + }, + { + "name": "Emergency Contact Name", + "dataType": "string", + "sourceColumn": "EmergencyContactName" + }, + { + "name": "Emergency Contact Phone", + "dataType": "string", + "sourceColumn": "EmergencyContactPhone" + }, + { + "name": "Is Salaried", + "dataType": "boolean", + "sourceColumn": "SalariedFlag" + }, + { + "name": "Gender", + "dataType": "string", + "sourceColumn": "Gender" + }, + { + "name": "Pay Frequency", + "dataType": "int64", + "sourceColumn": "PayFrequency", + "summarizeBy": "none" + }, + { + "name": "Base Rate", + "dataType": "decimal", + "sourceColumn": "BaseRate", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Vacation Hours", + "dataType": "int64", + "sourceColumn": "VacationHours", + "summarizeBy": "none" + }, + { + "name": "Sick Leave Hours", + "dataType": "int64", + "sourceColumn": "SickLeaveHours", + "summarizeBy": "none" + }, + { + "name": "Is Current", + "dataType": "boolean", + "sourceColumn": "CurrentFlag" + }, + { + "name": "Is Sales Person", + "dataType": "boolean", + "sourceColumn": "SalesPersonFlag" + }, + { + "name": "Department Name", + "dataType": "string", + "sourceColumn": "DepartmentName" + }, + { + "name": "Start Date", + "dataType": "dateTime", + "sourceColumn": "StartDate" + }, + { + "name": "End Date", + "dataType": "dateTime", + "sourceColumn": "EndDate" + }, + { + "name": "Status", + "dataType": "string", + "sourceColumn": "Status" + }, + { + "name": "Employee Photo", + "dataType": "binary", + "sourceColumn": "EmployeePhoto", + "isDefaultImage": true + } + ], + "partitions": [ + { + "name": "Employee", + "source": { + "query": "SELECT [dbo].[DimEmployee].* \t\tFROM [dbo].[DimEmployee]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "QueryEditorSerialization" + }, + { + "name": "TableWidgetSerialization" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "DimEmployee" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[DimEmployee].* FROM [dbo].[DimEmployee] " + } + ] + }, + { + "name": "Promotion", + "columns": [ + { + "name": "PromotionKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "PromotionKey", + "summarizeBy": "none" + }, + { + "name": "Promotion Name", + "dataType": "string", + "sourceColumn": "EnglishPromotionName" + }, + { + "name": "DiscountPct", + "dataType": "double", + "sourceColumn": "DiscountPct", + "formatString": "0.00", + "summarizeBy": "none" + }, + { + "name": "Promotion Type", + "dataType": "string", + "sourceColumn": "EnglishPromotionType" + }, + { + "name": "Promotion Category", + "dataType": "string", + "sourceColumn": "EnglishPromotionCategory" + }, + { + "name": "Promotion Start Date", + "dataType": "dateTime", + "sourceColumn": "StartDate" + }, + { + "name": "Promotion End Date", + "dataType": "dateTime", + "sourceColumn": "EndDate" + }, + { + "name": "Min Quantity", + "dataType": "int64", + "sourceColumn": "MinQty" + }, + { + "name": "Max Quantity", + "dataType": "int64", + "sourceColumn": "MaxQty" + } + ], + "partitions": [ + { + "name": "Promotion", + "source": { + "query": "SELECT [dbo].[DimPromotion].* \t\tFROM [dbo].[DimPromotion]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "QueryEditorSerialization" + }, + { + "name": "TableWidgetSerialization" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "DimPromotion" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[DimPromotion].* FROM [dbo].[DimPromotion] " + } + ] + }, + { + "name": "Reseller", + "columns": [ + { + "name": "ResellerKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ResellerKey" + }, + { + "name": "GeographyKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "GeographyKey" + }, + { + "name": "Reseller Id", + "dataType": "string", + "sourceColumn": "ResellerAlternateKey" + }, + { + "name": "Reseller Phone", + "dataType": "string", + "sourceColumn": "Phone" + }, + { + "name": "Business Type", + "dataType": "string", + "sourceColumn": "BusinessType" + }, + { + "name": "Reseller Name", + "dataType": "string", + "sourceColumn": "ResellerName", + "isDefaultLabel": true + }, + { + "name": "Number Employees", + "dataType": "int64", + "sourceColumn": "NumberEmployees", + "summarizeBy": "none" + }, + { + "name": "Order Frequency", + "dataType": "string", + "sourceColumn": "OrderFrequency" + }, + { + "name": "Order Month", + "dataType": "int64", + "sourceColumn": "OrderMonth" + }, + { + "name": "First Order Year", + "dataType": "int64", + "sourceColumn": "FirstOrderYear", + "summarizeBy": "none" + }, + { + "name": "Last Order Year", + "dataType": "int64", + "sourceColumn": "LastOrderYear", + "summarizeBy": "none" + }, + { + "name": "Product Line", + "dataType": "string", + "sourceColumn": "ProductLine" + }, + { + "name": "Address Line 1", + "dataType": "string", + "sourceColumn": "AddressLine1" + }, + { + "name": "Address Line 2", + "dataType": "string", + "sourceColumn": "AddressLine2" + }, + { + "name": "Annual Sales", + "dataType": "decimal", + "sourceColumn": "AnnualSales", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Bank Name", + "dataType": "string", + "sourceColumn": "BankName" + }, + { + "name": "Min Payment Type", + "dataType": "int64", + "sourceColumn": "MinPaymentType", + "summarizeBy": "none" + }, + { + "name": "Min Payment Amount", + "dataType": "decimal", + "sourceColumn": "MinPaymentAmount", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Annua Revenue", + "dataType": "decimal", + "sourceColumn": "AnnualRevenue", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Year Opened", + "dataType": "int64", + "sourceColumn": "YearOpened", + "summarizeBy": "none" + } + ], + "partitions": [ + { + "name": "Reseller", + "source": { + "query": "SELECT [dbo].[DimReseller].* \t\tFROM [dbo].[DimReseller]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "QueryEditorSerialization" + }, + { + "name": "TableWidgetSerialization" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "DimReseller" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[DimReseller].* FROM [dbo].[DimReseller] " + } + ] + }, + { + "name": "Sales Territory", + "columns": [ + { + "name": "SalesTerritoryKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "SalesTerritoryKey" + }, + { + "name": "Sales Territory Region", + "dataType": "string", + "sourceColumn": "SalesTerritoryRegion", + "isDefaultLabel": true + }, + { + "name": "Sales Territory Country", + "dataType": "string", + "sourceColumn": "SalesTerritoryCountry" + }, + { + "name": "Sales Territory Group", + "dataType": "string", + "sourceColumn": "SalesTerritoryGroup" + }, + { + "name": "Sales Territory Image", + "dataType": "binary", + "sourceColumn": "SalesTerritoryImage", + "isDefaultImage": true + } + ], + "partitions": [ + { + "name": "Sales Territory", + "source": { + "query": "SELECT [dbo].[DimSalesTerritory].* \t\tFROM [dbo].[DimSalesTerritory]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "QueryEditorSerialization" + }, + { + "name": "TableWidgetSerialization" + } + ] + } + ], + "measures": [ + { + "name": "Distinct Count Sales Orders", + "expression": "[Reseller Distinct Count Sales Order] + [Internet Distinct Count Sales Order]", + "formatString": "#,0" + }, + { + "name": "Total Sales", + "expression": "[Reseller Total Sales] + [Internet Total Sales]", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Discount Amount", + "expression": "[Reseller Total Discount Amount] + [Internet Total Discount Amount]", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Products Cost", + "expression": "[Reseller Total Product Cost] + [Internet Total Product Cost]", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Tax Amount", + "expression": "[Reseller Total Tax Amount] + [Internet Total Tax Amount]", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Freight", + "expression": "[Reseller Total Freight] + [Internet Total Freight]", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Gross Profit", + "expression": "[Reseller Total Gross Profit] + [Internet Total Gross Profit]", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Units Sold", + "expression": "[Reseller Total Units] + [Internet Total Units]", + "formatString": "#,0" + }, + { + "name": "Order Lines Count", + "expression": "[Reseller Order Lines Count] + [Internet Order Lines Count]", + "formatString": "#,0" + }, + { + "name": "Total Previous Quarter Gross Profit", + "expression": "CALCULATE([Total Gross Profit], PREVIOUSQUARTER('Date'[Date]))", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Current Quarter Gross Profit", + "expression": "TOTALQTD([Total Gross Profit],'Date'[Date])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Previous Quarter Gross Profit Proportion to QTD", + "expression": "[Total Previous Quarter Gross Profit]*([Days In Current Quarter to Date]/[Days In Current Quarter])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Previous Quarter Sales", + "expression": "CALCULATE([Total Sales], PREVIOUSQUARTER('Date'[Date]))", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Current Quarter Sales", + "expression": "TOTALQTD([Total Sales],'Date'[Date])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Previous Quarter Sales Proportion to QTD", + "expression": "[Total Previous Quarter Sales]*([Days In Current Quarter to Date]/[Days In Current Quarter])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Sales - Sales Territory sliced by Employee", + "expression": "[Reseller Total Sales - Sales Territory sliced by Employee] + [Internet Total Sales]", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Current Quarter Sales Performance", + "expression": "IFERROR([Total Current Quarter Sales]/[Total Previous Quarter Sales Proportion to QTD],BLANK())", + "kpi": { + "targetExpression": "1.1", + "statusGraphic": "Three Symbols UnCircled Colored", + "statusExpression": [ + "if(ISBLANK('Sales Territory'[Total Current Quarter Sales Performance]),BLANK(),", + " If('Sales Territory'[Total Current Quarter Sales Performance]<0.8,-1,", + "\t If('Sales Territory'[Total Current Quarter Sales Performance]<1.07,0,1)", + " )", + " )", + " " + ], + "annotations": [ + { + "name": "GoalType", + "value": "StaticValue" + }, + { + "name": "KpiStatusType", + "value": "Linear" + }, + { + "name": "KpiThresholdType", + "value": "Absolute" + }, + { + "name": "KpiThresholdOrdering", + "value": "Ascending" + }, + { + "name": "KpiThresholdCount", + "value": "2" + }, + { + "name": "KpiThreshold_0", + "value": "0.8" + }, + { + "name": "KpiThreshold_1", + "value": "1.07" + } + ] + } + }, + { + "name": "Total Current Quarter Gross Profit Performance", + "expression": "IF([Total Previous Quarter Gross Profit Proportion to QTD]<>0,([Total Current Quarter Gross Profit]-[Total Previous Quarter Gross Profit Proportion to QTD])/[Total Previous Quarter Gross Profit Proportion to QTD],BLANK())", + "kpi": { + "targetExpression": "1.25", + "statusGraphic": "Three Symbols UnCircled Colored", + "statusExpression": [ + "if(ISBLANK('Sales Territory'[Total Current Quarter Gross Profit Performance]),BLANK(),", + " If('Sales Territory'[Total Current Quarter Gross Profit Performance]<0.8,-1,", + "\t If('Sales Territory'[Total Current Quarter Gross Profit Performance]<1.03,0,1)", + " )", + " )", + " " + ], + "annotations": [ + { + "name": "GoalType", + "value": "StaticValue" + }, + { + "name": "KpiStatusType", + "value": "Linear" + }, + { + "name": "KpiThresholdType", + "value": "Absolute" + }, + { + "name": "KpiThresholdOrdering", + "value": "Ascending" + }, + { + "name": "KpiThresholdCount", + "value": "2" + }, + { + "name": "KpiThreshold_0", + "value": "0.8" + }, + { + "name": "KpiThreshold_1", + "value": "1.03" + } + ] + } + } + ], + "hierarchies": [ + { + "name": "Territory", + "levels": [ + { + "name": "Group", + "ordinal": 0, + "column": "Sales Territory Group" + }, + { + "name": "Country", + "ordinal": 1, + "column": "Sales Territory Country" + }, + { + "name": "Region", + "ordinal": 2, + "column": "Sales Territory Region" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "DimSalesTerritory" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[DimSalesTerritory].* FROM [dbo].[DimSalesTerritory] " + } + ] + }, + { + "name": "Product Inventory", + "columns": [ + { + "name": "ProductKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ProductKey" + }, + { + "name": "DateKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "DateKey" + }, + { + "name": "Movement Date", + "dataType": "dateTime", + "sourceColumn": "MovementDate", + "formatString": "Short Date", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Unit Cost", + "dataType": "decimal", + "sourceColumn": "UnitCost", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Units In", + "dataType": "int64", + "sourceColumn": "UnitsIn" + }, + { + "name": "Units Out", + "dataType": "int64", + "sourceColumn": "UnitsOut" + }, + { + "name": "Units Balance", + "dataType": "int64", + "sourceColumn": "UnitsBalance", + "summarizeBy": "none" + }, + { + "type": "calculated", + "name": "Product-Date Inventory Value", + "dataType": "decimal", + "isDataTypeInferred": true, + "expression": "[Unit Cost]*[Units Balance]", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "type": "calculated", + "name": "Product-Date Optimal Inventory Value", + "dataType": "decimal", + "isDataTypeInferred": true, + "expression": "[Unit Cost]*RELATED('Product'[Reorder Point])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "type": "calculated", + "name": "Product-Date Max Inventory Value", + "dataType": "decimal", + "isDataTypeInferred": true, + "expression": "[Unit Cost]*RELATED('Product'[Safety Stock Level])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "type": "calculated", + "name": "Product-Date OverStocked", + "dataType": "int64", + "isDataTypeInferred": true, + "expression": "IF([Units Balance]>RELATED('Product'[Safety Stock Level]),1,0)" + }, + { + "type": "calculated", + "name": "Product-Date UnderStocked", + "dataType": "int64", + "isDataTypeInferred": true, + "expression": "IF([Units Balance]= 20050101) AND ([DateKey] <= 20051231))", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization", + "value": [ + "<?xml version=\"1.0\" encoding=\"UTF-16\"?><Gemini xmlns=\"TableWidgetSerialization\"><AnnotationContent><![CDATA[<?xml version=\"1.0\"?>", + "<TableWidgetGridSerialization xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">", + " <ColumnSuggestedType />", + " <ColumnFormat />", + " <ColumnAccuracy />", + " <ColumnCurrencySymbol />", + " <ColumnPositivePattern />", + " <ColumnNegativePattern />", + " <ColumnWidths>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>105</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>123</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>84</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>92</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>111</int>", + " </value>", + " </item>", + " </ColumnWidths>", + " <ColumnDisplayIndex>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>0</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>1</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>2</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>3</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>4</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>5</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>6</int>", + " </value>", + " </item>", + " </ColumnDisplayIndex>", + " <ColumnFrozen />", + " <ColumnChecked />", + " <ColumnFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <FilterExpression xsi:type=\"LogicalExpression\">", + " <Operator>AND</Operator>", + " <ExpressionList>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>GreaterThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20050101</Value>", + " </FilterExpression>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>LesserThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20051231</Value>", + " </FilterExpression>", + " </ExpressionList>", + " </FilterExpression>", + " </value>", + " </item>", + " </ColumnFilter>", + " <SelectionFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <SelectionFilter xsi:nil=\"true\" />", + " </value>", + " </item>", + " </SelectionFilter>", + " <FilterParameters>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <CommandParameters />", + " </value>", + " </item>", + " </FilterParameters>", + " <IsSortDescending>false</IsSortDescending>", + "</TableWidgetGridSerialization>]]></AnnotationContent></Gemini>" + ] + }, + { + "name": "TableWidgetSourceTable" + }, + { + "name": "QueryEditorSerialization" + } + ] + }, + { + "name": "Product Inventory 2006", + "mode": "import", + "source": { + "query": "SELECT [dbo].[FactProductInventory].* \t\tFROM [dbo].[FactProductInventory] WHERE (([DateKey] >= 20060101) AND ([DateKey] <= 20061231))", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization", + "value": [ + "<?xml version=\"1.0\" encoding=\"UTF-16\"?><Gemini xmlns=\"TableWidgetSerialization\"><AnnotationContent><![CDATA[<?xml version=\"1.0\"?>", + "<TableWidgetGridSerialization xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">", + " <ColumnSuggestedType />", + " <ColumnFormat />", + " <ColumnAccuracy />", + " <ColumnCurrencySymbol />", + " <ColumnPositivePattern />", + " <ColumnNegativePattern />", + " <ColumnWidths>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>105</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>123</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>84</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>92</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>111</int>", + " </value>", + " </item>", + " </ColumnWidths>", + " <ColumnDisplayIndex>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>0</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>1</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>2</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>3</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>4</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>5</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>6</int>", + " </value>", + " </item>", + " </ColumnDisplayIndex>", + " <ColumnFrozen />", + " <ColumnChecked />", + " <ColumnFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <FilterExpression xsi:type=\"LogicalExpression\">", + " <Operator>AND</Operator>", + " <ExpressionList>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>GreaterThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20060101</Value>", + " </FilterExpression>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>LesserThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20061231</Value>", + " </FilterExpression>", + " </ExpressionList>", + " </FilterExpression>", + " </value>", + " </item>", + " </ColumnFilter>", + " <SelectionFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <SelectionFilter xsi:nil=\"true\" />", + " </value>", + " </item>", + " </SelectionFilter>", + " <FilterParameters>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <CommandParameters />", + " </value>", + " </item>", + " </FilterParameters>", + " <IsSortDescending>false</IsSortDescending>", + "</TableWidgetGridSerialization>]]></AnnotationContent></Gemini>" + ] + }, + { + "name": "TableWidgetSourceTable" + }, + { + "name": "QueryEditorSerialization" + } + ] + }, + { + "name": "Product Inventory 2007", + "mode": "import", + "source": { + "query": "SELECT [dbo].[FactProductInventory].* \t\tFROM [dbo].[FactProductInventory] WHERE (([DateKey] >= 20070101) AND ([DateKey] <= 20071231))", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization", + "value": [ + "<?xml version=\"1.0\" encoding=\"UTF-16\"?><Gemini xmlns=\"TableWidgetSerialization\"><AnnotationContent><![CDATA[<?xml version=\"1.0\"?>", + "<TableWidgetGridSerialization xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">", + " <ColumnSuggestedType />", + " <ColumnFormat />", + " <ColumnAccuracy />", + " <ColumnCurrencySymbol />", + " <ColumnPositivePattern />", + " <ColumnNegativePattern />", + " <ColumnWidths>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>105</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>123</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>84</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>92</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>111</int>", + " </value>", + " </item>", + " </ColumnWidths>", + " <ColumnDisplayIndex>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>0</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>1</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>2</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>3</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>4</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>5</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>6</int>", + " </value>", + " </item>", + " </ColumnDisplayIndex>", + " <ColumnFrozen />", + " <ColumnChecked />", + " <ColumnFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <FilterExpression xsi:type=\"LogicalExpression\">", + " <Operator>AND</Operator>", + " <ExpressionList>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>GreaterThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20070101</Value>", + " </FilterExpression>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>LesserThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20071231</Value>", + " </FilterExpression>", + " </ExpressionList>", + " </FilterExpression>", + " </value>", + " </item>", + " </ColumnFilter>", + " <SelectionFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <SelectionFilter xsi:nil=\"true\" />", + " </value>", + " </item>", + " </SelectionFilter>", + " <FilterParameters>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <CommandParameters />", + " </value>", + " </item>", + " </FilterParameters>", + " <IsSortDescending>false</IsSortDescending>", + "</TableWidgetGridSerialization>]]></AnnotationContent></Gemini>" + ] + }, + { + "name": "TableWidgetSourceTable" + }, + { + "name": "QueryEditorSerialization" + } + ] + }, + { + "name": "Product Inventory 2008", + "mode": "import", + "source": { + "query": "SELECT [dbo].[FactProductInventory].* \t\tFROM [dbo].[FactProductInventory] WHERE (([DateKey] >= 20080101) AND ([DateKey] <= 20081231))", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization", + "value": [ + "<?xml version=\"1.0\" encoding=\"UTF-16\"?><Gemini xmlns=\"TableWidgetSerialization\"><AnnotationContent><![CDATA[<?xml version=\"1.0\"?>", + "<TableWidgetGridSerialization xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">", + " <ColumnSuggestedType />", + " <ColumnFormat />", + " <ColumnAccuracy />", + " <ColumnCurrencySymbol />", + " <ColumnPositivePattern />", + " <ColumnNegativePattern />", + " <ColumnWidths>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>105</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>123</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>84</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>92</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>111</int>", + " </value>", + " </item>", + " </ColumnWidths>", + " <ColumnDisplayIndex>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>0</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>1</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>2</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>3</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>4</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>5</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>6</int>", + " </value>", + " </item>", + " </ColumnDisplayIndex>", + " <ColumnFrozen />", + " <ColumnChecked />", + " <ColumnFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <FilterExpression xsi:type=\"LogicalExpression\">", + " <Operator>AND</Operator>", + " <ExpressionList>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>GreaterThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20080101</Value>", + " </FilterExpression>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>LesserThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20081231</Value>", + " </FilterExpression>", + " </ExpressionList>", + " </FilterExpression>", + " </value>", + " </item>", + " </ColumnFilter>", + " <SelectionFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <SelectionFilter xsi:nil=\"true\" />", + " </value>", + " </item>", + " </SelectionFilter>", + " <FilterParameters>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <CommandParameters />", + " </value>", + " </item>", + " </FilterParameters>", + " <IsSortDescending>false</IsSortDescending>", + "</TableWidgetGridSerialization>]]></AnnotationContent></Gemini>" + ] + }, + { + "name": "TableWidgetSourceTable" + }, + { + "name": "QueryEditorSerialization" + } + ] + }, + { + "name": "Product Inventory 2009", + "mode": "import", + "source": { + "query": "SELECT [dbo].[FactProductInventory].* \t\tFROM [dbo].[FactProductInventory] WHERE (([DateKey] >= 20090101) AND ([DateKey] <= 20091231))", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization", + "value": [ + "<?xml version=\"1.0\" encoding=\"UTF-16\"?><Gemini xmlns=\"TableWidgetSerialization\"><AnnotationContent><![CDATA[<?xml version=\"1.0\"?>", + "<TableWidgetGridSerialization xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">", + " <ColumnSuggestedType />", + " <ColumnFormat />", + " <ColumnAccuracy />", + " <ColumnCurrencySymbol />", + " <ColumnPositivePattern />", + " <ColumnNegativePattern />", + " <ColumnWidths>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>105</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>123</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>84</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>92</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>111</int>", + " </value>", + " </item>", + " </ColumnWidths>", + " <ColumnDisplayIndex>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>0</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>1</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>2</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>3</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>4</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>5</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>6</int>", + " </value>", + " </item>", + " </ColumnDisplayIndex>", + " <ColumnFrozen />", + " <ColumnChecked />", + " <ColumnFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <FilterExpression xsi:type=\"LogicalExpression\">", + " <Operator>AND</Operator>", + " <ExpressionList>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>GreaterThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20090101</Value>", + " </FilterExpression>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>LesserThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20091231</Value>", + " </FilterExpression>", + " </ExpressionList>", + " </FilterExpression>", + " </value>", + " </item>", + " </ColumnFilter>", + " <SelectionFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <SelectionFilter xsi:nil=\"true\" />", + " </value>", + " </item>", + " </SelectionFilter>", + " <FilterParameters>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <CommandParameters />", + " </value>", + " </item>", + " </FilterParameters>", + " <IsSortDescending>false</IsSortDescending>", + "</TableWidgetGridSerialization>]]></AnnotationContent></Gemini>" + ] + }, + { + "name": "TableWidgetSourceTable" + }, + { + "name": "QueryEditorSerialization" + } + ] + }, + { + "name": "Product Inventory 2010", + "mode": "import", + "source": { + "query": "SELECT [dbo].[FactProductInventory].* \t\tFROM [dbo].[FactProductInventory] WHERE (([DateKey] >= 20100101) AND ([DateKey] <= 20101231))", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "TableWidgetSerialization", + "value": [ + "<?xml version=\"1.0\" encoding=\"UTF-16\"?><Gemini xmlns=\"TableWidgetSerialization\"><AnnotationContent><![CDATA[<?xml version=\"1.0\"?>", + "<TableWidgetGridSerialization xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">", + " <ColumnSuggestedType />", + " <ColumnFormat />", + " <ColumnAccuracy />", + " <ColumnCurrencySymbol />", + " <ColumnPositivePattern />", + " <ColumnNegativePattern />", + " <ColumnWidths>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>105</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>123</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>91</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>84</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>92</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>111</int>", + " </value>", + " </item>", + " </ColumnWidths>", + " <ColumnDisplayIndex>", + " <item>", + " <key>", + " <string>ProductKey</string>", + " </key>", + " <value>", + " <int>0</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <int>1</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>MovementDate</string>", + " </key>", + " <value>", + " <int>2</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitCost</string>", + " </key>", + " <value>", + " <int>3</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsIn</string>", + " </key>", + " <value>", + " <int>4</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsOut</string>", + " </key>", + " <value>", + " <int>5</int>", + " </value>", + " </item>", + " <item>", + " <key>", + " <string>UnitsBalance</string>", + " </key>", + " <value>", + " <int>6</int>", + " </value>", + " </item>", + " </ColumnDisplayIndex>", + " <ColumnFrozen />", + " <ColumnChecked />", + " <ColumnFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <FilterExpression xsi:type=\"LogicalExpression\">", + " <Operator>AND</Operator>", + " <ExpressionList>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>GreaterThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20100101</Value>", + " </FilterExpression>", + " <FilterExpression xsi:type=\"ConditionalExpression\">", + " <Operator>LesserThanEqualTo</Operator>", + " <Value xsi:type=\"xsd:double\">20101231</Value>", + " </FilterExpression>", + " </ExpressionList>", + " </FilterExpression>", + " </value>", + " </item>", + " </ColumnFilter>", + " <SelectionFilter>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <SelectionFilter xsi:nil=\"true\" />", + " </value>", + " </item>", + " </SelectionFilter>", + " <FilterParameters>", + " <item>", + " <key>", + " <string>DateKey</string>", + " </key>", + " <value>", + " <CommandParameters />", + " </value>", + " </item>", + " </FilterParameters>", + " <IsSortDescending>false</IsSortDescending>", + "</TableWidgetGridSerialization>]]></AnnotationContent></Gemini>" + ] + }, + { + "name": "TableWidgetSourceTable" + }, + { + "name": "QueryEditorSerialization" + } + ] + } + ], + "measures": [ + { + "name": "Total Units In", + "expression": "SUM([Units In])", + "formatString": "#,0" + }, + { + "name": "Total Units Out", + "expression": "SUM([Units Out])", + "formatString": "#,0" + }, + { + "name": "Total Units Movement", + "expression": "[Total Units In]-[Total Units Out]", + "formatString": "#,0" + }, + { + "name": "Total Units", + "expression": "CALCULATE(SUM([Units Balance]),LASTDATE('Product Inventory'[Movement Date]))", + "formatString": "#,0" + }, + { + "name": "Total Inventory Value", + "expression": "CALCULATE(SUM([Product-Date Inventory Value]),LASTDATE('Product Inventory'[Movement Date]))", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Inventory Optimal Value", + "expression": "CALCULATE(SUM([Product-Date Optimal Inventory Value]),LASTDATE('Product Inventory'[Movement Date]))", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Inventory Maximum Value", + "expression": "CALCULATE(SUM([Product-Date Max Inventory Value]),LASTDATE('Product Inventory'[Movement Date]))", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Inventory Value Performance", + "expression": "IF([Total Inventory Value]<[Total Inventory Optimal Value],-(1-([Total Inventory Value]/[Total Inventory Optimal Value])),([Total Inventory Value]-[Total Inventory Optimal Value])/([Total Inventory Maximum Value]-[Total Inventory Optimal Value]))", + "kpi": { + "targetExpression": "0", + "statusGraphic": "Three Symbols UnCircled Colored", + "statusExpression": [ + "if(ISBLANK('Product Inventory'[Total Inventory Value Performance]),BLANK(),", + " If('Product Inventory'[Total Inventory Value Performance]<0,", + "\t If('Product Inventory'[Total Inventory Value Performance]<-0.2,-1,0),", + "\t If('Product Inventory'[Total Inventory Value Performance]<0.6,1,", + "\t\t If('Product Inventory'[Total Inventory Value Performance]<1,0,-1)", + "\t )", + " )", + " )", + " " + ], + "annotations": [ + { + "name": "GoalType", + "value": "StaticValue" + }, + { + "name": "KpiStatusType", + "value": "Centered" + }, + { + "name": "KpiThresholdType", + "value": "Absolute" + }, + { + "name": "KpiThresholdOrdering", + "value": "Ascending" + }, + { + "name": "KpiThresholdCount", + "value": "4" + }, + { + "name": "KpiThreshold_0", + "value": "-0.2" + }, + { + "name": "KpiThreshold_1", + "value": "0" + }, + { + "name": "KpiThreshold_2", + "value": "0.6" + }, + { + "name": "KpiThreshold_3", + "value": "1" + } + ] + } + }, + { + "name": "Products OverStocked", + "expression": "CALCULATE(SUM([Product-Date OverStocked]),LASTDATE('Product Inventory'[Movement Date]))", + "kpi": { + "targetExpression": "0", + "statusGraphic": "Three Symbols UnCircled Colored", + "statusExpression": [ + "if(ISBLANK('Product Inventory'[Products OverStocked]),BLANK(),", + " If('Product Inventory'[Products OverStocked]<1,1,", + "\t If('Product Inventory'[Products OverStocked]<5,0,-1)", + " )", + " )", + " " + ], + "annotations": [ + { + "name": "GoalType", + "value": "StaticValue" + }, + { + "name": "KpiStatusType", + "value": "LinearReversed" + }, + { + "name": "KpiThresholdType", + "value": "Absolute" + }, + { + "name": "KpiThresholdOrdering", + "value": "Ascending" + }, + { + "name": "KpiThresholdCount", + "value": "2" + }, + { + "name": "KpiThreshold_0", + "value": "1" + }, + { + "name": "KpiThreshold_1", + "value": "5" + } + ] + } + }, + { + "name": "Products UnderStocked", + "expression": "CALCULATE(SUM([Product-Date UnderStocked]),LASTDATE('Product Inventory'[Movement Date]))", + "kpi": { + "targetExpression": "0", + "statusGraphic": "Three Symbols UnCircled Colored", + "statusExpression": [ + "if(ISBLANK('Product Inventory'[Products UnderStocked]),BLANK(),", + " If('Product Inventory'[Products UnderStocked]<5,1,", + "\t If('Product Inventory'[Products UnderStocked]<15,0,-1)", + " )", + " )", + " " + ], + "annotations": [ + { + "name": "GoalType", + "value": "StaticValue" + }, + { + "name": "KpiStatusType", + "value": "LinearReversed" + }, + { + "name": "KpiThresholdType", + "value": "Absolute" + }, + { + "name": "KpiThresholdOrdering", + "value": "Ascending" + }, + { + "name": "KpiThresholdCount", + "value": "2" + }, + { + "name": "KpiThreshold_0", + "value": "5" + }, + { + "name": "KpiThreshold_1", + "value": "15" + } + ] + } + }, + { + "name": "Products with Negative Stock", + "expression": "CALCULATE(SUM([Product-Date Negative Stock]),LASTDATE('Product Inventory'[Movement Date]))", + "kpi": { + "targetExpression": "0", + "statusGraphic": "Three Symbols UnCircled Colored", + "statusExpression": [ + "if(ISBLANK('Product Inventory'[Products with Negative Stock]),BLANK(),", + " If('Product Inventory'[Products with Negative Stock]<0.1,1,", + "\t If('Product Inventory'[Products with Negative Stock]<5,0,-1)", + " )", + " )", + " " + ], + "annotations": [ + { + "name": "GoalType", + "value": "StaticValue" + }, + { + "name": "KpiStatusType", + "value": "LinearReversed" + }, + { + "name": "KpiThresholdType", + "value": "Absolute" + }, + { + "name": "KpiThresholdOrdering", + "value": "Ascending" + }, + { + "name": "KpiThresholdCount", + "value": "2" + }, + { + "name": "KpiThreshold_0", + "value": "0.1" + }, + { + "name": "KpiThreshold_1", + "value": "5" + } + ] + } + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "FactProductInventory" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[FactProductInventory].* FROM [dbo].[FactProductInventory] " + } + ] + }, + { + "name": "Sales Quota", + "columns": [ + { + "name": "SalesQuotaKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "SalesQuotaKey", + "summarizeBy": "none" + }, + { + "name": "EmployeeKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "EmployeeKey", + "summarizeBy": "none" + }, + { + "name": "DateKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "DateKey", + "summarizeBy": "none" + }, + { + "name": "Calendar Year", + "dataType": "int64", + "sourceColumn": "CalendarYear", + "summarizeBy": "none" + }, + { + "name": "Calendar Quarter", + "dataType": "int64", + "sourceColumn": "CalendarQuarter", + "summarizeBy": "none" + }, + { + "name": "Sales Amount Quota", + "dataType": "decimal", + "sourceColumn": "SalesAmountQuota", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Date", + "dataType": "dateTime", + "sourceColumn": "Date", + "formatString": "Short Date", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + } + ], + "partitions": [ + { + "name": "Sales Quota", + "source": { + "query": "SELECT [dbo].[FactSalesQuota].* \t\tFROM [dbo].[FactSalesQuota]", + "dataSource": "AdventureWorksDW" + }, + "annotations": [ + { + "name": "QueryEditorSerialization" + }, + { + "name": "TableWidgetSerialization" + } + ] + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "FactSalesQuota" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[FactSalesQuota].* FROM [dbo].[FactSalesQuota] " + } + ] + }, + { + "name": "Internet Sales", + "columns": [ + { + "name": "Sales Order Number", + "dataType": "string", + "sourceColumn": "SalesOrderNumber" + }, + { + "name": "Sales Order Line Number", + "dataType": "int64", + "sourceColumn": "SalesOrderLineNumber", + "summarizeBy": "none" + }, + { + "name": "Revision Number", + "dataType": "int64", + "sourceColumn": "RevisionNumber", + "summarizeBy": "none" + }, + { + "name": "Order Quantity", + "dataType": "int64", + "sourceColumn": "OrderQuantity", + "summarizeBy": "none" + }, + { + "name": "Unit Price", + "dataType": "decimal", + "sourceColumn": "UnitPrice", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Extended Amount", + "dataType": "decimal", + "sourceColumn": "ExtendedAmount", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Unit Price Discount Pct", + "dataType": "double", + "sourceColumn": "UnitPriceDiscountPct", + "summarizeBy": "none" + }, + { + "name": "Discount Amount", + "dataType": "decimal", + "sourceColumn": "DiscountAmount", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Product Standard Cost", + "dataType": "decimal", + "sourceColumn": "ProductStandardCost", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Product Cost", + "dataType": "decimal", + "sourceColumn": "TotalProductCost", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Sales Amount", + "dataType": "decimal", + "sourceColumn": "SalesAmount", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Freight", + "dataType": "decimal", + "sourceColumn": "Freight", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Carrier Tracking Number", + "dataType": "string", + "sourceColumn": "CarrierTrackingNumber" + }, + { + "name": "Customer PO Number", + "dataType": "string", + "sourceColumn": "CustomerPONumber" + }, + { + "name": "Order Date", + "dataType": "dateTime", + "sourceColumn": "OrderDate" + }, + { + "name": "Due Date", + "dataType": "dateTime", + "sourceColumn": "DueDate" + }, + { + "name": "Ship Date", + "dataType": "dateTime", + "sourceColumn": "ShipDate" + }, + { + "name": "ProductKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ProductKey", + "summarizeBy": "none" + }, + { + "name": "OrderDateKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "OrderDateKey", + "summarizeBy": "none" + }, + { + "name": "DueDateKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "DueDateKey", + "summarizeBy": "none" + }, + { + "name": "ShipDateKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ShipDateKey", + "summarizeBy": "none" + }, + { + "name": "CustomerKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "CustomerKey", + "summarizeBy": "none" + }, + { + "name": "PromotionKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "PromotionKey", + "summarizeBy": "none" + }, + { + "name": "CurrencyKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "CurrencyKey", + "summarizeBy": "none" + }, + { + "name": "SalesTerritoryKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "SalesTerritoryKey", + "summarizeBy": "none" + }, + { + "name": "Tax Amount", + "dataType": "decimal", + "sourceColumn": "TaxAmt", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "type": "calculated", + "name": "Gross Profit", + "dataType": "decimal", + "isDataTypeInferred": true, + "expression": "[Sales Amount]-[Total Product Cost]", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + } + ], + "partitions": [ + { + "name": "Internet Sales", + "source": { + "query": "SELECT * FROM [dbo].[FactInternetSales]", + "dataSource": "AdventureWorksDW" + } + } + ], + "measures": [ + { + "name": "Internet Distinct Count Sales Order", + "expression": "DISTINCTCOUNT([Sales Order Number])", + "formatString": "#,0" + }, + { + "name": "Internet Total Sales", + "expression": "SUM([Sales Amount])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Total Discount Amount", + "expression": "SUM([Discount Amount])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Total Product Cost", + "expression": "SUM([Total Product Cost])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Total Freight", + "expression": "SUM([Freight])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Total Units", + "expression": "SUM([Order Quantity])", + "formatString": "#,0" + }, + { + "name": "Internet Order Lines Count", + "expression": "COUNT([Sales Order Line Number])", + "formatString": "#,0" + }, + { + "name": "Internet Previous Quarter Sales", + "expression": "CALCULATE([Internet Total Sales],PREVIOUSQUARTER('Date'[Date]))", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Current Quarter Sales", + "expression": "TOTALQTD([Internet Total Sales],'Date'[Date])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Previous Quarter Sales Proportion to QTD", + "expression": "[Internet Previous Quarter Sales]*([Days In Current Quarter]/[Days In Current Quarter])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Current Quarter Sales Performance", + "expression": "IFERROR([Internet Current Quarter Sales]/[Internet Previous Quarter Sales Proportion to QTD],BLANK())", + "kpi": { + "targetExpression": "1.1", + "statusGraphic": "Three Symbols UnCircled Colored", + "statusExpression": [ + "if(ISBLANK('Internet Sales'[Internet Current Quarter Sales Performance]),BLANK(),\r", + " If('Internet Sales'[Internet Current Quarter Sales Performance]<1,-1,\r", + "\t If('Internet Sales'[Internet Current Quarter Sales Performance]<1.07,0,1)\r", + " )\r", + " )\r", + " " + ], + "annotations": [ + { + "name": "GoalType", + "value": "StaticValue" + }, + { + "name": "KpiStatusType", + "value": "Linear" + }, + { + "name": "KpiThresholdType", + "value": "Absolute" + }, + { + "name": "KpiThresholdOrdering", + "value": "Ascending" + }, + { + "name": "KpiThresholdCount", + "value": "2" + }, + { + "name": "KpiThreshold_0", + "value": "1" + }, + { + "name": "KpiThreshold_1", + "value": "1.07" + } + ] + } + }, + { + "name": "Internet Total Tax Amount", + "expression": "SUM([Tax Amount])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Total Gross Profit", + "expression": "SUM([Gross Profit])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Previous Quarter Gross Profit", + "expression": "CALCULATE([Internet Total Gross Profit],PREVIOUSQUARTER('Date'[Date]))", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Current Quarter Gross Profit", + "expression": "TOTALQTD([Internet Total Gross Profit],'Date'[Date])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Previous Quarter Gross Profit Proportion to QTD", + "expression": "[Internet Previous Quarter Gross Profit]*([Days In Current Quarter to Date]/[Days In Current Quarter])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Internet Current Quarter Gross Profit Performance", + "expression": "IF([Internet Previous Quarter Gross Profit Proportion to QTD]<>0,([Internet Current Quarter Gross Profit]-[Internet Previous Quarter Gross Profit Proportion to QTD])/[Internet Previous Quarter Gross Profit Proportion to QTD],BLANK())", + "kpi": { + "targetExpression": "1.25", + "statusGraphic": "Three Symbols UnCircled Colored", + "statusExpression": [ + "if(ISBLANK('Internet Sales'[Internet Current Quarter Gross Profit Performance]),BLANK(),\r", + " If('Internet Sales'[Internet Current Quarter Gross Profit Performance]<0.8,-1,\r", + "\t If('Internet Sales'[Internet Current Quarter Gross Profit Performance]<1.03,0,1)\r", + " )\r", + " )\r", + " " + ], + "annotations": [ + { + "name": "GoalType", + "value": "StaticValue" + }, + { + "name": "KpiStatusType", + "value": "Linear" + }, + { + "name": "KpiThresholdType", + "value": "Absolute" + }, + { + "name": "KpiThresholdOrdering", + "value": "Ascending" + }, + { + "name": "KpiThresholdCount", + "value": "2" + }, + { + "name": "KpiThreshold_0", + "value": "0.8" + }, + { + "name": "KpiThreshold_1", + "value": "1.03" + } + ] + } + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "FactInternetSales" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[FactInternetSales].* FROM [dbo].[FactInternetSales] " + } + ] + }, + { + "name": "Reseller Sales", + "columns": [ + { + "name": "ProductKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ProductKey" + }, + { + "name": "OrderDateKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "OrderDateKey" + }, + { + "name": "DueDateKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "DueDateKey" + }, + { + "name": "ShipDateKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ShipDateKey" + }, + { + "name": "ResellerKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "ResellerKey" + }, + { + "name": "EmployeeKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "EmployeeKey" + }, + { + "name": "PromotionKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "PromotionKey" + }, + { + "name": "CurrencyKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "CurrencyKey" + }, + { + "name": "SalesTerritoryKey", + "dataType": "int64", + "isHidden": true, + "sourceColumn": "SalesTerritoryKey" + }, + { + "name": "Sales Order Number", + "dataType": "string", + "sourceColumn": "SalesOrderNumber" + }, + { + "name": "Sales Order Line Number", + "dataType": "int64", + "sourceColumn": "SalesOrderLineNumber", + "summarizeBy": "none" + }, + { + "name": "Revision Number", + "dataType": "int64", + "sourceColumn": "RevisionNumber", + "summarizeBy": "none" + }, + { + "name": "Order Quantity", + "dataType": "int64", + "sourceColumn": "OrderQuantity", + "summarizeBy": "none" + }, + { + "name": "Unit Price", + "dataType": "decimal", + "sourceColumn": "UnitPrice", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Extended Amount", + "dataType": "decimal", + "sourceColumn": "ExtendedAmount", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Unit Price Discount Pct", + "dataType": "double", + "sourceColumn": "UnitPriceDiscountPct", + "summarizeBy": "none" + }, + { + "name": "Discount Amount", + "dataType": "decimal", + "sourceColumn": "DiscountAmount", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Product Standard Cost", + "dataType": "decimal", + "sourceColumn": "ProductStandardCost", + "summarizeBy": "none", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Total Product Cost", + "dataType": "decimal", + "sourceColumn": "TotalProductCost", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Sales Amount", + "dataType": "decimal", + "sourceColumn": "SalesAmount", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Tax Amount", + "dataType": "decimal", + "sourceColumn": "TaxAmt", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Freight", + "dataType": "decimal", + "sourceColumn": "Freight", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Carrier Tracking Number", + "dataType": "string", + "sourceColumn": "CarrierTrackingNumber" + }, + { + "name": "Reseller PO Number", + "dataType": "string", + "sourceColumn": "CustomerPONumber" + }, + { + "name": "Order Date", + "dataType": "dateTime", + "sourceColumn": "OrderDate", + "formatString": "Short Date", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Due Date", + "dataType": "dateTime", + "sourceColumn": "DueDate", + "formatString": "Short Date", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Ship Date", + "dataType": "dateTime", + "sourceColumn": "ShipDate", + "formatString": "Short Date", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "type": "calculated", + "name": "Gross Profit", + "dataType": "decimal", + "isDataTypeInferred": true, + "expression": "[Sales Amount]-[Total Product Cost]", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + } + ], + "partitions": [ + { + "name": "Reseller Sales", + "source": { + "query": "SELECT * FROM [dbo].[FactResellerSales]", + "dataSource": "AdventureWorksDW" + } + } + ], + "measures": [ + { + "name": "Reseller Distinct Count Sales Order", + "expression": "DISTINCTCOUNT([Sales Order Number])", + "formatString": "#,0" + }, + { + "name": "Reseller Total Sales", + "expression": "SUM([Sales Amount])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Total Discount Amount", + "expression": "SUM([Discount Amount])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Total Product Cost", + "expression": "SUM([Total Product Cost])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Total Tax Amount", + "expression": "SUM([Tax Amount])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Total Freight", + "expression": "SUM([Freight])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Total Gross Profit", + "expression": "SUM([Gross Profit])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Total Units", + "expression": "SUM([Order Quantity])", + "formatString": "#,0" + }, + { + "name": "Reseller Order Lines Count", + "expression": "COUNT([Sales Order Line Number])", + "formatString": "#,0" + }, + { + "name": "Reseller Previous Quarter Gross Profit", + "expression": "CALCULATE([Reseller Total Gross Profit],PREVIOUSQUARTER('Date'[Date]))", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Current Quarter Gross Profit", + "expression": "TOTALQTD([Reseller Total Gross Profit],'Date'[Date])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Previous Quarter Gross Profit Proportion to QTD", + "expression": "[Reseller Previous Quarter Gross Profit]*([Days In Current Quarter to Date]/[Days In Current Quarter])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Previous Quarter Sales", + "expression": "CALCULATE([Reseller Total Sales],PREVIOUSQUARTER('Date'[Date]))", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Current Quarter Sales", + "expression": "TOTALQTD([Reseller Total Sales],'Date'[Date])", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Previous Quarter Sales Proportion to QTD", + "expression": "[Reseller Previous Quarter Sales]*([Days In Current Quarter to Date]/[Days In Current Quarter])" + }, + { + "name": "Reseller Total Sales - Sales Territory sliced by Employee", + "expression": "CALCULATE(SUM('Reseller Sales'[Sales Amount]),USERELATIONSHIP(Employee[EmployeeKey],'Reseller Sales'[EmployeeKey]),USERELATIONSHIP('Sales Territory'[SalesTerritoryKey],Employee[SalesTerritoryKey]))", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Total Sales - Sales Territory sliced by Reseller", + "expression": "CALCULATE(SUM('Reseller Sales'[Sales Amount]), USERELATIONSHIP(Reseller[ResellerKey], 'Reseller Sales'[ResellerKey]), USERELATIONSHIP(Geography[GeographyKey], Reseller[GeographyKey]), USERELATIONSHIP('Sales Territory'[SalesTerritoryKey], Geography[SalesTerritoryKey]))", + "formatString": "\\$#,0.00;(\\$#,0.00);\\$#,0.00", + "annotations": [ + { + "name": "Format", + "value": "" + } + ] + }, + { + "name": "Reseller Current Quarter Sales Performance", + "expression": "IFERROR([Reseller Current Quarter Sales]/[Reseller Previous Quarter Sales Proportion to QTD],BLANK())", + "kpi": { + "targetExpression": "1.1", + "statusGraphic": "Three Symbols UnCircled Colored", + "statusExpression": [ + "if(ISBLANK('Reseller Sales'[Reseller Current Quarter Sales Performance]),BLANK(),\r", + " If('Reseller Sales'[Reseller Current Quarter Sales Performance]<0.8,-1,\r", + "\t If('Reseller Sales'[Reseller Current Quarter Sales Performance]<1.07,0,1)\r", + " )\r", + " )\r", + " " + ], + "annotations": [ + { + "name": "GoalType", + "value": "StaticValue" + }, + { + "name": "KpiStatusType", + "value": "Linear" + }, + { + "name": "KpiThresholdType", + "value": "Absolute" + }, + { + "name": "KpiThresholdOrdering", + "value": "Ascending" + }, + { + "name": "KpiThresholdCount", + "value": "2" + }, + { + "name": "KpiThreshold_0", + "value": "0.8" + }, + { + "name": "KpiThreshold_1", + "value": "1.07" + } + ] + } + }, + { + "name": "Reseller Current Quarter Gross Profit Performance", + "expression": "IF([Reseller Previous Quarter Gross Profit Proportion to QTD]<>0,([Reseller Current Quarter Gross Profit]-[Reseller Previous Quarter Gross Profit Proportion to QTD])/[Reseller Previous Quarter Gross Profit Proportion to QTD],BLANK())", + "kpi": { + "targetExpression": "1.25", + "statusGraphic": "Three Symbols UnCircled Colored", + "statusExpression": [ + "if(ISBLANK('Reseller Sales'[Reseller Current Quarter Gross Profit Performance]),BLANK(),\r", + " If('Reseller Sales'[Reseller Current Quarter Gross Profit Performance]<0.8,-1,\r", + "\t If('Reseller Sales'[Reseller Current Quarter Gross Profit Performance]<1.03,0,1)\r", + " )\r", + " )\r", + " " + ], + "annotations": [ + { + "name": "GoalType", + "value": "StaticValue" + }, + { + "name": "KpiStatusType", + "value": "Linear" + }, + { + "name": "KpiThresholdType", + "value": "Absolute" + }, + { + "name": "KpiThresholdOrdering", + "value": "Ascending" + }, + { + "name": "KpiThresholdCount", + "value": "2" + }, + { + "name": "KpiThreshold_0", + "value": "0.8" + }, + { + "name": "KpiThreshold_1", + "value": "1.03" + } + ] + } + } + ], + "annotations": [ + { + "name": "_TM_ExtProp_DbSchemaName", + "value": "dbo" + }, + { + "name": "_TM_ExtProp_DbTableName", + "value": "FactResellerSales" + }, + { + "name": "_TM_ExtProp_QueryDefinition", + "value": " SELECT [dbo].[FactResellerSales].* FROM [dbo].[FactResellerSales] " + } + ] + } + ], + "relationships": [ + { + "name": "2c5177ff-b673-48e0-9168-821672c57e5e", + "fromTable": "Customer", + "fromColumn": "GeographyKey", + "toTable": "Geography", + "toColumn": "GeographyKey" + }, + { + "name": "e278ab89-4b93-42e3-b40c-636fdd8d681c", + "fromTable": "Employee", + "fromColumn": "SalesTerritoryKey", + "toTable": "Sales Territory", + "toColumn": "SalesTerritoryKey", + "isActive": false + }, + { + "name": "27dae602-f0b8-43f8-940b-43fb315458bb", + "fromTable": "Geography", + "fromColumn": "SalesTerritoryKey", + "toTable": "Sales Territory", + "toColumn": "SalesTerritoryKey", + "isActive": false + }, + { + "name": "9b8024a2-e51d-4a2b-9a8f-6a2199d1d2ea", + "fromTable": "Product", + "fromColumn": "ProductSubcategoryKey", + "toTable": "Product Subcategory", + "toColumn": "ProductSubcategoryKey" + }, + { + "name": "46425cb9-382b-4dfb-9b79-63bc5082c42b", + "fromTable": "Product Subcategory", + "fromColumn": "ProductCategoryKey", + "toTable": "Product Category", + "toColumn": "ProductCategoryKey" + }, + { + "name": "b32d6ef8-a309-4c10-8d36-0f0e2ae84901", + "fromTable": "Reseller", + "fromColumn": "GeographyKey", + "toTable": "Geography", + "toColumn": "GeographyKey" + }, + { + "name": "602b2378-4717-4122-b97e-95f36c15dc85", + "fromTable": "Product Inventory", + "fromColumn": "DateKey", + "toTable": "Date", + "toColumn": "DateKey" + }, + { + "name": "7dbfc7d9-a935-432d-b5c6-a53d129fbd16", + "fromTable": "Product Inventory", + "fromColumn": "ProductKey", + "toTable": "Product", + "toColumn": "ProductKey" + }, + { + "name": "39981e17-197f-4647-a8d8-850cbe2aea09", + "fromTable": "Sales Quota", + "fromColumn": "DateKey", + "toTable": "Date", + "toColumn": "DateKey" + }, + { + "name": "d96cf292-44da-4a47-837c-173bf1f49c18", + "fromTable": "Sales Quota", + "fromColumn": "EmployeeKey", + "toTable": "Employee", + "toColumn": "EmployeeKey" + }, + { + "name": "877c4e4a-6f61-43c8-ae8b-b748a6400407", + "fromTable": "Internet Sales", + "fromColumn": "CurrencyKey", + "toTable": "Currency", + "toColumn": "CurrencyKey" + }, + { + "name": "90d94b82-0243-4632-b9f7-99de63279961", + "fromTable": "Internet Sales", + "fromColumn": "CustomerKey", + "toTable": "Customer", + "toColumn": "CustomerKey" + }, + { + "name": "3db0e485-88a9-44d9-9a12-657c8ef0f881", + "fromTable": "Internet Sales", + "fromColumn": "OrderDateKey", + "toTable": "Date", + "toColumn": "DateKey" + }, + { + "name": "f5617b1d-9840-4978-8860-f337cb5f852d", + "fromTable": "Internet Sales", + "fromColumn": "DueDateKey", + "toTable": "Date", + "toColumn": "DateKey", + "isActive": false + }, + { + "name": "8cd93710-86b7-47a6-b512-230bc4c3e73d", + "fromTable": "Internet Sales", + "fromColumn": "ShipDateKey", + "toTable": "Date", + "toColumn": "DateKey", + "isActive": false + }, + { + "name": "a5016faf-9af5-4750-92ff-505d05aefe68", + "fromTable": "Internet Sales", + "fromColumn": "ProductKey", + "toTable": "Product", + "toColumn": "ProductKey" + }, + { + "name": "b3d9edf1-1e10-436b-b870-80029b0ee4f3", + "fromTable": "Internet Sales", + "fromColumn": "PromotionKey", + "toTable": "Promotion", + "toColumn": "PromotionKey" + }, + { + "name": "d9761f6e-1fec-476d-9eee-9e1511044619", + "fromTable": "Internet Sales", + "fromColumn": "SalesTerritoryKey", + "toTable": "Sales Territory", + "toColumn": "SalesTerritoryKey" + }, + { + "name": "1b277ca4-d6fc-4366-b164-2fd3c56cfd24", + "fromTable": "Reseller Sales", + "fromColumn": "CurrencyKey", + "toTable": "Currency", + "toColumn": "CurrencyKey" + }, + { + "name": "100ca454-655f-4e46-a040-cfa2ca981f88", + "fromTable": "Reseller Sales", + "fromColumn": "OrderDateKey", + "toTable": "Date", + "toColumn": "DateKey" + }, + { + "name": "1f6426cb-30d0-4d70-bbd5-5da59ce7292b", + "fromTable": "Reseller Sales", + "fromColumn": "DueDateKey", + "toTable": "Date", + "toColumn": "DateKey", + "isActive": false + }, + { + "name": "1c37fcb7-a454-4b11-8389-9a97d5d09f4c", + "fromTable": "Reseller Sales", + "fromColumn": "ShipDateKey", + "toTable": "Date", + "toColumn": "DateKey", + "isActive": false + }, + { + "name": "262def50-5047-491a-8fc6-e96a9d66a80b", + "fromTable": "Reseller Sales", + "fromColumn": "EmployeeKey", + "toTable": "Employee", + "toColumn": "EmployeeKey" + }, + { + "name": "adbcdd01-4156-4b47-9c94-5f6283c9c20a", + "fromTable": "Reseller Sales", + "fromColumn": "ProductKey", + "toTable": "Product", + "toColumn": "ProductKey" + }, + { + "name": "03b67804-b82a-433a-9589-f768a6eb3945", + "fromTable": "Reseller Sales", + "fromColumn": "PromotionKey", + "toTable": "Promotion", + "toColumn": "PromotionKey" + }, + { + "name": "431b1361-7b09-4540-91d3-fda884108451", + "fromTable": "Reseller Sales", + "fromColumn": "ResellerKey", + "toTable": "Reseller", + "toColumn": "ResellerKey" + }, + { + "name": "b62a7abc-f954-41a1-ac0a-bce7fdd5eaa7", + "fromTable": "Reseller Sales", + "fromColumn": "SalesTerritoryKey", + "toTable": "Sales Territory", + "toColumn": "SalesTerritoryKey" + } + ], + "perspectives": [ + { + "name": "Inventory", + "tables": [ + { + "name": "Product Inventory", + "columns": [ + { + "name": "Units In" + }, + { + "name": "Product-Date OverStocked" + }, + { + "name": "Units Balance" + }, + { + "name": "Product-Date Negative Stock" + }, + { + "name": "Unit Cost" + }, + { + "name": "DateKey" + }, + { + "name": "Product-Date Inventory Value" + }, + { + "name": "Movement Date" + }, + { + "name": "Product-Date UnderStocked" + }, + { + "name": "Units Out" + }, + { + "name": "ProductKey" + }, + { + "name": "Product-Date Optimal Inventory Value" + }, + { + "name": "Product-Date Max Inventory Value" + } + ], + "measures": [ + { + "name": "Total Inventory Value Performance" + }, + { + "name": "Total Units Out" + }, + { + "name": "Total Units" + }, + { + "name": "Total Units Movement" + }, + { + "name": "Products with Negative Stock" + }, + { + "name": "Products UnderStocked" + }, + { + "name": "Total Inventory Value" + }, + { + "name": "Total Units In" + }, + { + "name": "Total Inventory Maximum Value" + }, + { + "name": "Total Inventory Optimal Value" + }, + { + "name": "Products OverStocked" + } + ] + }, + { + "name": "Date", + "columns": [ + { + "name": "Month Name" + }, + { + "name": "Calendar Quarter" + }, + { + "name": "Month" + }, + { + "name": "Week Of Year" + }, + { + "name": "Calendar Year" + }, + { + "name": "Fiscal Quarter" + }, + { + "name": "Day Of Month" + }, + { + "name": "Fiscal Year" + }, + { + "name": "Day Name Of Week" + }, + { + "name": "Day Of Year" + }, + { + "name": "DateKey" + }, + { + "name": "Date" + }, + { + "name": "Fiscal Semester" + }, + { + "name": "Day Number Of Week" + }, + { + "name": "Calendar Semester" + } + ], + "measures": [ + { + "name": "Days In Current Quarter to Date" + }, + { + "name": "Days In Current Quarter" + } + ] + }, + { + "name": "Product", + "columns": [ + { + "name": "Product SubCategory Name" + }, + { + "name": "Style" + }, + { + "name": "Model Name" + }, + { + "name": "Product Name" + }, + { + "name": "Product Start Date" + }, + { + "name": "Weight Unit Code" + }, + { + "name": "Safety Stock Level" + }, + { + "name": "Product Id" + }, + { + "name": "Standard Cost" + }, + { + "name": "Color" + }, + { + "name": "Days To Manufacture" + }, + { + "name": "Dealer Price" + }, + { + "name": "ProductKey" + }, + { + "name": "LargePhoto" + }, + { + "name": "Size" + }, + { + "name": "Is Finished Goods" + }, + { + "name": "Description" + }, + { + "name": "Product Line" + }, + { + "name": "Size Unit Code" + }, + { + "name": "List Price" + }, + { + "name": "Class" + }, + { + "name": "Size Range" + }, + { + "name": "Reorder Point" + }, + { + "name": "Product Status" + }, + { + "name": "Weight" + }, + { + "name": "ProductSubcategoryKey" + }, + { + "name": "Product Category Name" + }, + { + "name": "Product End Date" + } + ] + }, + { + "name": "Product Subcategory", + "columns": [ + { + "name": "Product Subcategory Name" + }, + { + "name": "ProductSubcategoryKey" + }, + { + "name": "ProductCategoryKey" + } + ] + }, + { + "name": "Product Category", + "columns": [ + { + "name": "Product Category Name" + }, + { + "name": "ProductCategoryKey" + } + ] + } + ] + }, + { + "name": "Internet Operation", + "tables": [ + { + "name": "Date", + "columns": [ + { + "name": "Month Name" + }, + { + "name": "Calendar Quarter" + }, + { + "name": "Month" + }, + { + "name": "Week Of Year" + }, + { + "name": "Calendar Year" + }, + { + "name": "Fiscal Quarter" + }, + { + "name": "Day Of Month" + }, + { + "name": "Fiscal Year" + }, + { + "name": "Day Name Of Week" + }, + { + "name": "Day Of Year" + }, + { + "name": "DateKey" + }, + { + "name": "Date" + }, + { + "name": "Fiscal Semester" + }, + { + "name": "Day Number Of Week" + }, + { + "name": "Calendar Semester" + } + ], + "measures": [ + { + "name": "Days In Current Quarter to Date" + }, + { + "name": "Days In Current Quarter" + } + ] + }, + { + "name": "Internet Sales", + "columns": [ + { + "name": "Carrier Tracking Number" + }, + { + "name": "Order Date" + }, + { + "name": "Sales Order Line Number" + }, + { + "name": "CustomerKey" + }, + { + "name": "Gross Profit" + }, + { + "name": "Due Date" + }, + { + "name": "Sales Order Number" + }, + { + "name": "Discount Amount" + }, + { + "name": "Sales Amount" + }, + { + "name": "PromotionKey" + }, + { + "name": "Customer PO Number" + }, + { + "name": "ProductKey" + }, + { + "name": "Unit Price" + }, + { + "name": "Freight" + }, + { + "name": "Total Product Cost" + }, + { + "name": "CurrencyKey" + }, + { + "name": "ShipDateKey" + }, + { + "name": "Product Standard Cost" + }, + { + "name": "Extended Amount" + }, + { + "name": "Order Quantity" + }, + { + "name": "Revision Number" + }, + { + "name": "SalesTerritoryKey" + }, + { + "name": "Tax Amount" + }, + { + "name": "OrderDateKey" + }, + { + "name": "Unit Price Discount Pct" + }, + { + "name": "Ship Date" + }, + { + "name": "DueDateKey" + } + ], + "measures": [ + { + "name": "Internet Total Gross Profit" + }, + { + "name": "Internet Previous Quarter Sales Proportion to QTD" + }, + { + "name": "Internet Current Quarter Gross Profit Performance" + }, + { + "name": "Internet Current Quarter Sales" + }, + { + "name": "Internet Current Quarter Sales Performance" + }, + { + "name": "Internet Total Product Cost" + }, + { + "name": "Internet Distinct Count Sales Order" + }, + { + "name": "Internet Previous Quarter Gross Profit" + }, + { + "name": "Internet Total Sales" + }, + { + "name": "Internet Previous Quarter Gross Profit Proportion to QTD" + }, + { + "name": "Internet Order Lines Count" + }, + { + "name": "Internet Previous Quarter Sales" + }, + { + "name": "Internet Total Freight" + }, + { + "name": "Internet Total Units" + }, + { + "name": "Internet Total Tax Amount" + }, + { + "name": "Internet Current Quarter Gross Profit" + }, + { + "name": "Internet Total Discount Amount" + } + ] + }, + { + "name": "Sales Territory", + "columns": [ + { + "name": "Sales Territory Region" + }, + { + "name": "SalesTerritoryKey" + }, + { + "name": "Sales Territory Country" + }, + { + "name": "Sales Territory Group" + }, + { + "name": "Sales Territory Image" + } + ], + "measures": [ + { + "name": "Total Gross Profit" + }, + { + "name": "Distinct Count Sales Orders" + }, + { + "name": "Total Sales - Sales Territory sliced by Employee" + }, + { + "name": "Total Current Quarter Sales" + }, + { + "name": "Total Previous Quarter Sales" + }, + { + "name": "Total Tax Amount" + }, + { + "name": "Total Current Quarter Sales Performance" + }, + { + "name": "Total Freight" + }, + { + "name": "Total Sales" + }, + { + "name": "Total Previous Quarter Gross Profit Proportion to QTD" + }, + { + "name": "Order Lines Count" + }, + { + "name": "Total Discount Amount" + }, + { + "name": "Total Previous Quarter Sales Proportion to QTD" + }, + { + "name": "Total Products Cost" + }, + { + "name": "Total Units Sold" + }, + { + "name": "Total Current Quarter Gross Profit Performance" + }, + { + "name": "Total Current Quarter Gross Profit" + }, + { + "name": "Total Previous Quarter Gross Profit" + } + ] + }, + { + "name": "Product", + "columns": [ + { + "name": "Product SubCategory Name" + }, + { + "name": "Style" + }, + { + "name": "Model Name" + }, + { + "name": "Product Name" + }, + { + "name": "Product Start Date" + }, + { + "name": "Weight Unit Code" + }, + { + "name": "Safety Stock Level" + }, + { + "name": "Product Id" + }, + { + "name": "Standard Cost" + }, + { + "name": "Color" + }, + { + "name": "Days To Manufacture" + }, + { + "name": "Dealer Price" + }, + { + "name": "ProductKey" + }, + { + "name": "LargePhoto" + }, + { + "name": "Size" + }, + { + "name": "Is Finished Goods" + }, + { + "name": "Description" + }, + { + "name": "Product Line" + }, + { + "name": "Size Unit Code" + }, + { + "name": "List Price" + }, + { + "name": "Class" + }, + { + "name": "Size Range" + }, + { + "name": "Reorder Point" + }, + { + "name": "Product Status" + }, + { + "name": "Weight" + }, + { + "name": "ProductSubcategoryKey" + }, + { + "name": "Product Category Name" + }, + { + "name": "Product End Date" + } + ] + }, + { + "name": "Promotion", + "columns": [ + { + "name": "Promotion Name" + }, + { + "name": "Max Quantity" + }, + { + "name": "Promotion Type" + }, + { + "name": "Promotion Start Date" + }, + { + "name": "Min Quantity" + }, + { + "name": "Promotion Category" + }, + { + "name": "DiscountPct" + }, + { + "name": "PromotionKey" + }, + { + "name": "Promotion End Date" + } + ] + }, + { + "name": "Product Subcategory", + "columns": [ + { + "name": "Product Subcategory Name" + }, + { + "name": "ProductSubcategoryKey" + }, + { + "name": "ProductCategoryKey" + } + ] + }, + { + "name": "Geography", + "columns": [ + { + "name": "City" + }, + { + "name": "State Province Name" + }, + { + "name": "Country Region Code" + }, + { + "name": "State Province Code" + }, + { + "name": "SalesTerritoryKey" + }, + { + "name": "GeographyKey" + }, + { + "name": "Country Region Name" + }, + { + "name": "Postal Code" + } + ] + }, + { + "name": "Customer", + "columns": [ + { + "name": "CustomerKey" + }, + { + "name": "Number of Children At Home" + }, + { + "name": "Middle Name" + }, + { + "name": "Name Style" + }, + { + "name": "Date Of First Purchase" + }, + { + "name": "Address Line 2" + }, + { + "name": "GeographyKey" + }, + { + "name": "Yearly Income" + }, + { + "name": "Marital Status" + }, + { + "name": "Phone" + }, + { + "name": "Email Address" + }, + { + "name": "Gender" + }, + { + "name": "Occupation" + }, + { + "name": "Address Line 1" + }, + { + "name": "Total Cars Owned" + }, + { + "name": "Total Children" + }, + { + "name": "Suffix" + }, + { + "name": "First Name" + }, + { + "name": "Birth Date" + }, + { + "name": "Education" + }, + { + "name": "Last Name" + }, + { + "name": "Commute Distance" + }, + { + "name": "Customer Id" + }, + { + "name": "Title" + }, + { + "name": "Owns House" + } + ] + }, + { + "name": "Currency", + "columns": [ + { + "name": "CurrencyKey" + }, + { + "name": "Currency Code" + }, + { + "name": "CurrencyName" + } + ] + }, + { + "name": "Product Category", + "columns": [ + { + "name": "Product Category Name" + }, + { + "name": "ProductCategoryKey" + } + ] + } + ] + }, + { + "name": "Reseller Operation", + "tables": [ + { + "name": "Date", + "columns": [ + { + "name": "Month Name" + }, + { + "name": "Calendar Quarter" + }, + { + "name": "Month" + }, + { + "name": "Week Of Year" + }, + { + "name": "Calendar Year" + }, + { + "name": "Fiscal Quarter" + }, + { + "name": "Day Of Month" + }, + { + "name": "Fiscal Year" + }, + { + "name": "Day Name Of Week" + }, + { + "name": "Day Of Year" + }, + { + "name": "DateKey" + }, + { + "name": "Date" + }, + { + "name": "Fiscal Semester" + }, + { + "name": "Day Number Of Week" + }, + { + "name": "Calendar Semester" + } + ], + "measures": [ + { + "name": "Days In Current Quarter to Date" + }, + { + "name": "Days In Current Quarter" + } + ] + }, + { + "name": "Reseller Sales", + "columns": [ + { + "name": "Sales Order Line Number" + }, + { + "name": "Carrier Tracking Number" + }, + { + "name": "Order Date" + }, + { + "name": "Due Date" + }, + { + "name": "Gross Profit" + }, + { + "name": "Sales Order Number" + }, + { + "name": "Discount Amount" + }, + { + "name": "Sales Amount" + }, + { + "name": "PromotionKey" + }, + { + "name": "ProductKey" + }, + { + "name": "Reseller PO Number" + }, + { + "name": "Unit Price" + }, + { + "name": "Freight" + }, + { + "name": "ResellerKey" + }, + { + "name": "DueDateKey" + }, + { + "name": "EmployeeKey" + }, + { + "name": "Total Product Cost" + }, + { + "name": "CurrencyKey" + }, + { + "name": "ShipDateKey" + }, + { + "name": "Product Standard Cost" + }, + { + "name": "Extended Amount" + }, + { + "name": "Order Quantity" + }, + { + "name": "Revision Number" + }, + { + "name": "SalesTerritoryKey" + }, + { + "name": "Tax Amount" + }, + { + "name": "OrderDateKey" + }, + { + "name": "Unit Price Discount Pct" + }, + { + "name": "Ship Date" + } + ], + "measures": [ + { + "name": "Reseller Total Tax Amount" + }, + { + "name": "Reseller Distinct Count Sales Order" + }, + { + "name": "Reseller Previous Quarter Gross Profit Proportion to QTD" + }, + { + "name": "Reseller Total Discount Amount" + }, + { + "name": "Reseller Current Quarter Sales" + }, + { + "name": "Reseller Total Gross Profit" + }, + { + "name": "Reseller Order Lines Count" + }, + { + "name": "Reseller Previous Quarter Sales Proportion to QTD" + }, + { + "name": "Reseller Total Product Cost" + }, + { + "name": "Reseller Previous Quarter Sales" + }, + { + "name": "Reseller Total Sales" + }, + { + "name": "Reseller Current Quarter Gross Profit Performance" + }, + { + "name": "Reseller Current Quarter Sales Performance" + }, + { + "name": "Reseller Total Units" + }, + { + "name": "Reseller Total Freight" + }, + { + "name": "Reseller Total Sales - Sales Territory sliced by Reseller" + }, + { + "name": "Reseller Total Sales - Sales Territory sliced by Employee" + }, + { + "name": "Reseller Current Quarter Gross Profit" + }, + { + "name": "Reseller Previous Quarter Gross Profit" + } + ] + }, + { + "name": "Sales Territory", + "columns": [ + { + "name": "Sales Territory Region" + }, + { + "name": "SalesTerritoryKey" + }, + { + "name": "Sales Territory Country" + }, + { + "name": "Sales Territory Group" + }, + { + "name": "Sales Territory Image" + } + ], + "measures": [ + { + "name": "Total Gross Profit" + }, + { + "name": "Distinct Count Sales Orders" + }, + { + "name": "Total Previous Quarter Sales" + }, + { + "name": "Total Previous Quarter Gross Profit Proportion to QTD" + }, + { + "name": "Order Lines Count" + }, + { + "name": "Total Discount Amount" + }, + { + "name": "Total Previous Quarter Sales Proportion to QTD" + }, + { + "name": "Total Products Cost" + }, + { + "name": "Total Units Sold" + }, + { + "name": "Total Current Quarter Gross Profit Performance" + }, + { + "name": "Total Current Quarter Gross Profit" + }, + { + "name": "Total Previous Quarter Gross Profit" + }, + { + "name": "Total Sales - Sales Territory sliced by Employee" + }, + { + "name": "Total Current Quarter Sales" + }, + { + "name": "Total Tax Amount" + }, + { + "name": "Total Current Quarter Sales Performance" + }, + { + "name": "Total Freight" + }, + { + "name": "Total Sales" + } + ] + }, + { + "name": "Product", + "columns": [ + { + "name": "Product SubCategory Name" + }, + { + "name": "Style" + }, + { + "name": "Model Name" + }, + { + "name": "Product Name" + }, + { + "name": "Product Start Date" + }, + { + "name": "Weight Unit Code" + }, + { + "name": "Safety Stock Level" + }, + { + "name": "Product Id" + }, + { + "name": "Standard Cost" + }, + { + "name": "Color" + }, + { + "name": "Days To Manufacture" + }, + { + "name": "Dealer Price" + }, + { + "name": "ProductKey" + }, + { + "name": "LargePhoto" + }, + { + "name": "Size" + }, + { + "name": "Is Finished Goods" + }, + { + "name": "Description" + }, + { + "name": "Product Line" + }, + { + "name": "Size Unit Code" + }, + { + "name": "List Price" + }, + { + "name": "Class" + }, + { + "name": "Size Range" + }, + { + "name": "Reorder Point" + }, + { + "name": "Product Status" + }, + { + "name": "Weight" + }, + { + "name": "ProductSubcategoryKey" + }, + { + "name": "Product Category Name" + }, + { + "name": "Product End Date" + } + ] + }, + { + "name": "Promotion", + "columns": [ + { + "name": "Promotion Name" + }, + { + "name": "Max Quantity" + }, + { + "name": "Promotion Type" + }, + { + "name": "Promotion Start Date" + }, + { + "name": "Min Quantity" + }, + { + "name": "Promotion Category" + }, + { + "name": "DiscountPct" + }, + { + "name": "PromotionKey" + }, + { + "name": "Promotion End Date" + } + ] + }, + { + "name": "Product Subcategory", + "columns": [ + { + "name": "Product Subcategory Name" + }, + { + "name": "ProductSubcategoryKey" + }, + { + "name": "ProductCategoryKey" + } + ] + }, + { + "name": "Employee", + "columns": [ + { + "name": "ParentEmployeeKey" + }, + { + "name": "Middle Name" + }, + { + "name": "NameStyle" + }, + { + "name": "Hire Date" + }, + { + "name": "Marital Status" + }, + { + "name": "Pay Frequency" + }, + { + "name": "Phone" + }, + { + "name": "Email" + }, + { + "name": "Gender" + }, + { + "name": "Vacation Hours" + }, + { + "name": "Employee Id" + }, + { + "name": "Is Current" + }, + { + "name": "Login" + }, + { + "name": "First Name" + }, + { + "name": "Is Salaried" + }, + { + "name": "Base Rate" + }, + { + "name": "Birth Date" + }, + { + "name": "Sick Leave Hours" + }, + { + "name": "Last Name" + }, + { + "name": "SalesTerritoryKey" + }, + { + "name": "Status" + }, + { + "name": "Emergency Contact Name" + }, + { + "name": "Title" + }, + { + "name": "Department Name" + }, + { + "name": "Emergency Contact Phone" + }, + { + "name": "EmployeeKey" + }, + { + "name": "End Date" + }, + { + "name": "Is Sales Person" + }, + { + "name": "Employee Photo" + }, + { + "name": "Start Date" + } + ] + }, + { + "name": "Geography", + "columns": [ + { + "name": "City" + }, + { + "name": "State Province Name" + }, + { + "name": "Country Region Code" + }, + { + "name": "State Province Code" + }, + { + "name": "SalesTerritoryKey" + }, + { + "name": "GeographyKey" + }, + { + "name": "Country Region Name" + }, + { + "name": "Postal Code" + } + ] + }, + { + "name": "Reseller", + "columns": [ + { + "name": "Year Opened" + }, + { + "name": "Order Month" + }, + { + "name": "Business Type" + }, + { + "name": "Annual Sales" + }, + { + "name": "Number Employees" + }, + { + "name": "Reseller Id" + }, + { + "name": "Address Line 2" + }, + { + "name": "GeographyKey" + }, + { + "name": "Reseller Phone" + }, + { + "name": "ResellerKey" + }, + { + "name": "Min Payment Type" + }, + { + "name": "Address Line 1" + }, + { + "name": "Bank Name" + }, + { + "name": "Annua Revenue" + }, + { + "name": "Reseller Name" + }, + { + "name": "Min Payment Amount" + }, + { + "name": "Product Line" + }, + { + "name": "Last Order Year" + }, + { + "name": "First Order Year" + }, + { + "name": "Order Frequency" + } + ] + }, + { + "name": "Sales Quota", + "columns": [ + { + "name": "Calendar Quarter" + }, + { + "name": "Sales Amount Quota" + }, + { + "name": "Calendar Year" + }, + { + "name": "Date" + }, + { + "name": "DateKey" + }, + { + "name": "EmployeeKey" + }, + { + "name": "SalesQuotaKey" + } + ] + }, + { + "name": "Currency", + "columns": [ + { + "name": "CurrencyKey" + }, + { + "name": "Currency Code" + }, + { + "name": "CurrencyName" + } + ] + }, + { + "name": "Product Category", + "columns": [ + { + "name": "Product Category Name" + }, + { + "name": "ProductCategoryKey" + } + ] + } + ] + } + ], + "roles": [ + { + "name": "Users", + "description": "All allowed users to query the model", + "modelPermission": "read" + }, + { + "name": "Admins", + "description": "All administrators", + "modelPermission": "administrator" + }, + { + "name": "Analysts", + "description": "All users responsible for the status of the model", + "modelPermission": "readRefresh" + }, + { + "name": "Operators", + "description": "All users responsible for updating the data", + "modelPermission": "refresh" + } + ] + } +} \ No newline at end of file diff --git a/AsPartitionProcessing/AsPartitionProcessing.SampleClient/App.config b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/App.config new file mode 100644 index 0000000..624d5ce --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/App.config @@ -0,0 +1,24 @@ + + + + +
+ + + + + + + + + localhost + + + AsPartitionProcessing + + + True + + + + diff --git a/AsPartitionProcessing/AsPartitionProcessing.SampleClient/AsPartitionProcessing.SampleClient.csproj b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/AsPartitionProcessing.SampleClient.csproj new file mode 100644 index 0000000..62ae3f1 --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/AsPartitionProcessing.SampleClient.csproj @@ -0,0 +1,88 @@ + + + + + Debug + AnyCPU + {C45B329D-F606-4F89-A41A-785C247F24B2} + Exe + Properties + AsPartitionProcessing.SampleClient + AsPartitionProcessing.SampleClient + v4.5.2 + 512 + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\Microsoft.AnalysisServices.DLL + + + False + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\Microsoft.AnalysisServices.Core.DLL + + + False + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\Microsoft.AnalysisServices.Tabular.DLL + + + + + + + + + + + + + + + True + True + Settings.settings + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + {fb937281-b06d-47fb-9846-e97b0287afce} + AsPartitionProcessing + + + + + \ No newline at end of file diff --git a/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Program.cs b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Program.cs new file mode 100644 index 0000000..c502994 --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Program.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using Microsoft.AnalysisServices.Tabular; + + +namespace AsPartitionProcessing.SampleClient +{ + class Program + { + const bool UseDatabase = false; + + static void Main(string[] args) + { + try + { + List modelsConfig; + if (!UseDatabase) + { + modelsConfig = InitializeAdventureWorksInline(); + } + else + { + modelsConfig = InitializeFromDatabase(); + } + + foreach (PartitionedModelConfig modelConfig in modelsConfig) + { + if (!modelConfig.IntegratedAuth) //For Azure AS + { + Console.WriteLine(); + Console.Write("User name for AS server: "); + modelConfig.UserName = Console.ReadLine(); + Console.Write("Password for AS server: "); + modelConfig.Password = ReadPassword(); + } + + //Most important method: + PartitionProcessor.PerformProcessing(modelConfig, LogMessage); + } + } + catch (Exception exc) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(); + Console.WriteLine(exc.Message, null); + } + + Console.WriteLine("Press any key to exit."); + Console.ReadKey(); + } + + private static List InitializeAdventureWorksInline() + { + PartitionedModelConfig partitionedModel = new PartitionedModelConfig( + partitionedModelConfigID: 1, + analysisServicesServer: "localhost", + analysisServicesDatabase: "AdventureWorks", + initialSetUp: true, + incrementalOnline: true, + incrementalParallelTables: true, + integratedAuth: true, + userName: "", + password: "", + partitionedTables: + new List + { + new PartitionedTableConfig( + partitionedTableConfigID: 1, + maxDate: Convert.ToDateTime("2012-12-01"), + granularity: Granularity.Monthly, + numberOfPartitionsFull: 12, + numberOfPartitionsForIncrementalProcess: 3, + analysisServicesTable: "Internet Sales", + sourceTableName: "[dbo].[FactInternetSales]", + sourcePartitionColumn: "OrderDateKey" + ), + new PartitionedTableConfig( + partitionedTableConfigID: 2, + maxDate: Convert.ToDateTime("2012-12-01"), + granularity: Granularity.Yearly, + numberOfPartitionsFull: 3, + numberOfPartitionsForIncrementalProcess: 1, + analysisServicesTable: "Reseller Sales", + sourceTableName: "[dbo].[FactResellerSales]", + sourcePartitionColumn: "OrderDateKey" + ) + } + ); + + #region Not needed as sample includes pre-prepared version of AdventureWorks + + ////This section not to be used normally - just to get started with AdventureWorks. It removes existing partitions that come in AdventureWorks and creates a template one to align with assumptions listed at top of PartitionProcessor.cs file. + //if (partitionedModel.InitialSetUp) + //{ + // Console.WriteLine("Initialize AdventureWorks template partitions? [y/n]"); + // if (Console.ReadLine().ToLower() == "y") + // InitializeAdventureWorksDatabase(partitionedModel); + //} + + #endregion + + return new List { partitionedModel }; + } + + private static List InitializeFromDatabase() + { + ConfigDatabaseConnectionInfo connectionInfo = new ConfigDatabaseConnectionInfo(); + + connectionInfo.Server = Settings.Default.ConfigServer; + connectionInfo.Database = Settings.Default.ConfigDatabase; + connectionInfo.IntegratedAuth = Settings.Default.ConfigDatabaseIntegratedAuth; + + if (!Settings.Default.ConfigDatabaseIntegratedAuth) + { + Console.Write("User name for config database: "); + connectionInfo.UserName = Console.ReadLine(); + Console.Write("Password for config database: "); + connectionInfo.Password = ReadPassword(); + } + + return ConfigDatabaseHelper.ReadConfig(connectionInfo); + } + + private static void LogMessage(string message, PartitionedModelConfig partitionedModel) + { + //Can provide custom logger here + try + { + if (UseDatabase) + ConfigDatabaseHelper.LogMessage(message, partitionedModel); + + Console.WriteLine(message); + } + catch (Exception exc) + { + Console.WriteLine(exc.Message); + Console.WriteLine("Press any key to exit."); + Console.ReadKey(); + Environment.Exit(0); //Avoid recursion if errored connecting to db + } + } + + public static string ReadPassword() + { + string password = ""; + ConsoleKeyInfo info = Console.ReadKey(true); + while (info.Key != ConsoleKey.Enter) + { + if (info.Key != ConsoleKey.Backspace) + { + Console.Write("*"); + password += info.KeyChar; + } + else if (info.Key == ConsoleKey.Backspace) + { + if (!string.IsNullOrEmpty(password)) + { + // remove one character from the list of password characters + password = password.Substring(0, password.Length - 1); + // get the location of the cursor + int pos = Console.CursorLeft; + // move the cursor to the left by one character + Console.SetCursorPosition(pos - 1, Console.CursorTop); + // replace it with space + Console.Write(" "); + // move the cursor to the left by one character again + Console.SetCursorPosition(pos - 1, Console.CursorTop); + } + } + info = Console.ReadKey(true); + } + // add a new line because user pressed enter at the end of password + Console.WriteLine(); + return password; + } + + #region Not needed as sample includes pre-prepared version of AdventureWorks + + private static void InitializeAdventureWorksDatabase(PartitionedModelConfig parameters) + { + //In order to align with assumptions listed in PartitionProcessor.cs, need to: + //1. Delete existing partitions in InternetSales and ResellerSales + //2. Create template partition (again, see comments at top of PartitionProcessor.cs) + + Console.WriteLine("Initializing AdventureWorks ..."); + + using (Server server = new Server()) + { + //Connect and get main objects + string serverConnectionString; + if (parameters.IntegratedAuth) + serverConnectionString = $"Provider=MSOLAP;Data Source={parameters.AnalysisServicesServer};"; + else + { + serverConnectionString = $"Provider=MSOLAP;Data Source={parameters.AnalysisServicesServer};User ID={parameters.UserName};Password={parameters.Password};Persist Security Info=True;Impersonation Level=Impersonate;"; + } + server.Connect(serverConnectionString); + + Database database = server.Databases.FindByName(parameters.AnalysisServicesDatabase); + if (database == null) + { + throw new Microsoft.AnalysisServices.ConnectionException($"Could not connect to database {parameters.AnalysisServicesDatabase}."); + } + + InitializeAdventureWorksTable(database, "Internet Sales", "[dbo].[FactInternetSales]"); + InitializeAdventureWorksTable(database, "Reseller Sales", "[dbo].[FactResellerSales]"); + + database.Update(Microsoft.AnalysisServices.UpdateOptions.ExpandFull); + server.Disconnect(); + } + } + + private static void InitializeAdventureWorksTable(Database database, string analysisServicesTableName, string sourceFactTableName) + { + Table table = database.Model.Tables.Find(analysisServicesTableName); + if (table == null) + { + throw new Microsoft.AnalysisServices.ConnectionException($"Could not connect to table {analysisServicesTableName}."); + } + table.Partitions.Clear(); + Partition templatePartition = new Partition(); + templatePartition.Name = analysisServicesTableName; + table.Partitions.Add(templatePartition); + templatePartition.Source = new QueryPartitionSource() + { + DataSource = database.Model.DataSources[0], //Assuming this is only data source (SqlServer localhost) + Query = $"SELECT * FROM {sourceFactTableName}" + }; + } + + #endregion + } +} + diff --git a/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Properties/AssemblyInfo.cs b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..652f509 --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("AsPartitionProcessing.SampleClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyProduct("AsPartitionProcessing.SampleClient")] +[assembly: AssemblyCopyright("Copyright © Microsoft Corporation 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c45b329d-f606-4f89-a41a-785c247f24b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Settings.Designer.cs b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Settings.Designer.cs new file mode 100644 index 0000000..15ddb4b --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Settings.Designer.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace AsPartitionProcessing.SampleClient { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("localhost")] + public string ConfigServer { + get { + return ((string)(this["ConfigServer"])); + } + set { + this["ConfigServer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("AsPartitionProcessing")] + public string ConfigDatabase { + get { + return ((string)(this["ConfigDatabase"])); + } + set { + this["ConfigDatabase"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool ConfigDatabaseIntegratedAuth { + get { + return ((bool)(this["ConfigDatabaseIntegratedAuth"])); + } + set { + this["ConfigDatabaseIntegratedAuth"] = value; + } + } + } +} diff --git a/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Settings.settings b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Settings.settings new file mode 100644 index 0000000..14e4cff --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing.SampleClient/Settings.settings @@ -0,0 +1,15 @@ + + + + + + localhost + + + AsPartitionProcessing + + + True + + + \ No newline at end of file diff --git a/AsPartitionProcessing/AsPartitionProcessing.sln b/AsPartitionProcessing/AsPartitionProcessing.sln new file mode 100644 index 0000000..af53563 --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing.sln @@ -0,0 +1,73 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsPartitionProcessing", "AsPartitionProcessing\AsPartitionProcessing.csproj", "{FB937281-B06D-47FB-9846-E97B0287AFCE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsPartitionProcessing.SampleClient", "AsPartitionProcessing.SampleClient\AsPartitionProcessing.SampleClient.csproj", "{C45B329D-F606-4F89-A41A-785C247F24B2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0B23151E-8A6B-44AA-B670-797D1EB8B3C0}" + ProjectSection(SolutionItems) = preProject + AdventureWorksDW.bak = AdventureWorksDW.bak + CreateDatabaseObjects.sql = CreateDatabaseObjects.sql + SampleConfiguration.sql = SampleConfiguration.sql + EndProjectSection +EndProject +Project("{6870E480-7721-4708-BFB8-9AE898AA21B3}") = "AsPartitionProcessing.AdventureWorks", "AsPartitionProcessing.AdventureWorks\AsPartitionProcessing.AdventureWorks.smproj", "{7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 + Development|Any CPU = Development|Any CPU + Development|x86 = Development|x86 + Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Debug|x86.ActiveCfg = Debug|Any CPU + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Debug|x86.Build.0 = Debug|Any CPU + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Development|Any CPU.ActiveCfg = Debug|Any CPU + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Development|Any CPU.Build.0 = Debug|Any CPU + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Development|x86.ActiveCfg = Debug|Any CPU + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Development|x86.Build.0 = Debug|Any CPU + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Release|Any CPU.Build.0 = Release|Any CPU + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Release|x86.ActiveCfg = Release|Any CPU + {FB937281-B06D-47FB-9846-E97B0287AFCE}.Release|x86.Build.0 = Release|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Debug|x86.ActiveCfg = Debug|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Debug|x86.Build.0 = Debug|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Development|Any CPU.ActiveCfg = Debug|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Development|Any CPU.Build.0 = Debug|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Development|x86.ActiveCfg = Debug|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Development|x86.Build.0 = Debug|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Release|Any CPU.Build.0 = Release|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Release|x86.ActiveCfg = Release|Any CPU + {C45B329D-F606-4F89-A41A-785C247F24B2}.Release|x86.Build.0 = Release|Any CPU + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Debug|Any CPU.ActiveCfg = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Debug|Any CPU.Build.0 = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Debug|Any CPU.Deploy.0 = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Debug|x86.ActiveCfg = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Debug|x86.Build.0 = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Debug|x86.Deploy.0 = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Development|Any CPU.ActiveCfg = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Development|x86.ActiveCfg = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Development|x86.Build.0 = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Development|x86.Deploy.0 = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Release|Any CPU.ActiveCfg = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Release|Any CPU.Build.0 = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Release|Any CPU.Deploy.0 = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Release|x86.ActiveCfg = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Release|x86.Build.0 = Development|x86 + {7274EFCC-E7DF-40AD-83DF-1DFD5B954E67}.Release|x86.Deploy.0 = Development|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/AsPartitionProcessing/AsPartitionProcessing/AsPartitionProcessing.csproj b/AsPartitionProcessing/AsPartitionProcessing/AsPartitionProcessing.csproj new file mode 100644 index 0000000..27dc10a --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing/AsPartitionProcessing.csproj @@ -0,0 +1,72 @@ + + + + + Debug + AnyCPU + {FB937281-B06D-47FB-9846-E97B0287AFCE} + Library + Properties + AsPartitionProcessing + AsPartitionProcessing + v4.5.2 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\AsPartitionProcessing.XML + + + + False + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\Microsoft.AnalysisServices.DLL + + + False + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\Microsoft.AnalysisServices.Core.DLL + + + False + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\Microsoft.AnalysisServices.Tabular.DLL + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AsPartitionProcessing/AsPartitionProcessing/ConfigDatabaseConnectionInfo.cs b/AsPartitionProcessing/AsPartitionProcessing/ConfigDatabaseConnectionInfo.cs new file mode 100644 index 0000000..879d99a --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing/ConfigDatabaseConnectionInfo.cs @@ -0,0 +1,35 @@ +using System; + +namespace AsPartitionProcessing +{ + /// + /// Information required to connect to the configuration and logging database. + /// + public class ConfigDatabaseConnectionInfo + { + /// + /// Database server name. + /// + public string Server { get; set; } + + /// + /// Name of the database. + /// + public string Database { get; set; } + + /// + /// User name used for connection. + /// + public string UserName { get; set; } + + /// + /// Password used for connection. + /// + public string Password { get; set; } + + /// + /// Whether connection to be made using integrated authentication or SQL authentication. + /// + public bool IntegratedAuth { get; set; } + } +} diff --git a/AsPartitionProcessing/AsPartitionProcessing/ConfigDatabaseHelper.cs b/AsPartitionProcessing/AsPartitionProcessing/ConfigDatabaseHelper.cs new file mode 100644 index 0000000..07a772c --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing/ConfigDatabaseHelper.cs @@ -0,0 +1,173 @@ +using System; +using System.Data; +using System.Data.SqlClient; +using System.Collections.Generic; + +namespace AsPartitionProcessing +{ + /// + /// Class containing helper methods for reading and writing to the configuration and logging database. + /// + public static class ConfigDatabaseHelper + { + /// + /// Read configuration information from the database. + /// + /// Information required to connect to the configuration and logging database. + /// Collection of partitioned models with configuration information. + public static List ReadConfig(ConfigDatabaseConnectionInfo connectionInfo) + { + using (SqlConnection connection = new SqlConnection(GetConnectionString(connectionInfo))) + { + connection.Open(); + using (SqlCommand command = new SqlCommand()) + { + command.Connection = connection; + command.CommandType = CommandType.Text; + command.CommandText = @" + SELECT [PartitionedModelConfigID] + ,[AnalysisServicesServer] + ,[AnalysisServicesDatabase] + ,[InitialSetUp] + ,[IncrementalOnline] + ,[IncrementalParallelTables] + ,[IntegratedAuth] + ,[PartitionedTableConfigID] + ,[MaxDate] + ,[Granularity] + ,[NumberOfPartitionsFull] + ,[NumberOfPartitionsForIncrementalProcess] + ,[AnalysisServicesTable] + ,[SourceTableName] + ,[SourcePartitionColumn] + FROM [dbo].[vPartitionedTableConfig] + ORDER BY [PartitionedModelConfigID], [PartitionedTableConfigID];"; + + List models = new List(); + PartitionedModelConfig modelConfig = null; + int currentPartitionedModelConfigID = -1; + + SqlDataReader reader = command.ExecuteReader(); + while (reader.Read()) + { + if (modelConfig == null || currentPartitionedModelConfigID != Convert.ToInt32(reader["PartitionedModelConfigID"])) + { + modelConfig = new PartitionedModelConfig(); + modelConfig.PartitionedTables = new List(); + models.Add(modelConfig); + + modelConfig.PartitionedModelConfigID = Convert.ToInt32(reader["PartitionedModelConfigID"]); + modelConfig.AnalysisServicesServer = Convert.ToString(reader["AnalysisServicesServer"]); + modelConfig.AnalysisServicesDatabase = Convert.ToString(reader["AnalysisServicesDatabase"]); + modelConfig.InitialSetUp = Convert.ToBoolean(reader["InitialSetUp"]); + modelConfig.IncrementalOnline = Convert.ToBoolean(reader["IncrementalOnline"]); + modelConfig.IncrementalParallelTables = Convert.ToBoolean(reader["IncrementalParallelTables"]); + modelConfig.IntegratedAuth = Convert.ToBoolean(reader["IntegratedAuth"]); + modelConfig.ConfigDatabaseConnectionInfo = connectionInfo; + + currentPartitionedModelConfigID = modelConfig.PartitionedModelConfigID; + } + + modelConfig.PartitionedTables.Add( + new PartitionedTableConfig( + Convert.ToInt32(reader["PartitionedTableConfigID"]), + Convert.ToDateTime(reader["MaxDate"]), + (Granularity)Convert.ToInt32(reader["Granularity"]), + Convert.ToInt32(reader["NumberOfPartitionsFull"]), + Convert.ToInt32(reader["NumberOfPartitionsForIncrementalProcess"]), + Convert.ToString(reader["AnalysisServicesTable"]), + Convert.ToString(reader["SourceTableName"]), + Convert.ToString(reader["SourcePartitionColumn"]) + ) + ); + } + + return models; + } + } + } + + /// + /// Delete all existing logs from the database. Useful in demo scenarios to initialize the database. + /// + /// Information required to connect to the configuration and logging database. + public static void ClearLogTable(ConfigDatabaseConnectionInfo connectionInfo) + { + using (var connection = new SqlConnection(GetConnectionString(connectionInfo))) + { + connection.Open(); + using (var command = new SqlCommand()) + { + command.Connection = connection; + command.CommandType = CommandType.Text; + command.CommandText = "DELETE FROM [dbo].[PartitionedModelLog];"; + command.ExecuteNonQuery(); + } + } + } + + /// + /// Log a message to the databsae. + /// + /// Message to be logged. + /// Partitioned model with configuration information. + public static void LogMessage(string message, PartitionedModelConfig partitionedModel) + { + using (var connection = new SqlConnection(GetConnectionString(partitionedModel.ConfigDatabaseConnectionInfo))) + { + connection.Open(); + using (var command = new SqlCommand()) + { + command.Connection = connection; + command.CommandType = CommandType.Text; + command.CommandText = @" + INSERT INTO [dbo].[PartitionedModelLog] + ([PartitionedModelConfigID] + ,[ExecutionID] + ,[LogDateTime] + ,[Message]) + VALUES + (@PartitionedModelConfigID + ,@ExecutionID + ,@LogDateTime + ,@Message);"; + + SqlParameter parameter; + + parameter = new SqlParameter("@PartitionedModelConfigID", SqlDbType.Int); + parameter.Value = partitionedModel.PartitionedModelConfigID; + command.Parameters.Add(parameter); + + parameter = new SqlParameter("@ExecutionID", SqlDbType.Char, 36); + parameter.Value = partitionedModel.ExecutionID; + command.Parameters.Add(parameter); + + parameter = new SqlParameter("@LogDateTime", SqlDbType.DateTime); + parameter.Value = DateTime.Now; + command.Parameters.Add(parameter); + + parameter = new SqlParameter("@Message", SqlDbType.VarChar, 4000); + parameter.Value = message; + command.Parameters.Add(parameter); + + command.ExecuteNonQuery(); + } + } + } + + private static string GetConnectionString(ConfigDatabaseConnectionInfo connectionInfo) + { + string connectionString; + if (connectionInfo.IntegratedAuth) + { + connectionString = $"Server={connectionInfo.Server};Database={connectionInfo.Database};Integrated Security=SSPI;"; + } + else + { + connectionString = $"Server={connectionInfo.Server};Database={connectionInfo.Database};User ID={connectionInfo.UserName};Password={connectionInfo.Password};"; + } + + return connectionString; + } + } +} diff --git a/AsPartitionProcessing/AsPartitionProcessing/LogMessageEventArgs.cs b/AsPartitionProcessing/AsPartitionProcessing/LogMessageEventArgs.cs new file mode 100644 index 0000000..772f934 --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing/LogMessageEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace AsPartitionProcessing +{ + public class LogMessageEventArgs : EventArgs + { + public string Message { get; set; } + + public LogMessageEventArgs(string message) + { + Message = message; + } + } +} diff --git a/AsPartitionProcessing/AsPartitionProcessing/PartitionProcessor.cs b/AsPartitionProcessing/AsPartitionProcessing/PartitionProcessor.cs new file mode 100644 index 0000000..8c55201 --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing/PartitionProcessor.cs @@ -0,0 +1,343 @@ +using System; +using System.Collections.Generic; +using Microsoft.AnalysisServices.Tabular; + + +//----------- +//ASSUMPTIONS +//Rolling window. Removes oldest partition on increment +//Depends on date keys in source to be integers formatted as yyyymmdd +//Source queries take the form "SELECT * FROM WHERE FLOOR( / 100) = " (monthly) +//Template partition exists with same name as table +//Non-template partitions have name of the format yyyy (yearly), yyyymm (monthly), yyyymmdd (daily) +//----------- + + +namespace AsPartitionProcessing +{ + /// + /// Delegate to allow client to pass in a custom logging method + /// + /// The message to be logged + /// Configuration info for the partitioned model + public delegate void LogMessageDelegate(string message, PartitionedModelConfig partitionedModel); + + /// + /// Processor of partitions in AS tabular models + /// + public static class PartitionProcessor + { + private static PartitionedModelConfig _partitionedModel; + private static LogMessageDelegate _messageLogger; + + /// + /// Partitions tables in a tabular model based on configuration + /// + /// Configuration info for the partitioned model + /// Pointer to logging method + public static void PerformProcessing(PartitionedModelConfig partitionedModel, LogMessageDelegate messageLogger) + { + _partitionedModel = partitionedModel; + _messageLogger = messageLogger; + + Server server = new Server(); + try + { + Database database; + Connect(server, out database); + + Console.ForegroundColor = ConsoleColor.White; + LogMessage($"Start: {DateTime.Now.ToString("hh:mm:ss tt")}", false); + LogMessage($"Server: {_partitionedModel.AnalysisServicesServer}", false); + + LogMessage($"Database: {_partitionedModel.AnalysisServicesDatabase}", false); + + foreach (PartitionedTableConfig partitionedTable in _partitionedModel.PartitionedTables) + { + Console.ForegroundColor = ConsoleColor.Yellow; + + Table table = database.Model.Tables.Find(partitionedTable.AnalysisServicesTable); + if (table == null) + { + throw new Microsoft.AnalysisServices.ConnectionException($"Could not connect to table {partitionedTable.AnalysisServicesTable}."); + } + Partition templatePartition = table.Partitions.Find(partitionedTable.AnalysisServicesTable); + if (templatePartition == null) + { + throw new Microsoft.AnalysisServices.ConnectionException($"Table {partitionedTable.AnalysisServicesTable} does not contain a partition with the same name to act as the template partition."); + } + + LogMessage("", false); + LogMessage($"Rolling-window partitioning for table {partitionedTable.AnalysisServicesTable}", false); + LogMessage(new String('-', partitionedTable.AnalysisServicesTable.Length + 38), false); + + //Figure out what processing needs to be done + List partitionKeysCurrent = GetPartitionKeysTable(table, partitionedTable.Granularity); + List partitionKeysNew = GetPartitionKeys(false, partitionedTable, partitionedTable.Granularity); + List partitionKeysForProcessing = GetPartitionKeys(true, partitionedTable, partitionedTable.Granularity); + DisplayPartitionRange(partitionKeysCurrent, true, partitionedTable.Granularity); + DisplayPartitionRange(partitionKeysNew, false, partitionedTable.Granularity); + LogMessage("", false); + LogMessage("=>Actions & progress:", false); + + //Check for old partitions that need to be removed + foreach (string partitionKey in partitionKeysCurrent) + { + if (Convert.ToInt32(partitionKey) < Convert.ToInt32(partitionKeysNew[0])) + { + LogMessage($"Remove old partition {DateFormatPartitionKey(partitionKey, partitionedTable.Granularity)}", true); + table.Partitions.Remove(partitionKey); + } + } + + //Process partitions + string selectQueryTemplate; + switch (partitionedTable.Granularity) + { + case Granularity.Daily: + selectQueryTemplate = "SELECT * FROM {0} WHERE {1} = {2} ORDER BY {1}"; + break; + case Granularity.Monthly: + selectQueryTemplate = "SELECT * FROM {0} WHERE FLOOR({1} / 100) = {2} ORDER BY {1}"; + break; + default: //Granularity.Yearly: + selectQueryTemplate = "SELECT * FROM {0} WHERE FLOOR({1} / 10000) = {2} ORDER BY {1}"; + break; + } + + foreach (string partitionKey in partitionKeysForProcessing) + { + Partition partitionToProcess = table.Partitions.Find(partitionKey); + + if (partitionToProcess == null) + { + //Doesn't exist so create it + partitionToProcess = new Partition(); + templatePartition.CopyTo(partitionToProcess); + partitionToProcess.Name = partitionKey; + ((QueryPartitionSource)partitionToProcess.Source).Query = String.Format(selectQueryTemplate, partitionedTable.SourceTableName, partitionedTable.SourcePartitionColumn, partitionKey); + table.Partitions.Add(partitionToProcess); + LogMessage($"Create new partition {DateFormatPartitionKey(partitionKey, partitionedTable.Granularity)}", true); + + if (!_partitionedModel.InitialSetUp) + { + IncrementalProcessPartition(partitionKey, partitionToProcess, partitionedTable.Granularity); + } + } + else if (!_partitionedModel.InitialSetUp) + { + //Existing partition for processing + IncrementalProcessPartition(partitionKey, partitionToProcess, partitionedTable.Granularity); + } + + if (_partitionedModel.InitialSetUp) + { + if (partitionToProcess.State != ObjectState.Ready) + { + //Process new partitions sequentially during initial setup so don't run out of memory + LogMessage($"Sequentially process {DateFormatPartitionKey(partitionKey, partitionedTable.Granularity)} /DataOnly", true); + partitionToProcess.RequestRefresh(RefreshType.DataOnly); + database.Model.SaveChanges(); + } + else + { + //Partition already exists during initial setup (and is fully processed), so ignore it + LogMessage($"Partition {DateFormatPartitionKey(partitionKey, partitionedTable.Granularity)} already exists and is processed", true); + } + } + } + + //Ensure template partition doesn't contain any data + if (_partitionedModel.InitialSetUp) + { + ((QueryPartitionSource)templatePartition.Source).Query = String.Format("SELECT * FROM {0} WHERE 0=1", partitionedTable.SourceTableName); + templatePartition.RequestRefresh(RefreshType.DataOnly); + } + + //If processing tables sequentially (but all partitions being done in parallel), then save changes now + if (!_partitionedModel.IncrementalParallelTables) + { + LogMessage($"Save changes for table {partitionedTable.AnalysisServicesTable} ...", true); + database.Model.SaveChanges(); + } + } + + //Commit the data changes, and bring model back online if necessary + + LogMessage("", false); + LogMessage("Final operations", false); + LogMessage(new String('-', 16), false); + + if (_partitionedModel.IncrementalParallelTables) + { + LogMessage("Save changes ...", true); + database.Model.SaveChanges(); + } + + if (_partitionedModel.InitialSetUp || (!_partitionedModel.InitialSetUp && !_partitionedModel.IncrementalOnline)) + { + LogMessage("Recalc model to bring back online ...", true); + + database.Model.RequestRefresh(RefreshType.Calculate); + database.Model.SaveChanges(); + } + + Console.ForegroundColor = ConsoleColor.White; + LogMessage("", false); + LogMessage("Finish: " + DateTime.Now.ToString("hh:mm:ss tt"), false); + } + catch (Exception exc) + { + Console.ForegroundColor = ConsoleColor.Red; + LogMessage("", false); + LogMessage($"Exception occurred: {DateTime.Now.ToString("hh:mm:ss tt")}", false); + LogMessage($"Exception message: {exc.Message}", false); + LogMessage($"Inner exception message: {exc.InnerException.Message}", false); + } + finally + { + try + { + _partitionedModel = null; + _messageLogger = null; + if (server != null) server.Disconnect(); + } + catch { } + } + } + + private static void IncrementalProcessPartition(string partitionKey, Partition partitionToProcess, Granularity granularity) + { + if (_partitionedModel.IncrementalOnline) + { + LogMessage($"Parallel process partition {DateFormatPartitionKey(partitionKey, granularity)} /Full", true); + partitionToProcess.RequestRefresh(RefreshType.Full); + } + else + { + LogMessage($"Parallel process partition {DateFormatPartitionKey(partitionKey, granularity)} /DataOnly", true); + partitionToProcess.RequestRefresh(RefreshType.DataOnly); + } + } + + private static void LogMessage(string message, bool indented) + { + _messageLogger($"{(indented ? new String(' ', 3) : "")}{message}", _partitionedModel); + } + + private static string DateFormatPartitionKey(string partitionKey, Granularity granularity) + { + string returnVal = partitionKey.Substring(0, 4); + + try + { + if (granularity == Granularity.Monthly || granularity == Granularity.Daily) + { + returnVal += "-" + partitionKey.Substring(4, 2); + } + + if (granularity == Granularity.Daily) + { + returnVal += "-" + partitionKey.Substring(6, 2); + } + } + catch (ArgumentOutOfRangeException exc) + { + throw new InvalidOperationException($"Failed to derive date from partition key. Check the key {partitionKey} matches {Convert.ToString(granularity)} granularity."); + } + + return returnVal; + } + + private static void DisplayPartitionRange(List partitionKeys, bool current, Granularity granularity) + { + LogMessage("", false); + + if (partitionKeys.Count > 0) + { + LogMessage($"=>{(current ? "Current" : "New")} partition range ({Convert.ToString(granularity)}):", false); + LogMessage($"MIN partition: {DateFormatPartitionKey(partitionKeys[0], granularity)}", true); + LogMessage($"MAX partition: {DateFormatPartitionKey(partitionKeys[partitionKeys.Count - 1], granularity)}", true); + LogMessage($"Partition count: {partitionKeys.Count}", true); + } + else + { + LogMessage("=>Table not yet partitioned", false); + } + } + + private static void Connect(Server server, out Database database) + { + //Connect and get main objects + string serverConnectionString; + if (_partitionedModel.IntegratedAuth) + serverConnectionString = $"Provider=MSOLAP;Data Source={_partitionedModel.AnalysisServicesServer};"; + else + { + serverConnectionString = $"Provider=MSOLAP;Data Source={_partitionedModel.AnalysisServicesServer};User ID={_partitionedModel.UserName};Password={_partitionedModel.Password};Persist Security Info=True;Impersonation Level=Impersonate;"; + } + server.Connect(serverConnectionString); + + database = server.Databases.FindByName(_partitionedModel.AnalysisServicesDatabase); + if (database == null) + { + throw new Microsoft.AnalysisServices.ConnectionException($"Could not connect to database {_partitionedModel.AnalysisServicesDatabase}."); + } + } + + private static List GetPartitionKeys(bool forProcessing, PartitionedTableConfig partitionedTable, Granularity granularity) + { + //forProcessing: false to return complete target set of partitions, true to return partitons to be processed (may be less if incremental mode). + + List partitionKeys = new List(); + int numberOfPartitions = (forProcessing && !_partitionedModel.InitialSetUp ? partitionedTable.NumberOfPartitionsForIncrementalProcess : partitionedTable.NumberOfPartitionsFull); + + for (int i = numberOfPartitions - 1; i >= 0; i--) + { + DateTime periodToAdd; + switch (granularity) + { + case Granularity.Daily: + periodToAdd = partitionedTable.MaxDate.AddDays(-i); + partitionKeys.Add(Convert.ToString((periodToAdd.Year * 100 + periodToAdd.Month) * 100 + periodToAdd.Day)); + break; + case Granularity.Monthly: + periodToAdd = partitionedTable.MaxDate.AddMonths(-i); + partitionKeys.Add(Convert.ToString(periodToAdd.Year * 100 + periodToAdd.Month)); + break; + default: //Granularity.Yearly: + periodToAdd = partitionedTable.MaxDate.AddYears(-i); + partitionKeys.Add(Convert.ToString(periodToAdd.Year)); + break; + } + } + partitionKeys.Sort(); + + return partitionKeys; + } + + private static List GetPartitionKeysTable(Table table, Granularity granularity) + { + List partitionKeysExisting = new List(); + + foreach (Partition partition in table.Partitions) + { + //Ignore partitions that don't follow the convention yyyy, yyyymm or yyyymmdd + int partitionKey; + if ( + ( ( partition.Name.Length == 4 && granularity == Granularity.Yearly ) || + ( partition.Name.Length == 6 && granularity == Granularity.Monthly ) || + ( partition.Name.Length == 8 && granularity == Granularity.Daily ) + ) && int.TryParse(partition.Name, out partitionKey) + ) + { + partitionKeysExisting.Add(Convert.ToString(partitionKey)); + } + } + partitionKeysExisting.Sort(); + + return partitionKeysExisting; + } + } +} + diff --git a/AsPartitionProcessing/AsPartitionProcessing/PartitionedModelConfig.cs b/AsPartitionProcessing/AsPartitionProcessing/PartitionedModelConfig.cs new file mode 100644 index 0000000..d215a50 --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing/PartitionedModelConfig.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; + +namespace AsPartitionProcessing +{ + /// + /// Configuration information for a partitioned AS tabular model. + /// + public class PartitionedModelConfig + { + /// + /// ID of the PartitionedModelConfig table. + /// + public int PartitionedModelConfigID { get; set; } + + /// + /// Name of the Analysis Services instance. Can be SSAS or an Azure AS URL. + /// + public string AnalysisServicesServer { get; set; } + + /// + /// Name of the Analysis Services database. + /// + public string AnalysisServicesDatabase { get; set; } + + /// + /// True for initial set up to create partitions and process them sequentially. False for incremental processing. + /// + public bool InitialSetUp { get; set; } + + /// + /// When initialSetUp=false, determines if processing is performed as an online operation, which may require more memory, but users can still query the model. + /// + public bool IncrementalOnline { get; set; } + + /// + /// When initialSetUp=false, determines if separate tables are processed in parallel. Partitions within a table are always processed in parallel. + /// + public bool IncrementalParallelTables { get; set; } + + /// + /// Should always set to true for SSAS implementations that will run under the current process account. For Azure AS, normally set to false. + /// + public bool IntegratedAuth { get; set; } + + /// + /// Only applies when integratedAuth=false. Used for Azure AD UPNs to connect to Azure AS. + /// + public string UserName { get; set; } + + /// + /// Only applies when integratedAuth=false. Used for Azure AD UPNs to connect to Azure AS. + /// + public string Password { get; set; } + + /// + /// Collection of partitioned tables containing configuration information. + /// + public List PartitionedTables { get; set; } + + /// + /// Connection information to connect to the configuration and logging database. + /// + public ConfigDatabaseConnectionInfo ConfigDatabaseConnectionInfo { get; set; } + + /// + /// GUID generated to the execution run. + /// + public string ExecutionID { get; set; } + + /// + /// Parameters normally from configuration database to determine partitioning ranges and design. + /// + /// ID of the PartitionedModelConfig table. + /// Name of the Analysis Services instance. Can be SSAS or an Azure AS URL. + /// Name of the Analysis Services database. + /// True for initial set up to create partitions and process them sequentially. False for incremental processing. + /// When initialSetUp=false, determines if processing is performed as an online operation, which may require more memory, but users can still query the model. + /// When initialSetUp=false, determines if separate tables are processed in parallel. Partitions within a table are always processed in parallel. + /// Should always set to true for SSAS implementations that will run under the current process account. For Azure AS, normally set to false. + /// Only applies when integratedAuth=false. Used for Azure AD UPNs to connect to Azure AS. + /// Only applies when integratedAuth=false. Used for Azure AD UPNs to connect to Azure AS. + /// Collection of partitioned tables containing configuration information. + public PartitionedModelConfig( + int partitionedModelConfigID, + string analysisServicesServer, + string analysisServicesDatabase, + bool initialSetUp, + bool incrementalOnline, + bool incrementalParallelTables, + bool integratedAuth, + string userName, + string password, + List partitionedTables + ) + { + PartitionedModelConfigID = partitionedModelConfigID; + AnalysisServicesServer = analysisServicesServer; + AnalysisServicesDatabase = analysisServicesDatabase; + InitialSetUp = initialSetUp; + IncrementalOnline = incrementalOnline; + IncrementalParallelTables = incrementalParallelTables; + IntegratedAuth = integratedAuth; + UserName = userName; + Password = password; + PartitionedTables = partitionedTables; + ExecutionID = Guid.NewGuid().ToString(); + } + + /// + /// Default constructor. + /// + public PartitionedModelConfig() + { + ExecutionID = Guid.NewGuid().ToString(); + } + } +} diff --git a/AsPartitionProcessing/AsPartitionProcessing/PartitionedTableConfig.cs b/AsPartitionProcessing/AsPartitionProcessing/PartitionedTableConfig.cs new file mode 100644 index 0000000..4783810 --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing/PartitionedTableConfig.cs @@ -0,0 +1,93 @@ +using System; + +namespace AsPartitionProcessing +{ + /// + /// Configuration information for a partitioned table within an AS tabular model. + /// + public class PartitionedTableConfig + { + /// + /// ID of the PartitionedTableConfig table. + /// + public int PartitionedTableConfigID { get; set; } + + /// + /// The maximum date that needs to be accounted for in the partitioned table. Represents the upper boundary of the rolling window. + /// + public DateTime MaxDate { get; set; } + + /// + /// Partition granularity, which can be Yearly, Monthly or Daily. + /// + public Granularity Granularity { get; set; } + + /// + /// Count of all partitions in the rolling window. For example, a rolling window of 10 years partitioned by month would result in 120 partitions. + /// + public int NumberOfPartitionsFull { get; set; } + + /// + /// Count of “hot partitions” where the data can change. For example, it may be necessary to refresh the most recent 3 months of data every day. This only applies to the most recent partitions. + /// + public int NumberOfPartitionsForIncrementalProcess { get; set; } + + /// + /// Name of the partitioned table in the tabular model. + /// + public string AnalysisServicesTable { get; set; } + + /// + /// Name of the source table in the relational database. + /// + public string SourceTableName { get; set; } + + /// + /// Name of the source column from the table in the relational database. + /// + public string SourcePartitionColumn { get; set; } + + /// + /// Initialize configuration info for partitioned table. Normally populated from a configuration database. + /// + /// Parent model. + /// ID of the PartitionedTableConfig table. + /// The maximum date that needs to be accounted for in the partitioned table. Represents the upper boundary of the rolling window. + /// Partition granularity, which can be Yearly, Monthly or Daily. + /// Count of all partitions in the rolling window. For example, a rolling window of 10 years partitioned by month would result in 120 partitions. + /// Count of “hot partitions” where the data can change. For example, it may be necessary to refresh the most recent 3 months of data every day. This only applies to the most recent partitions. + /// Name of the partitioned table in the tabular model. + /// Name of the source table in the relational database. + /// Name of the source column from the table in the relational database. + public PartitionedTableConfig( + int partitionedTableConfigID, + DateTime maxDate, + Granularity granularity, + int numberOfPartitionsFull, + int numberOfPartitionsForIncrementalProcess, + string analysisServicesTable, + string sourceTableName, + string sourcePartitionColumn + ) + { + PartitionedTableConfigID = partitionedTableConfigID; + MaxDate = maxDate; + Granularity = granularity; + NumberOfPartitionsFull = numberOfPartitionsFull; + NumberOfPartitionsForIncrementalProcess = numberOfPartitionsForIncrementalProcess; + AnalysisServicesTable = analysisServicesTable; + SourceTableName = sourceTableName; + SourcePartitionColumn = sourcePartitionColumn; + } + } + + /// + /// Enumeration of supported partition granularities. + /// + public enum Granularity + { + Daily = 0, + Monthly = 1, + Yearly = 2 + } +} diff --git a/AsPartitionProcessing/AsPartitionProcessing/Properties/AssemblyInfo.cs b/AsPartitionProcessing/AsPartitionProcessing/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9180916 --- /dev/null +++ b/AsPartitionProcessing/AsPartitionProcessing/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("AsPartitionProcessing")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyProduct("AsPartitionProcessing")] +[assembly: AssemblyCopyright("Copyright © Microsoft Corporation 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("fb937281-b06d-47fb-9846-e97b0287afce")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AsPartitionProcessing/CreateDatabaseObjects.sql b/AsPartitionProcessing/CreateDatabaseObjects.sql new file mode 100644 index 0000000..886ee1f --- /dev/null +++ b/AsPartitionProcessing/CreateDatabaseObjects.sql @@ -0,0 +1,87 @@ +CREATE DATABASE [AsPartitionProcessing] +GO +USE [AsPartitionProcessing] +GO + +CREATE TABLE [dbo].[PartitionedModelConfig]( + [PartitionedModelConfigID] [int] NOT NULL, + [AnalysisServicesServer] [varchar](255) NOT NULL, + [AnalysisServicesDatabase] [varchar](255) NOT NULL, + [InitialSetUp] [bit] NOT NULL, + [IncrementalOnline] [bit] NOT NULL, + [IncrementalParallelTables] [bit] NOT NULL, + [IntegratedAuth] [bit] NOT NULL, + CONSTRAINT [PK_PartitionedDatabaseConfig] PRIMARY KEY CLUSTERED +( + [PartitionedModelConfigID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[PartitionedTableConfig]( + [PartitionedTableConfigID] [int] NOT NULL, + [PartitionedModelConfigID] [int] NOT NULL, + [MaxDate] [date] NOT NULL, + [Granularity] [tinyint] NOT NULL, + [NumberOfPartitionsFull] [int] NOT NULL, + [NumberOfPartitionsForIncrementalProcess] [int] NOT NULL, + [AnalysisServicesTable] [varchar](255) NOT NULL, + [SourceTableName] [varchar](255) NOT NULL, + [SourcePartitionColumn] [varchar](255) NOT NULL, + CONSTRAINT [PK_PartitionedTablesConfig] PRIMARY KEY CLUSTERED +( + [PartitionedTableConfigID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[PartitionedModelLog]( + [PartitionedModelLogID] [int] IDENTITY(1,1) NOT NULL, + [PartitionedModelConfigID] [int] NOT NULL, + [ExecutionID] [char](36) NOT NULL, + [LogDateTime] [datetime] NOT NULL, + [Message] [varchar](8000) NOT NULL, + CONSTRAINT [PK_PartitionedModelLog] PRIMARY KEY CLUSTERED +( + [PartitionedModelLogID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[PartitionedTableConfig] WITH CHECK ADD CONSTRAINT [FK_PartitionedTableConfig_PartitionedDatabaseConfig] FOREIGN KEY([PartitionedModelConfigID]) +REFERENCES [dbo].[PartitionedModelConfig] ([PartitionedModelConfigID]) +GO + +ALTER TABLE [dbo].[PartitionedTableConfig] CHECK CONSTRAINT [FK_PartitionedTableConfig_PartitionedDatabaseConfig] +GO + +ALTER TABLE [dbo].[PartitionedModelLog] WITH CHECK ADD CONSTRAINT [FK_PartitionedModelLog_PartitionedModelConfig] FOREIGN KEY([PartitionedModelConfigID]) +REFERENCES [dbo].[PartitionedModelConfig] ([PartitionedModelConfigID]) +GO + +ALTER TABLE [dbo].[PartitionedModelLog] CHECK CONSTRAINT [FK_PartitionedModelLog_PartitionedModelConfig] +GO + + + +CREATE VIEW [dbo].[vPartitionedTableConfig] +AS +SELECT m.[PartitionedModelConfigID] + ,m.[AnalysisServicesServer] + ,m.[AnalysisServicesDatabase] + ,m.[InitialSetUp] + ,m.[IncrementalOnline] + ,m.[IncrementalParallelTables] + ,m.[IntegratedAuth] + ,t.[PartitionedTableConfigID] + ,t.[MaxDate] + ,t.[Granularity] + ,t.[NumberOfPartitionsFull] + ,t.[NumberOfPartitionsForIncrementalProcess] + ,t.[AnalysisServicesTable] + ,t.[SourceTableName] + ,t.[SourcePartitionColumn] + FROM [dbo].[PartitionedTableConfig] t +INNER JOIN [dbo].[PartitionedModelConfig] m ON t.[PartitionedModelConfigID] = m.[PartitionedModelConfigID] +GO + diff --git a/AsPartitionProcessing/SampleConfiguration.sql b/AsPartitionProcessing/SampleConfiguration.sql new file mode 100644 index 0000000..8288a74 --- /dev/null +++ b/AsPartitionProcessing/SampleConfiguration.sql @@ -0,0 +1,34 @@ +INSERT INTO [dbo].[PartitionedModelConfig] +VALUES( + 1 --[PartitionedModelConfigID] + ,'localhost' --[AnalysisServicesServer] + ,'AdventureWorks' --[AnalysisServicesDatabase] + ,1 --[InitialSetUp] + ,1 --[IncrementalOnline] + ,1 --[IncrementalParallelTables] + ,1 --[IntegratedAuth] +); + +INSERT INTO [dbo].[PartitionedTableConfig] +VALUES( + 1 --[PartitionedTableConfigID] + ,1 --[PartitionedModelConfigID] + ,'2012-12-01' --[MaxDate] + ,1 --[Granularity] 1=Monthly + ,12 --[NumberOfPartitionsFull] + ,3 --[NumberOfPartitionsForIncrementalProcess] + ,'Internet Sales' --[AnalysisServicesTable] + ,'[dbo].[FactInternetSales]'--[SourceTableName] + ,'OrderDateKey' --[SourcePartitionColumn] +), +( + 2 --[PartitionedTableConfigID] + ,1 --[PartitionedModelConfigID] + ,'2012-12-01' --[MaxDate] + ,2 --[Granularity] 2=Yearly + ,3 --[NumberOfPartitionsFull] + ,1 --[NumberOfPartitionsForIncrementalProcess] + ,'Reseller Sales' --[AnalysisServicesTable] + ,'[dbo].[FactResellerSales]'--[SourceTableName] + ,'OrderDateKey' --[SourcePartitionColumn] +);