水滴石穿

渴望成为高手--Amy.Qiu
posts - 81, comments - 9, trackbacks - 0, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

2010年4月16日

如何使用MDX编写同比和环比

在做BI项目中,会经常提出类似这样的需求:
1. 本月比上月的销售额增长了多少?百分比是多少?
2. 本季度第一个月比上季度第一个月的销售额增长了多少?百分比是多少?

环比
需求:本月比上个月的销售额增长了多少个百分点?
MDX语句如下:
WITH MEMBER Measures.[Amount Change Rate] AS 
  
[Measures].[Internet Sales Amount]/
  (
[Measures].[Internet Sales Amount],[Date].[Fiscal].CurrentMember.PrevMember) -1
  , FORMAT_STRING 
= '0%'
SELECT 
{
[Measures].[Internet Sales Amount], Measures.[Amount Change Rate]ON 0,
[Date].[Fiscal].[Month].Members ON 1
FROM [Adventure Works]

结果如下:


上面的输出结果有一个问题,就是第一行数据会发生除0的情况,为了防止这种情况,MDX修改如下:
WITH MEMBER Measures.[Amount Change Rate] AS 
  IIF(ISEMPTY((
[Measures].[Internet Sales Amount],[Date].[Fiscal].CurrentMember.PrevMember)), "", 
   
[Measures].[Internet Sales Amount]/
  (
[Measures].[Internet Sales Amount],[Date].[Fiscal].CurrentMember.PrevMember) -1
  ), FORMAT_STRING 
= '0%'
SELECT 
{
[Measures].[Internet Sales Amount], Measures.[Amount Change Rate]ON 0,
[Date].[Fiscal].[Month].Members
 
ON 1
FROM [Adventure Works]

如果发现0为空,则显示空字符串。输出结果如下:



同比
需求:本季度每个月比上个季度同期销售额增长了多少个百分点?
WITH MEMBER Measures.[Change Amount Rate] AS 
 
[Measures].[Internet Sales Amount]/
 (  
[Measures].[Internet Sales Amount]
    Cousin(
[Date].[Fiscal].CurrentMember, [Date].[Fiscal].CurrentMember.Parent.PrevMember)
 ) 
- 1, FORMAT_STRING = '0%'   
SELECT {[Measures].[Internet Sales Amount], Measures.[Change Amount Rate]ON 0,
[Date].[Fiscal].[Month].Members ON 1
FROM [Adventure Works]

运行结果如下:


和环比相同,这里也出现了除0的情况,所以把MDX做如下修改:
WITH MEMBER Measures.[Change Amount Rate] AS 
  IIF(
    (
[Measures].[Internet Sales Amount]
     Cousin(
[Date].[Fiscal].CurrentMember, [Date].[Fiscal].CurrentMember.Parent.PrevMember)
    )=0
    , "", 
     
[Measures].[Internet Sales Amount]/
     (  
[Measures].[Internet Sales Amount]
        Cousin(
[Date].[Fiscal].CurrentMember, [Date].[Fiscal].CurrentMember.Parent.PrevMember)
     ) 
- 1
  ), FORMAT_STRING 
= '0%'   
SELECT {[Measures].[Internet Sales Amount], Measures.[Change Amount Rate]ON 0,
[Date].[Fiscal].[Month].Members ON 1
FROM [Adventure Works]

输出结果如下:

posted @ 2010-04-16 16:22 AmyQiu 阅读(171) 评论(0) 编辑

2010年4月14日

SSIS2008组件使用测试

Author: Amy.Qiu

 

Control Flow

1.         Foreach Loop Container

Eg. 循环Excel文件。

a. 配置foreach, 双击后选择Collection-> Enumerator-> Foreach File Enumerator;

Variable Mappings-> Variable.@ExcelFilePath(要给个初始值)

b. Connection Managers: 新建Excel Connection Manager, 设置

Properties-> Expressions-> ExcelFilePath-> Variable.@ExcelFilePath

c. Excel Source-> Destination

Eg. 循环Flat Files

操作类似循环Excel. Flat文件源与OLEDB Destination如有CodePage不相符,需设置

Destination->Advanced Editor->AlwaysUseDefaultCodePage->True.

 

2.         Sequence Container ,  Execute SQL ,  variables,  Package Configurations,  Transaction

a.       Sequence Container可用作使IS设计模块化。

b.       Sequence Container可用作事务处理。开启事务->TransactionOption->Required。所有组件默认TransactionOptionSupported支持,即参与父级开启的事务。

c.       Package Configurations配置包信息,eg: 存储变量值。当使用变量时,首先读取包配置,赋值给变量。

d.       Execute SQL:使用变量,SQL语句中用“?”代替变量,Parameter Mapping中设置对应变量,Parameter Name设置为012…分别对应第1个问号,第2个问号。。。

 

3.         Analysis Services

a.       Analysis Services Process->Process Analysis ServicesDataBase, 可以单独process cube或某个维度。我没找到能Process 本地SSAS工程。

b.       Analysis Services Execute DDL->xmla语言,也是连接Analysis ServicesDataBase

如:<Batch xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">

 <Parallel>

    <Process xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

      <Object>

        <DatabaseID>Analysis Services Tutorial</DatabaseID>

      </Object>

      <Type>ProcessFull</Type>

      <WriteBackTableCreation>UseExisting</WriteBackTableCreation>

    </Process>

 </Parallel>

</Batch>

 

4Bulk Insert Task

         a. Flat File插入DB

 

5Execute Package Task

Package设置,Connection->帐号设置为能登入Integration Services. DataBase可以不选或为任意DBPackageIntegration Services.Stored Packages.MSDB.Maintenance PlansPackage.

 

6. Execute Process Task

执行本地可执行文件.exe,  .com,  .bat,  .cmd

 

7. Execute SQL Task: 执行SQL语句或存储过程。

设置连接

a. 设置SQL来源类型,直接输入/文件/变量

b. 输入输出参数用“?”代替,参数对应在Parameter Mapping中设置。

c. 结果集,None/Row/Set/xml

 

8. File System Task

操作文件目录,可以删除文件,复制文件,重命名文件等。

 

9. Send Mail Task

配置SMTP Server. mail.

 

10. Transfer Database Task

可实现在线复制或离线移动BD

测试中,在线复制,当table中的sql_variant数据类型时,复制数据时报错。

 

11. Transfer SQL Server Objects Task

COPY DB Objects, 可以设置当目标存在时,先删除。

测试中,当Table有分区的时候,COPY表结构报错.

 

12. Web Service

a. 设置Http Connection. New Connection->Http->Server URL:

http://localhost:3992/WebService/Service.asmx

b. 设置WSDL File路经

c. 设置Input->Web Service方法

d. 设置 Output->用变量接收

 

13. Script Task

可接收变量,编程

 

14. WMI Task

WMI Data Reader Task: WQL 查询返回应用程序日志事件中的项或查询返回操作系统的快速修补工程 (QFE) 更新列表等,至Flat File中。(Excel不能接收)

如:SELECT * FROM Win32_NTLogEvent WHERE LogFile = 'Application' AND

(SourceName='SQLISService' OR SourceName='SQLISPackage') AND TimeGenerated > '20050117'

 

WMI Event Watcher: 查询监视 CPU 使用率超过 40% 的通知等,可使用于条件判断。

如:SELECT * from __InstanceModificationEvent WITHIN 2 WHERE TargetInstance ISA

'Win32_Processor' and TargetInstance.LoadPercentage > 40

 

15. Maintenance Plan Task.

(16) Notify Operator Task: MailSQL操作员。类似有Send Mail Task.

封装EXECUTE msdb.dbo.sp_notify_operator。配置好DataBase Mail后,该组件我没找到在哪里设置Profile Name

 

16. 控制流的路径

可以编辑路径Constraint

(1) Constraint options: Evaluation operation: 选择操作可为约束、表达式、或约束与表达式的组合。约束可选值为成功、失败或完成。表达式,如:@i==1

(2) Multiple constraints: Logical AND, Logical OR, 指一个控件接收多方源路径时,这些源路径的关系约束,如:可设置当源中的某个成功,即运行该目标组件。

 

Data Flow

17.数据流的路径

可设置Data Viewers, 使用该方法可实时查看经过的数据。

 

18. Data Flow Task

(1) Audit: 增加一些Package信息列,如Machine Name, User Name

(2) Character Map: 字符转换,转换大小写等。

(3) Conditional Split: 分流,根据Condition条件把数据分组,编写condition时,可使用右上方的Functions.

(4) Copy Column: 复制列

(5) Derived Column: 可对列进行表达式操作,然后覆盖当前列或作为新列输出。

(6) Data Conversion: 数据类型转换。因为Data Flow 路径连接的数据类型要一致。

(7) Sort: 排序,常用于Merge Join之前。

(8) Lookup: 查找。有类似于Merge Join,设置当查找不到数据时,组件失败或重定向行或忽略错误,忽略错误可能会造成数据丢失。两个数据源连接的columns数据类型要一致。

(9) OLE DB Command: 对数据流中的每一行运行一条 SQL 语句。Column Mappings,配置?号对应参数。

(10) Lookup + OLE DB Command: 可用于处理缓慢变化维度,Lookup成功数据行用OLE DB

Command更新数据库,Lookup不成功的行插入数据库。也可用Merge Join + OLE DB

Command处理缓慢变化维度.

(11) Slowly Changing Dimension: 渐变维度转换协调数据仓库维度表中的记录更新与插入。设置Key Type, 需设置Business key, 用于Joincolumns, 其他为‘Not a key column’即需要更新数据的columns.

(12) Merge Join: 要求输入列一定是经过排序的。可使用下列方法之一对此数据进行排序:

a. 在源中,在用于加载数据的语句中使用 ORDER BY 子句。

b. 在数据流中,在合并转换或合并联接转换之前插入一个排序转换。

必须为向合并转换和合并联接转换提供数据的源或上游转换设置两个重要的排序属性:

数据源中输出的 IsSorted 属性,指示数据是否已排序。此属性必须设置为 True。将 IsSorted 属性的值设置为 True 时不会对数据进行排序。此属性仅向下游组件提示数据之前已经过排序。

输出列的 SortKeyPosition 属性,指示单个列是否已排序、其排序顺序以及多个列的排序顺序。必须为已排序数据的每一列设置此属性。

如果使用排序转换对数据进行排序,则排序转换将按合并转换或合并联接转换的要求设置这两个属性。即,排序转换将其输出的 IsSorted 属性设置为 True,并设置其输出列的 SortKeyPosition 属性。

(13) Merge: 将两个排序后的数据集合并为一个数据集。合并转换与 Union All 转换类似。在下列情况下需使用 Union All 转换:a.转换输入未排序。b.合并的输出无需排序。c.转换的输入超过两个。

(14) Union All: 要求输入流对应列的数据类型一致。

(15) 抽样

Percentage Sampling: 通过选择转换输入行的百分比来创建样本数据集

Row Sampling: 用于获取输入数据集的随机选择子集

(16) Aggregate: group by某些列,对另外一些列作聚合操作。

(17) Pivot: 设置输入列的 PivotUsage 属性,以指定每列在透视过程中承担的角色。

PivotUsage 的有效值是 012 3

0表示此列未经透视,将列值传递到转换输出。

1表示此列为设置键的一部分,该设置键将一行或多行标识为一个集的组成部分。将所有具有同一设置键的输入行组合到一个输出行。

2表示列为透视列。从每个列值至少创建一列。

3表示将来自此列的值放入作为透视的结果而创建的列中。

(18) Unpivot: 将需要Unpivot的列选上,未经透视列只需Pass Through. Destination设置

Unpivot归一列的列名,Pivot Key Value即为该列Unpivot后的值,Pivot Key value column name设置透视结果列名。

(19) Row Count: Three steps are needed to configure your row count.

1. Create a variable of integer type (the default, so that's easy) at a scope where you can see it from your DataFlow task.

2. Now add your Row Count component to the DataFlow at the point in the process where you would like to count rows.

3. Edit the Row Count component and set its VariableName property to the name of the variable you created.

When you execute the Data Flow, the number of rows which pass through the Row Count component are written to the named variable. However, it's important to note that the variable value does not change until the Data Flow has completed.

This is the same for all SSIS package variables referenced in the Data Flow,

even when using the Script component, the values are locked when execution of the Data Flow starts and they are only updated at the end.

(20) Multicast: 多播转换,即将数据复制多份。

(21) Fuzzy: 执行数据清理任务

a. Fuzzy Grouping: 模糊匹配对具有相似值的行进行分组。近似匹配数据的方法基于用户指定的相似性得分。

b. Fuzzy Lookup: 模糊查找转换使用模糊匹配返回引用表中一个或多个接近的匹配项。

(22) Export Column: 读取数据流中的数据,并将数据插入到文件中。指定数据列和包含要向其写入数据的文件的路径的列。从包含文本数据或图像数据的输入列的列表中进行选择。数据源的所有行都应包含“提取列”和“文件路径列”的定义。从包含文件路径和文件名的输入列的列表中进行选择。

(23) Term Extraction: 从转换输入列的文本中提取字词,然后将这些字词写入转换输出列。

Term Lookup: 从转换输入列的文本中提取的字词与引用表中的字词进行匹配,然后计算出查找表中的字词在输入数据集中出现的次数,并将计数与引用表中的此字词一并写入转换输出的列中。

posted @ 2010-04-14 16:41 AmyQiu 阅读(185) 评论(0) 编辑

2010年4月7日

1说明:查询在所有州都销售的品牌。

with set [SoldInUSA] as

'Filter([Product].[Brand Name].Members, Not IsEmpty( ([USA], [Unit Sales]) ))'

member [Measures].[SoldInState] as

'iif( IsEmpty(([Product].CurrentMember, [Unit Sales], [Customers].CurrentMember)), "No","Yes" )'

select [USA].children on COLUMNS,

[SoldInUSA] on ROWS

from Sales

where ([SoldInState])

 

2说明:查询销售前名的产品类别

select {[Unit Sales]} on COLUMNS,

TopCount( [Product].[Product Category].Members, 10, ([Unit Sales]) ) on ROWS

from Sales

 

3说明:在过去三个季度里都存在销售量的商品销售记录

with set [LastQuarter] as

'Tail(Filter([Time].[Quarter].Members, Not IsEmpty([Time].CurrentMember)),1)'

set [Last3Quarters] as

'[LastQuarter].item(0).item(0).Lag(2) : [LastQuarter].item(0).item(0)'

select [Last3Quarters] on COLUMNS,

Non Empty Union(Descendants( [Food], [Product].[Brand Name] ), Descendants( [Drink],

[Product].[Brand Name] )) on ROWS

from Sales

 

4说明:查出最近个月销售趋势最好的前个商品及其各自销售量

with set [TenBest] as

'TopCount( [Product].[Brand Name].Members, 10, [Unit Sales] )'

set [LastMonth] as

'Tail(Filter([Time].[Month].Members, Not IsEmpty([Time].CurrentMember)),1)'

set [Last6Months] as

'[LastMonth].item(0).item(0).Lag(6) : [LastMonth].item(0).item(0)'

select [Last6Months] on COLUMNS,

[TenBest] on ROWS

from Sales

 

5说明:找出组成销售额%的商品销售及其记录;

select {[Unit Sales]} on COLUMNS,

TopPercent([Product].[Brand Name].Members, 80, [Unit Sales]) on ROWS

from Sales

 

6说明:按销售量排序,找出组成%销售量的商品销售记录

select {[Unit Sales]} on COLUMNS,

Non Empty BottomPercent([Product].[Brand Name].Members, 20, [Unit Sales]) on ROWS

from Sales

 

7说明:查出销售量最好的前名店和每个店的前个顾客及其销售记录

select {[Unit Sales]} on COLUMNS,

Generate( TopCount([Store].[Store Name].Members, 5, [Unit Sales]),

{ [Store].CurrentMember } * TopCount( [Customers].[Name].Members, 5, ([Unit Sales],

[Store].CurrentMember) ) ) on ROWS

from Sales

 

8说明:查出每种品牌前名产品的销售记录,以及各自分别占所在品牌的百分比

with member [Measures].[PercTotalSales] as

' Sum( TopCount([Product].CurrentMember.Children, 2, [Unit Sales]), [Unit Sales] )

/([Product].CurrentMember, [Unit Sales])',

FORMAT_STRING = '##.0%'

select [Store].[(All)].Members on COLUMNS,

Generate( [Product].[Brand Name].Members,

 Union(

    TopCount( [Product].CurrentMember.Children, 2, [Unit Sales] ) * {[Unit Sales]},

    { ([Product].CurrentMember, [PercTotalSales]) }

     )

) on ROWS

from Sales

 

9说明:查出个季度中,每个时期销售量在后%的产品销售量,并显示为粗体

with set [LastQuarter] as

'Tail(Filter([Time].[Quarter].Members, Not IsEmpty([Time].CurrentMember)))'

set [Last4Quarters] as

'[LastQuarter].item(0).item(0).Lag(3) : [LastQuarter].item(0).item(0)'

member [Measures].[HLUnit Sales] as '[Unit Sales]',

 FONT_FLAGS = 'iif( Count(

       Intersect( BottomPercent( [Product].[Brand Name].Members, 10, ([Unit Sales]) ),

    {[Product].CurrentMember})

       ) = 0, 0, 1)'

select [Last4Quarters] on COLUMNS,

[Product].[Brand Name].Members on ROWS

from Sales

where ([HLUnit Sales])

cell properties VALUE, FORMATTED_VALUE, FONT_FLAGS

 

10说明:比较具有相同相对位置的时间点的销售量,例如今年月和去年月;

with set [PromoMonths] as

'Filter([Time].[Month].Members, Not IsEmpty( ([Unit Sales], [Double Your Savings]) ) )'

set [PromoRange] as

'Head( [PromoMonths] ).item(0).item(0) : Tail( [PromoMonths] ).item(0).item(0)'

member [Measures].[Uplift] as '([Unit Sales], [Double Your Savings])'

member [Measures].[This Quarter] as '[Unit Sales]'

member [Measures].[Last Quarter] as '( ParallelPeriod( [Time].[Quarter] ), [Unit Sales] )'

member [Measures].[Growth] as ' [This Quarter] - [Last Quarter]'

select [PromoRange] on Columns,

{ [This Quarter], [Last Quarter], [Growth], [Uplift] } on Rows

from [Sales]

 

11说明:查出利润率在%以上的产品及销售记录

with member [Measures].[SalesRatio] as '([Store Sales] - [Store Cost]) / [Store Cost]', 

FORMAT_STRING = '##%'

select { [Store Sales], [Store Cost], [SalesRatio] } on COLUMNS,

Filter( [Product].[Brand Name].Members, [SalesRatio] > 1.60 ) on ROWS

from Sales

 

12说明:找出最近一季度比前一季度销售量增长幅度大于%的产品销售记录

with set [LastQuarter] as 'Tail(Filter([Time].[Quarter].Members, Not 

IsEmpty([Time].CurrentMember)))'

member [Measures].[CurrQSales] as '([LastQuarter].item(0).item(0), [Unit Sales])'

member [Measures].[PrevQSales] as '([LastQuarter].item(0).item(0).PrevMember, [Unit Sales])'

member [Measures].[Growth] as ' ([CurrQSales] - [PrevQSales]) / [PrevQSales]', 

FORMAT_STRING='##%'

select { [PrevQSales], [CurrQSales], [Growth] } on COLUMNS,

Filter( [Product].[Brand Name].Members, [Growth] > 0.5 ) on ROWS

from Sales

 

13说明:找出销售额在前、后名的产品销售记录,并列出总排名,就是找出销售情况最好和最坏的产品

with set [OrderedBrands] as 'Order( [Product].[Brand Name].Members, [Unit Sales], BDESC )'

member [Measures].[Brand Rank] as 'Rank( [Product].CurrentMember, [OrderedBrands] )'

select {[Brand Rank], [Unit Sales]} on COLUMNS,

Union( Head( [OrderedBrands], 10 ), Tail( [OrderedBrands], 10 ) ) on ROWS

from Sales

 

14说明:比较一下产品销售趋势,没什么用

with set [LastQuarter] as 'Tail(Filter([Time].[Quarter].Members, Not 

IsEmpty([Time].CurrentMember)))'

set [Last4Quarters] as ' [LastQuarter].item(0).item(0).Lag(3) : [LastQuarter].item(0).item(0)'

member [Measures].[GroupAvg] as 'Avg([Product].CurrentMember.Siblings, [Unit Sales])'

member [Measures].[AllAvg] as 'Avg( [Product].[Product Name].Members, [Unit Sales])'

select [Last4Quarters] on COLUMNS,

{ [Unit Sales], [GroupAvg], [AllAvg] } on ROWS

from Sales

where ([Ebony Plums])

 

15说明:查出一定条件下的前名产品的销售记录,例如销售量在到之间的

with set [LastQuarter] as 'Tail(Filter([Time].[Quarter].Members, Not 

IsEmpty([Time].CurrentMember)))'

set [Last4Quarters] as ' [LastQuarter].item(0).item(0).Lag(3) : [LastQuarter].item(0).item(0)'

member [Measures].[GroupAvg] as 'Avg([Product].CurrentMember.Siblings, [Unit Sales])'

member [Measures].[AllAvg] as 'Avg( [Product].[Product Name].Members, [Unit Sales])'

member [measures].[abc] as '[Product].CurrentMember.uniquename'

select [Last4Quarters] on COLUMNS,

{ [Unit Sales], [GroupAvg], [AllAvg],[measures].[abc] } on ROWS

from Sales

where ([Ebony Plums])

 

with set [LastQuarter] as 'Tail(Filter([Time].[Quarter].Members, Not 

IsEmpty([Time].CurrentMember)))'

set [Last4Quarters] as ' [LastQuarter].item(0).item(0).Lag(3) : [LastQuarter].item(0).item(0)'

member [Measures].[GroupAvg] as 'Avg([Product].CurrentMember.Siblings, [Unit Sales])'

member [Measures].[AllAvg] as 'Avg( [Product].[Product Name].Members, [Unit Sales])'

member [measures].[abc] as '[Product].CurrentMember.uniquename'

member [measures].[abcd] as 'lookupcube("Trained Cube","MemberToStr([Customers].[All Customers].[Canada])")'

select [Last4Quarters] on COLUMNS,

{ [Unit Sales], [GroupAvg], [AllAvg],[measures].[abc] ,[measures].[abcd] } on ROWS

from Sales

where ([Ebony Plums])

posted @ 2010-04-07 16:34 AmyQiu 阅读(286) 评论(0) 编辑

MDX涉及的一些概念

MDX主要涉及到了如下概念:

成员、成员名和成员键、计算成员、成员函数、元组、元组函数、元组和维数、集合、聚合函数、集合和维数、命名集

成员:  
  成员是代表维度中一次或多次数据出现的项。请把维度中的成员看作基础数据库中的一个或多个记录,其该列内的值归入该分类。成员是描述多维数据集中的单元数据时的最低参照层次。  
  例如,下面的关系图加入阴影以表示"时间.[下半年].[第三季度]"成员  
  注意“[”和“]”   
    

成员名和成员键:  
  可用其成员名或其成员键引用某个成员。上一示例中用其成员名"第四季度"引用"时间"维度中的成员。但是,在具有非唯一成员名的维度中成员名可以是"重复"的("不甚理解"),或者在可更改维度中成员名是可更改的。    
  引用成员的"可选方法"是引用成员键。成员键由维度用来专门标识给定成员。在   MDX   中使用"和"号   (&)   字符将成员键与成员名区别开,如下面的示例所示:    
  [Time].[2nd   half].&[Q4]  
  在这种情况下,使用"第四季度"成员的成员键   Q4。引用成员键确保在可更改维度中以及在具有非唯一成员名的维度中成员的正确标识。  
  "和"(&)   号字符可用来表示任何   MDX   表达式中的成员键引用。   
    

计算成员:  
  还可将成员创建为   MDX   查询的一部分,以使返回的数据基于评估表达式,而不是要查询的多维数据集中所存储的数据。这些成员称为计算成员,它们提供大量的   MDX   的功能和灵活性。在   MDX   查询中用   WITH   关键字定义计算成员。例如,如果想要通过增加"包"度量值现有值的   10%   来对所有包裹进行预测估算,可以仅创建一个提供此信息的计算成员,并象使用多维数据集中的任何其它成员那样使用它,如以下示例所示。  
  WITH   MEMBER   [Measures].[PackagesForecast]   AS  
  '[Measures].[Packages]   *   1.1'   
    

成员函数:  
  MDX   提供许多函数来从其它   MDX   实体(如维度和级别)检索成员,因此对成员的显式引用并非总是必要。  
  Time.[1st   half]等价于  
  Time.FirstChild   
    
元组:  
  元组用于定义来自多维数据集的数据切片;它由来自一个或多个维度的单个成员的有序集合组成。元组用于标识来自多维数据集的特定多维数据块;由来自多维数据集中各个维度的一个成员组成的元组完全描述单元值。换言之,元组是一种成员向量;请把元组看作基础数据库中的一个或多个记录,其这些列内的值归入这些分类。一系列关系图给出了元组的各种类型。  
  “帮助中有两个图,画得很好,看看很直观!”  
  在   MDX   中,元组根据其复杂性依照语法进行构造。如果它仅由来自单个维度的一个成员组成(通常称作简单元组),则以下语法是可接受的。  
  Time.[2nd   half]  
  如果它由来自不止一个维度的成员组成,则元组所表示的成员必须括在圆括号内,如以下示例所示。  
  (Time.[2nd   half],   Route.nonground.air)  
  由单个成员组成的元组也可括在圆括号内,但这不是必需的。元组常常编组成集合,以便在   MDX   查询中使用。   
    
元组函数:  
  有一些返回元组的   MDX   函数,它们可在任何接受元组的地方使用。  
   
  元组和“维数”!:  
  元组可包括多个维度中的成员,也可包括来自同一个维度的多个成员。术语维数用来表示元组中成员所描述的维度。顺序在元组的维数中起一定作用,并可影响集合内元组的使用。   
    

集合:  
  集合是零个、一个或多个元组的有序集合。集合最常用于在   MDX   查询中定义轴维度和切片器维度,并且同样可能只具有单个元组或可能在某些情况下为空。下面的示例显示具有两个元组的集合:  
  {   (Time.[1st   half],   Route.nonground.air),   (Time.[2nd   half],   Route.nonground.sea)   }  
  一个集合可包含同一个元组不止一次的出现。下面的集合是可接受的:  
  {   Time.[2nd   half],   Time.[2nd   half]   }  
  集合指以元组表示的一组成员组合,或指集合中的元组所代表的单元中的值,视集合使用的上下文而定。  
  在   MDX   语法中,元组用花括号括起来以构造集合。  
  重要!!!     由单个元组组成的集合不是元组;MDX   将其解释为集合。某些   MDX   函数接受元组作为参数,而如果传递单个元组集合,则会产生错误。元组和单个元组集合不可互换。   
    
    
    
聚合函数:  
  显式键入元组并将它们括在花括号内并不是检索集合的唯一方法。MDX   支持许多种返回集合的函数。  
  冒号运算符使使您得以使用成员的自然顺序创建集合。例如下面的集合:  
  {[1st   quarter]:[4th   quarter]}  
  检索与下面的集合相同的成员集合:  
  {[1st   quarter],   [2nd   quarter],   [3rd   quarter],   [4th   quarter]}  
  冒号运算符是一种包含函数;冒号运算符两边的成员都将包含在结果集内。  
  其它返回集合的   MDX   函数可单独使用,也可作为以逗号分隔的成员列表的一部分使用。例如,下面所有的   MDX   表达式都是有效的:  
  {Time.Children}  
  {Time.Children,   Route.nonground.air}  
  {Time.Children,   Route.nonground.air,   Source.Children}   
    
集合和维数:  
  与元组一样,集合也有维数。由于集合由元组组成,因此集合的维数用其中各个元组的维数表达。正因为如此,所以集合中的元组必须具有相同的维数。换言之,下面的示例不能作为集合使用:  
  {   (Time.[2nd   half],   Route.nonground.air),   (Route.nonground.air,   Time.[2nd   half])   }  
  集合内元组的顺序非常重要;例如,它影响轴维度内的嵌套顺序。第一个元组表示第一个或最外部维度,第二个元组表示次外部维度,以此类推。   
    
命名集:  
  命名集是为其创建了别名的集合。命名集最常用在复杂的   MDX   查询中,以使这些查询更易阅读和便于维护。

posted @ 2010-04-07 16:21 AmyQiu 阅读(109) 评论(0) 编辑

SQL Server Analysis Service中Cube的结构

SSAS中Cube的结构
在SSAS(SQL Server Analysis Services)中构建Cube和编写MDX的时候,我们很容易被一些名词弄糊涂,比如:Dimension(维度),Measures Dimension(度量维度),Measure(度量),Hierarchy(层次结构),Attribute hierarchy(属性层次结构),Level(级别),Cell(单元),Member(成员),Member Property(成员属性),Set(集),Turple(元组)等等。要想弄清楚这些名词,就必须理解Cube的结构。

上述名词的解释详见:http://msdn2.microsoft.com/en-us/library/ms144884.aspx

Cube、Dimension和Measure
Cube就象一个坐标系,每一个Dimension代表一个坐标轴,要想得到一个点,就必须在每一个坐标轴上取的一个值,而这个点就是Cube中的Cell。见下图(来源于http://msdn2.microsoft.com/zh-cn/library/ms144884.aspx):


上图很好的说明了Cube、Dimension、Measure之间的关系。这里需要注意的是:其实Measure也属于一个维度,即Measures Dimension。所有的Measure构成了Measures Dimension,这个维度的只有一个Hierarchy,而且这个Hierarchy只有一个层次(Level)。

Hierarchy、Level和Memeber
在上节的图中,每个Dimension只有一个Hierarchy,而在实际的环境中,一个Dimension往往有很多Hierarchy。因此,上一小节中关于“Cube就象一个坐标系,每一个Dimension代表一个坐标轴”这句话其实不够准确,准确的说应该是每一个Hierarchy代表了一个坐标轴,而Hierarchy中每一个Member代表了坐标轴上的一个值。下图以时间维度为例展示了Dimension的内部结构。


此外,我们需要说明的是:

1) 上图中说明的是一般Dimension的结构,在实际的模型中,其实可以做很多自定义的工作。比如:我们可以修改Hierarchy的默认Member。 

2) 一般情况下,SSAS中Hierarchy的默认Member是All(在你的模型中,可能叫其他名称)。换句话说在MDX中[时间].[财政]等价于[时间].[财政].[All],[时间].[财政].Children等价于[时间].[财政].[All].Children。

3) Dimension_Name.Hierarchy_Name.Level_Name等价于Dimension_Name.Hierarchy_Name.Level_Name.Members。比如:[时间].[财政].[半年]等价于[时间].[财政].[半年].Members。Level的Members是该级别的所有元素(对于[时间].[财政].[半年].Members={[上半年],[下半年],[上半年],[下半年]},其中前两个是2001年下的,后两个属于2002年),而Hierarchy的Members包含了该Hierarchy下所有的内容。

4) 当且仅当一个Dimension下只有一个Hierarchy,则Dimension_Name等价于Dimension_Name.Hierarchy_Name纬度。比方说:时间维度只有一个财务Hierarchy,则[时间]等价[时间].[财务]。

5) Attribute Hierarchy中Members的层次是两层(MSDN的说法更加准确,这里简化了一些):第一层:All,第二层:叶子节点。也就是说它和多层的Hierarchy相比,两者结构完全相同,这是统一维度模型(Unified Dimensional Model)一个方面的体现。

      注意:采用Attribute Hierarchy能够使编写MDX更加容易,但同时也增加了Cube的容量,加大了Cells的个数,对性能有负面影响。因此,在建模的时候,我们可以把一些Attribute Hierarchy的AttributeHierarchyEnabled属性设置成False,同时在编写MDX时,以Member Property的方式来引用,这样可以在满足需求的前提下提高性能。

6) Measures Dimension是一个特殊的维度,它的Members中没有All这个成员,它的默认Member可以在建模时指定。

7)对于一般的维度,其第一层Level的默认是“(All)”。

Turple和Set
如果说Cube好像一个坐标系,那么Turple、Set的关系就好比点和面的关系。Turple由Cube中每个Hierarchy的一个Member组成。由于Hierarchy的个数非常多,所以一般不可能在Turple表达式中把所有的Member都明确指定,故此,为了简化开发,所有没有明确指定Member的Hierarchy,用该Hierarchy的默认Member代替。也就是说:([时间].[财政].[2001].[上半年]) 等价于([时间].[财政].[2001].[上半年],[时间].[日历].[All])。另外我们需要注意的:

1) 有的说法认为:Turple是“Cube 上的一个子集(不断开的子Cube),这个看法是不准确的,因为Turple只是一个点,不是面,它仅仅由每个Hierarchy的一个Member组成的。

2) 外面()起来的表达式不一定是Turple。比如:([时间].[财政].[半年].Members,[时间].[日历].[2001].[上半年])就不是一个Turple,而是一个Set,其原因在于,Turple是点,它仅仅由每个Hierarchy的一个Member组成,如果在任何一个Hierarchy上有两个成员,则其就变成Set了。

      注意:([时间].[财政].[半年].Members,[时间].[日历].[2001].[上半年])等价于Crossjoin([时间].[财政].[半年].Members,[时间].[日历].[2001].[上半年])或{[时间].[财政].[半年].Members}*{[时间].[日历].[2001].[上半年]},在SSAS的MDX中,我们可以在()中定义多个用逗号分隔开的表达式,编译器会进行分析,如果发现是Set的话,就把它转化成多个Set相乘的形式。

3) Set中的Turple可以重复。比如:{[时间].[日历].[2001].[上半年],[时间].[日历].[2001].[上半年]}并不等于{[时间].[日历].[2001].[上半年]},因为前者有两个Turple,后者只有一个。

4) SSAS能够根据上下文的需要,自动把Turple变成Set,单个Member变成Turple,多个Member变成Set。这也是我们常常混淆Turple和Set的原因。详细的例子如下:

a)上下文需要Set时,([时间].[日历].[2001].[上半年])自动转化成{[时间].[日历].[2001].[上半年]}。
b)上下文需要Turple时,[时间].[日历].[2001].[上半年]自动转化成([时间].[日历].[2001].[上半年])。
c)上下文需要Set时,[时间].[日历].[2001].Children自动转化成{[时间].[日历].[2001].Children}。

总结
总体来看,SSAS中的Cube的内部结构非常的清晰,在实际开发中,只要多注意一下默认的一些转化,使用起来是很容易的。

posted @ 2010-04-07 16:21 AmyQiu 阅读(94) 评论(0) 编辑

摘要: MDX中函数的应用[代码]阅读全文

posted @ 2010-04-07 16:20 AmyQiu 阅读(209) 评论(0) 编辑

MDX优化Set操作—SUM中的CrossJoin

1. 优化Set操作的关键在于:把大的SET操作变成小的SET操作。
2. 由于CrossJoin代价(CPU、内存)巨大,所以最好用其他操作代替CrossJoin操作。

SUM中的CrossJoin
作者认为:要避免SUM一个包含多个CrossJoin的Set,你可以用其他的操作(比如嵌套SUM)进行替换。据此,我测试了一下
两组语句:
WITH MEMBER MEASURES.ABC AS 
Sum (
    CrossJoin (
        Descendants (
            
[Customer].[Customer Geography].CurrentMember,
            
[Customer].[Customer Geography].[State-Province]
        ),
        Crossjoin (
            Descendants (
                
[Date].[Calendar].CurrentMember,
                
[Date].[Calendar].[Date]
            ),
            Descendants (
                
[Product].[Product Categories].CurrentMember,
                
[Product].[Product Categories].[Product Name]
            )
        )
    )
    ,[Measures].[Internet Sales Amount]-[Measures].[Internet Tax Amount]

  )
SELECT MEASURES.ABC ON 0 ,
[Customer].[Customer Geography].[Country].Members *  
[Date].[Calendar].[Calendar Year].MEMBERS * 
[Product].[Product Categories].[Category].MEMBERS
ON 1
FROM [Adventure Works]
WITH MEMBER MEASURES.ABC AS 
Sum (
    Descendants (
        
[Customer].[Customer Geography].CurrentMember,
        
[Customer].[Customer Geography].[State-Province]
    ),        
    
SUM(
        Descendants (
            
[Product].[Product Categories].CurrentMember,
            
[Product].[Product Categories].[Product Name]
        ),
        
SUM(
            Descendants (
                
[Date].[Calendar].CurrentMember,
                
[Date].[Calendar].[Date]
            )        
            ,[Measures].[Internet Sales Amount]-[Measures].[Internet Tax Amount]

        )                
    )    
)
SELECT MEASURES.ABC ON 0 ,
{
[Customer].[Customer Geography].[Country].Members} *  
[Date].[Calendar].[Calendar Year].Members * 
[Product].[Product Categories].[Category].Members
ON 1
FROM [Adventure Works]

以上语句中,作者认为第一个语句慢于第二个语句(理由是嵌套的SUM每次操作的SET更小),可实际的结果(测了10次)恰恰相反,第一个语句平均花费的时间51.654秒,而第二个语句平均花费的时间在55.912秒,这是何故呢?此外,书中认为在第二个语句的嵌套SUM中,如果把大的Set放在里面,这样会快一些。也就是说下面的语句比上面第二个语句要慢5%-20%。

WITH MEMBER MEASURES.ABC AS 
Sum (
    Descendants (
        
[Date].[Calendar].CurrentMember,
        
[Date].[Calendar].[Date]
    ),
    
SUM(
        Descendants (
            
[Product].[Product Categories].CurrentMember,
            
[Product].[Product Categories].[Product Name]
        ),      
        
SUM(
        Descendants (
            
[Customer].[Customer Geography].CurrentMember,
            
[Customer].[Customer Geography].[State-Province]
        ),    
            
[Measures].[Internet Sales Amount]-[Measures].[Internet Tax Amount]        )                
    )    
)
SELECT MEASURES.ABC ON 0 ,
{
[Customer].[Customer Geography].[Country].Members} *  
[Date].[Calendar].[Calendar Year].Members * 
[Product].[Product Categories].[Category].Members
ON 1
FROM [Adventure Works]


 

以上测试语句中,关于[Date].[Calendar].[Date]的Set其Turple个数在365左右,关于[Product].[Product Categories].[Product Name]的Set其Turple个数在几十个左右,而关于[Customer].[Customer Geography].[State-Province]的Set其成员个数大多在十几个左右

经过测试发现上面这条语句平均时间在57.858秒。也就是说,测试结果和书中的观点是一致的,只是幅度没有那么大。此外,我还尝试了一下这样的写法。

WITH 
MEMBER MEASURES.ABC 
AS 
Sum (
    
    CrossJoin (
        Descendants (
            
[Customer].[Customer Geography].CurrentMember,
            
[Customer].[Customer Geography].[State-Province]
        ),
        Crossjoin (
            Descendants (
                
[Date].[Calendar].CurrentMember,
                
[Date].[Calendar].[Date]
            ),
            Descendants (
                
[Product].[Product Categories].CurrentMember,
                
[Product].[Product Categories].[Product Name]
            )
        )
    ) 
AS MYABC

    ,
[Measures].[Internet Sales Amount]
)
-
Sum (    
    MYABC
    ,
[Measures].[Internet Tax Amount]
)
SELECT MEASURES.ABC ON 0 ,
[Customer].[Customer Geography].[Country].Members *  
[Date].[Calendar].[Calendar Year].MEMBERS * 
[Product].[Product Categories].[Category].MEMBERS
ON 1
FROM [Adventure Works]

上面语句的不同之处在于,把要计算的内容分散开来了,令人惊异的是,这个语句只要2-3秒种就能运行完成。


总结
由上面两次测试我们可以得出以下结论:
1)SUM中的CrossJoin并不一定会降低速度,书中的观点可能是错误的。看来MDX解析器对CrossJoin有很多有优化,在上面的测试中CrossJoin比嵌套的SUM要快8%左右。
2)嵌套SUM中,把大的SET放在里层的SUM中,这样速度能够快一些。上面的测试中,把小的Set放在里层比把大的Set放在里层慢3.5%。
3)在做SUM等统计计算时,如果能够把计算项分解到每个单独的Measure,这个时候性能提升非常明显,速度将会大大提高。上面的测试中,速度提高了20多倍。

posted @ 2010-04-07 16:19 AmyQiu 阅读(91) 评论(0) 编辑

MDX中的Where vs. Subselect/Subcube

Where和Subselect/Subcube在MDX都是经常使用的语句,有的时候它们能起到相同的效果,而有的时候却又不能,这究竟是什么原因呢?本文将尝试就这些情况作一些分析(不对之处,欢迎大家及时指出)。以下MDX语句可以在SSAS(打了SP2补丁的版本)的示例库:Adventure Works中运行。

相同和不同
Where和Subselect/Subcube都能够限定计算的范围。请看下面的两个语句:
例一

SELECT
[Measures].[Internet Sales Amount]  ON 0,
[Date].[Calendar].[Calendar Year].Members ON 1
FROM [Adventure Works]
WHERE
[Product].[Product Categories].[Category].[Bikes]
SELECT
[Measures].[Internet Sales Amount] ON 0,
[Date].[Calendar].[Calendar Year].Members ON 1
FROM
(
    
SELECT [Product].[Product Categories].[Category].[Bikes] ON 0
    
FROM [Adventure Works]
)


以上两条语句能够返回相同的计算结果(见下图),这个结果中仅仅包括Bikes的销售金额。

但是,Where和Subselect/Subcube也有不同。请看下面的两个语句:
例二

WITH
MEMBER 
[Measures].[Category Name] AS
[Product].[Product Categories].CurrentMember.MemberValue
SELECT
[Measures].[Category Name]  ON 0,
[Date].[Calendar].[Calendar Year].Members ON 1
FROM [Adventure Works]
WHERE
{
    
[Product].[Product Categories].[Category].[Bikes]
}
WITH
MEMBER 
[Measures].[Category Name] AS
[Product].[Product Categories].CurrentMember.MemberValue
SELECT
[Measures].[Category Name]  ON 0,
[Date].[Calendar].[Calendar Year].Members ON 1
FROM
(
    
SELECT
    {
        
[Product].[Product Categories].[Category].[Bikes]
    } 
ON 0
    
FROM [Adventure Works]
)


它们的结果分别如下所见:


从上面的结果我们可以看出,Where语句的改变了[Product].[Product Categories]的当前Member,而Subselect/Subcube语句没有。

另外再看一种不同的情况:
例三

WITH
MEMBER 
[Measures].[Test1] AS
AGGREGATE(EXISTING 
[Product].[Product Categories].[Subcategory].Members,[Measures].[Internet Sales Amount])
MEMBER 
[Measures].[Test2] AS
AGGREGATE(
[Product].[Product Categories].[Subcategory].Members,[Measures].[Internet Sales Amount])
SELECT 
{
[Measures].[Test1][Measures].[Test2]ON 0,
[Date].[Calendar Year].MEMBERS ON 1
FROM [Adventure Works]
WHERE 
{
    
[Product].[Product Categories].[Subcategory].[Socks],
    
[Product].[Product Categories].[Subcategory].[Road Bikes]
}
WITH
MEMBER 
[Measures].[Test1] AS
AGGREGATE(EXISTING 
[Product].[Product Categories].[Subcategory].Members,[Measures].[Internet Sales Amount])
MEMBER 
[Measures].[Test2] AS
AGGREGATE(
[Product].[Product Categories].[Subcategory].Members,[Measures].[Internet Sales Amount])
SELECT 
{
[Measures].[Test1][Measures].[Test2]ON 0,
[Date].[Calendar Year].MEMBERS ON 1
FROM
(
    
SELECT
    {
        
[Product].[Product Categories].[Subcategory].[Socks],
        
[Product].[Product Categories].[Subcategory].[Road Bikes]
    } 
on 0
    
FROM [Adventure Works]
)

它们的结果分别如下:


上面的结果中,[Measures].[Test1]代表当前Context的销售数量,而[Measures].[Test2]代表了总的销售数量,我们知道Existing函数强制指定的Set在当前的Context下进行计算(http://msdn2.microsoft.com/en-us/library/ms145541.aspx),Where情况下,使用Existing的[Measures].[Test1]一切正常,而Subselect/Subcube情况下,使用Existing的[Measures].[Test1]似乎没有发生任何作用,这是什么原因呢?

分析
以上的结果,无论是相同还是不同,都和Context有关系。

我们知道:每一个Set,Member,Turple都执行在一个特定的Context中。Subselect/Subcube设定了整个Cube的Context,而Where和所有Axis一起构成了Calculate Member当前Context。这些设定都影响了计算时的数据范围,这就是第一个例子中为何Subselect/Subcube和Where返回相同结果的原因。

接下来的例子中,Where情况下,能够返回希望的当前Member的原因在于:Where也是一个Axis,即常说的Silcer Axis,它和所有Axis设定了各个Attribute Hierarchy的当前的Member,而Subselect/Subcube对Attribute Hierarchy当前的Member没有做任何的改变。

对于第三个例子,在Subselect/Subcube情况使用Existing函数无效,说明Subselect/Subcube对当前的Context并无影响,这同时说明Subselect/Subcube和Where构成的Context并不完全相同,在不同情况下,可能采用不同的上下文。Existing失效并不妨碍Subselect/Subcube在计算时对数据范围的影响,这说明在作Cell计算(包括Calculate Member)的时候,Subselect/Subcube和Where的Context都会同时被采用(除了Context被替换的情况,就像第三个例子中的[Measures].[Test2][Product].[Product Categories].[Subcategory].Members替换了当前Context和Cube Context中[Product]维度的当前设置一样)。

总结
Where和Subselect/Subcube都能够限定计算的范围,它们的最大不同在于,Where和所有Axis一起还设置了各个Attribute Hierarchy的当前Member。

posted @ 2010-04-07 16:17 AmyQiu 阅读(81) 评论(0) 编辑

MDX中一些边界问题的处理

在编写MDX中,各种各样的边界情况存在,比如:Member不存在,被0除,或则某个部分在cube和维度中并不存在等等,本文对这些情况下如何来处理做了一个小结。以下MDX语句可以在SSAS的示例库:Adventure Works中运行。 
 

Member不存在的情况

在使用Member的一些函数(比如:Lag,Lead,ParalledPeriod等)的时候,我们经常需要得到当前的Member相对的一个Member(比如:前一个Member,父级Member,或去年同期的一个Member等等)。然而由于维度和Hierarchy中的Member是有限的,所以这个相对的Member有时并不存在。在这种情况下,我们可以用IIF判断这个Member是否为Null来解决这个问题(z)。比方说,计算过去6个月的平均销售额。

 

WITH MEMBER [Measures].[Last 6 Month Average Sales Amount] AS 
IIF (
    
[Date].[Calendar].CurrentMember.Lag(5IS NULL,
    
NULL,
    
Avg (
        {
[Date].[Calendar].CurrentMember.Lag(5): [Date].[Calendar].CurrentMember},
        
[Measures].[Internet Sales Amount]
    )
), FORMAT_STRING
="Currency"
SELECT 
{
[Measures].[Internet Sales Amount][Measures].[Last 6 Month Average Sales Amount]ON 0,
{
[Date].[Calendar].[Month].Members} ON 1
FROM [Adventure Works]


这里需要注意的是如果 Cell(也就是Turple)不存在也会发生和Member不存在的相同的问题,其实如果Member不存在,Cell是一定不存在的,所以“Member不存在的情况”也可以被看作是“Cell不存在的情况”。
 

Level错误的情况

有的时候,Axis中的Set包含了位于不同Level的Member,而且每个Level中对于Member的处理方式不同,这种情况下的处理也要小心。比方说,在计算每种产品和其所属SubCategory,Category的销售量的时候,对于产品我们需要显示其标准价格(List Price),我们可以象下面这样来写。

 

WITH MEMBER [Measures].[Product List Price] AS 
IIF (
    
[Product].[Product Categories].CurrentMember.Level IS [Product].[Product Categories].[Product Name],
    
[Product].[Product Categories].CurrentMember.Properties("List Price"),
    
NULL
)
SELECT 
{
[Measures].[Internet Sales Amount],[Measures].[Product List Price]ON 0,
{DESCENDANTS(
[Product].[Product Categories].[All][Product].[Product Categories].[Product Name], SELF_AND_BEFORE )} ON 1
FROM [Adventure Works]


被0除的情况
表达式出现0为除数的情况有两种:
1)Member, Cell(也就是Turple)不存在
SSAS中,如果Member, Cell不存在,其返回的值是0。比如:统计每个月每种产品的销售额时,但是有可能在某个月份某个产品由于没有卖出一个。这种情况下,上文已有说明。
2)Cell统计的值为0
比如:统计每个月份占所处季度利润百分比的时候,有可能该季度的利润为0。

无论是那种情况,对于被0除你可以用判断表达式是否为0来处理,像下面这样计算同期比:

 

WITH MEMBER [Measures].[Same Period Ratio] AS     
IIF (
    (    
        
[Measures].[Internet Sales Amount],
        ParallelPeriod(
            
[Date].[Calendar].[Calendar Year]
            
1
            
[Date].[Calendar].CurrentMember
        )
    ) 
= 0,
    
NULL,
    
[Measures].[Internet Sales Amount]/
    (    
        
[Measures].[Internet Sales Amount],
        ParallelPeriod(
            
[Date].[Calendar].[Calendar Year]
            
1
            
[Date].[Calendar].CurrentMember
        )
    ) 
- 1
), FORMAT_STRING
="Percent"
SELECT 
{
[Measures].[Internet Sales Amount][Measures].[Same Period Ratio]ON 0,
NON EMPTY{DESCENDANTS(
[Date].[Calendar].[All Periods][Date].[Calendar].[Month], SELF)} ON 1
FROM [Adventure Works]

 

posted @ 2010-04-07 16:14 AmyQiu 阅读(73) 评论(0) 编辑

MDX中常见的计算方法

 
本文介绍了一些经常编写的MDX语句的写法。以下MDX语句可以在SSAS的示例库:Adventure Works中运行。

例子模型
以下的MDX中用到的Hierarchy如下:

百分比
1)某个子项占总体的百分比。比如:每种Product的销售额占所有Product销售额的百分比。
WITH MEMBER [Measures].[Sale Amount Ratio] AS 
'[Measures].[Internet Sales Amount]/([Measures].[Internet Sales Amount], [Product].[Product Categories].[All])' , FORMAT_STRING = '0.00%'   
SELECT 
{
[Measures].[Internet Sales Amount][Measures].[Sale Amount Ratio]ON 0,
NON EMPTY 
[Product].[Product Categories].[Product Name].Members ON 1
FROM [Adventure Works]

2)某个子项占其父项的百分比。比如:每种Product的销售额占其所属的SubCategory销售额的百分比。

WITH MEMBER [Measures].[Sale Amount Ratio] AS 
'[Measures].[Internet Sales Amount]/
([Measures].[Internet Sales Amount], [Product].[Product Categories].CurrentMember.Parent)
'
, FORMAT_STRING 
= '0.00%'   
SELECT 
{
[Measures].[Internet Sales Amount][Measures].[Sale Amount Ratio]ON 0,
NON EMPTY CROSSJOIN(
[Product].[Subcategory].[Subcategory].Members, 
    [Product].[Product Categories].[Product Name].Members) ON 1
FROM [Adventure Works]

3)某个子项占其祖先的百分比。比如:每种Product的销售额占其所属的Category销售额的百分比。

WITH MEMBER [Measures].[Sale Amount Ratio] AS 
'[Measures].[Internet Sales Amount]/
([Measures].[Internet Sales Amount], 
ANCESTOR([Product].[Product Categories].CurrentMember, [Product].[Product Categories].[Category]))
'
, FORMAT_STRING 
= '0.00%'   
SELECT 
{
[Measures].[Internet Sales Amount][Measures].[Sale Amount Ratio]ON 0,
NON EMPTY CROSSJOIN(
[Product].[Category].[Category].Members, [Product].[Product Categories].[Product Name].Members) ON 1
FROM [Adventure Works]

分配、分摊数量
1)根据一个Measure值来分配数量。比如:按照每种Product占总体的销售额多少来分摊成本。

WITH MEMBER [Measures].[Product Cost] AS 
'([Measures].[Internet Total Product Cost], [Product].[Product Categories].[All])*
[Measures].[Internet Sales Amount]/
([Measures].[Internet Sales Amount], [Product].[Product Categories].[All])
'  
, FORMAT_STRING 
= '0.00' 
SELECT 
{
[Measures].[Internet Sales Amount][Measures].[Product Cost]ON 0,
NON EMPTY 
[Product].[Product Categories].[Product Name].Members ON 1
FROM [Adventure Works]

2)根据一个Hierarchy来分配数量。比如:在Product Hierarchy中计算每种Category的成本的时候,可以根据每种Category下有多少个产品来进行分配。

WITH MEMBER [Measures].[Product Cost] AS 
'([Measures].[Internet Total Product Cost], [Product].[Product Categories].[All])/
Count(
    Descendants (
        [Product].[Product Categories].CurrentMember,
        [Product].[Product Categories].[Product Name],
        SELF
    ),
    INCLUDEEMPTY
)
'  
, FORMAT_STRING 
= '0.00' 
SELECT 
{
[Measures].[Internet Sales Amount][Measures].[Product Cost]ON 0,
NON EMPTY 
[Product].[Product Categories].[Category].Members ON 1
FROM [Adventure Works]

平均值
1)简单平均值。比如:计算一个月中每天平均的销售额是多少。

WITH MEMBER Measures.[Avg Gross Profit Margin] AS
   
[Measures].[Internet Sales Amount]/
   
COUNT(Descendants([Ship Date].[Fiscal].CurrentMember, [Ship Date].[Fiscal].[Date]), INCLUDEEMPTY)   

SELECT
  {
[Measures].[Internet Sales Amount], Measures.[Avg Gross Profit Margin]ON COLUMNS,
  
[Ship Date].[Fiscal].[month].Members ON ROWS
FROM [Adventure Works]

2)加权平均值。没有想到好的例子。

基于时间的计算
1)同比和环比。比如:今年每月的销售额和去年同期相比的变化
这里要补充的是,在同比MDX中,采用COUSIN或ParallelPeriod都可以,但是采用ParallelPeriod更好一些。

2)累计到当前的统计。比如:得到一年中每一个月的累计销售额。

WITH MEMBER Measures.[Additive Internet Sales Amount] AS
   
SUM(
       PeriodsToDate(
[Ship Date].[Fiscal].[Fiscal Year],[Ship Date].[Fiscal].CurrentMember), 
       
[Measures].[Internet Sales Amount]
   ) 
SELECT
  {
[Measures].[Internet Sales Amount], Measures.[Additive Internet Sales Amount]ON COLUMNS,
  
[Ship Date].[Fiscal].[month].Members ON ROWS
FROM [Adventure Works]

3)移动平均值。比如:计算一种Category过去三个月的平均销售额合计。

WITH MEMBER Measures.[Average Internet Sales Amount] AS
   
AVG(LastPeriods(3[Date].[Calendar].CurrentMember), 
       
[Measures].[Internet Sales Amount]
SELECT
  {
[Measures].[Internet Sales Amount], Measures.[Average Internet Sales Amount]ON COLUMNS,
 NON EMPTY (
[Product].[Product Categories].[Category].Members, 
    DESCENDANTS(
[Date].[Calendar].[Calendar Year].&[2002][Date].[Calendar].[Month], SELF)
  ) 
ON ROWS
FROM [Adventure Works]

posted @ 2010-04-07 16:12 AmyQiu 阅读(487) 评论(1) 编辑

2010年3月12日

一种:EXEC sp_helptext 'xxx'

二种:
select b.text from sysobjects as a   
inner join syscomments as b   
on a.ID=b.ID where a.xtype='P' and name='xxx'

 

posted @ 2010-03-12 16:43 AmyQiu 阅读(42) 评论(0) 编辑

2010年1月14日

在SQL Server的性能调优中,有一个不可比拟的问题:那就是如何在一段需要长时间的代码或被频繁调用的代码中处理临时数据集?表变量和临时表是两种选择。记得在给一家国内首屈一指的海运公司作SQL Server应用性能评估和调优的时候就看到过大量的临时数据集处理需求,而他们的开发人员就无法确定什么时候用临时表,什么时候用表变量,因此他们就简单的使用了临时表。实际上临时表和表变量都有特定的适用环境。

  表变量

  变量都以@或@@为前缀,表变量是变量的一种,另外一种变量被称为标量(可以理解为标准变量,就是标准数据类型的变量,例如整型int或者日期型DateTime)。以@前缀的表变量是本地的,因此只有在当前用户会话中才可以访问,而@@前缀的表变量是全局的,通常都是系统变量,比如说@@error代表最近的一个T-SQL语句的报错号。当然因为表变量首先是个变量,因此它只能在一个Batch中生存,也就是我们所说的边界,超出了这个边界,表变量也就消亡了。

  表变量存放在内存中,正是因为这一点所有用户访问表变量的时候SQL Server是不需要生成日志。同时变量是不需要考虑其他会话访问的问题,因此也不需要锁机制,对于非常繁忙的系统来说,避免锁的使用可以减少一部分系统负载。

  表变量另外还有一个限制就是不能创建索引,当然也不存在统计数据的问题,因此在用户访问表变量的时候也就不存在执行计划选择的问题了(也就是以为着编译阶段后就没有优化阶段了),这一特性有的时候是件好事,而有些时候却会造成一些麻烦。

  临时表

  临时对象都以#或##为前缀,临时表是临时对象的一种,还有例如临时存储过程、临时函数之类的临时对象,临时对象都存储在tempdb中。以#前缀的临时表为本地的,因此只有在当前用户会话中才可以访问,而##前缀的临时表是全局的,因此所有用户会话都可以访问。临时表以会话为边界,只要创建临时表的会话没有结束,临时表就会持续存在,当然用户在会话中可以通过DROP TABLE命令提前销毁临时表。

  我们前面说过临时表存储在tempdb中,因此临时表的访问是有可能造成物理IO的,当然在修改时也需要生成日志来确保一致性,同时锁机制也是不可缺少的。

  跟表变量另外一个显著去别就是临时表可以创建索引,也可以定义统计数据,因此SQL Server在处理访问临时表的语句时需要考虑执行计划优化的问题。

表变量 vs. 临时表

详解SQLServer中的临时表和表变量

结论

  综上所述,大家会发现临时表和表变量在底层处理机制上是有很多差别的。

  简单地总结,我们对于较小的临时计算用数据集推荐使用表变量。如果数据集比较大,如果在代码中用于临时计算,同时这种临时使用永远都是简单的全数据集扫描而不需要考虑什么优化,比如说没有分组或分组很少的聚合(比如说COUNT、SUM、AVERAGE、MAX等),也可以考虑使用表变量。使用表变量另外一个考虑因素是应用环境的内存压力,如果代码的运行实例很多,就要特别注意内存变量对内存的消耗。

  一般对于大的数据集我们推荐使用临时表,同时创建索引,或者通过SQL Server的统计数据(Statisitcs)自动创建和维护功能来提供访问SQL语句的优化。如果需要在多个用户会话间交换数据,当然临时表就是唯一的选择了。需要提及的是,由于临时表存放在tempdb中,因此要注意tempdb的调优。

posted @ 2010-01-14 16:36 AmyQiu 阅读(64) 评论(0) 编辑

2009年11月3日

摘要: [代码][代码][代码]阅读全文

posted @ 2009-11-03 14:41 AmyQiu 阅读(69) 评论(1) 编辑

2009年10月16日

摘要: sql中的某个字段用“,”分隔数据,需要获取数据的时候直接把“,”拆分成数据,获得一个数据的list。例如:需要查询某字段是否包含一个值,111是否存在于1111,2111,1112,1121,1113这个字段中。因为根据“,”逗号分开,要求的答案是:不在字段中。用传统的like '%111%',显然不合适,这样虽然111不存...阅读全文

posted @ 2009-10-16 12:23 AmyQiu 阅读(538) 评论(2) 编辑

2009年7月13日

摘要: 下面具体演示一下如何用SSIS完成这样的处理:1. 准备测试环境-- 1. 在数据库中创建下面的对象USE tempdbGOCREATE TABLE dbo.tb( id int PRIMARY KEY, name nvarchar(128))GO-- 2. 准备两个文本文件, 放在d:\test 目录下, 文件的内容如下t1.txtid name1 张三2 李四t2.txtid name1 张三...阅读全文

posted @ 2009-07-13 16:35 AmyQiu 阅读(121) 评论(0) 编辑

摘要: Project REAL:业务智能 ETL 设计实施策略发布日期: 2005年12月19日发布者 Erik VeermanSQL Server 技术文章技术审阅:Donald Farmer、Grant Dickinson合作伙伴:Intellinet适用于:SQL Server 2005摘要:了解 SQL Server 2005 Integration Services (SSIS) 的使用。在称...阅读全文

posted @ 2009-07-13 16:14 AmyQiu 阅读(211) 评论(0) 编辑

摘要: 这个帖子主要总结在触发器方面的应用。 对于没有使用过触发器的兄弟可以先看下面关于使用触发器的文章。 http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/vdbt7/html/dvtskworkingwithtriggers.asp 大家可以在下面的链接来搜索CSDN技术社区文档,很大的技术宝藏。 http://sea...阅读全文

posted @ 2009-07-13 16:07 AmyQiu 阅读(97) 评论(0) 编辑

摘要: >服务器端编程>创建供前端应用程序通过Web服务调用的存储过程。首先,我们在SQL Server 2005示例数据库AdventureWorks中创建两个存储过程—GetProducts和UpdateproductPrice。后面,我们将通过SQL Server本机Web服务来访问这两个存储过程。创建它们的相应SQL脚本代码如下所示:USE AdventureWorksGO...阅读全文

posted @ 2009-07-13 16:05 AmyQiu 阅读(50) 评论(0) 编辑

摘要: 查询死锁进程的方法:一条SQL2005里查询进程信息的SQL语句虽然简单,估计大家都能写出来,不过比较实用,返回的信息包括进程阻塞信息、登录的相关信息、执行的SQL语句信息、进程所消耗的CPU、内存、I/O信息、以及客户端IP信息,具体如下:with tbas(select blocking_session_id,session_id,db_name(database_id) as dbname,...阅读全文

posted @ 2009-07-13 15:51 AmyQiu 阅读(665) 评论(0) 编辑

摘要: HOW TO FIX SQLSERVER DATABASE SUSPECT?如何修复数据库状态"置疑"?**********************************************************Author:黄山光明顶mail:leimin@jxfw.comversion:1.0.0date:2004-1-30(如需转载,请注明出处!,如果有问题请发MAIL给我:-))*...阅读全文

posted @ 2009-07-13 15:39 AmyQiu 阅读(65) 评论(0) 编辑

摘要: *基本还原数据库方法*http://community.csdn.net/Expert/topic/5111/5111602.xml?temp=.9495661http://community.csdn.net/Expert/topic/4991/4991655.xml?temp=.515011SQL Server 2005还原问题http://community.csdn.net/Expert/...阅读全文

posted @ 2009-07-13 15:33 AmyQiu 阅读(81) 评论(0) 编辑

摘要: 一、 镜像简介1、 简介数据库镜像是将数据库事务处理从一个SQL Server数据库移动到不同SQL Server环境中的另一个SQL Server数据库中。镜像不能直接访问;它只用在错误恢复的情况下才可以被访问。要进行数据库镜像所需的最小需求包括了两个不同的SQL Server运行环境。主服务器被称为“主机”,第二个服务器被称作“备机”。主机数据库...阅读全文

posted @ 2009-07-13 15:28 AmyQiu 阅读(105) 评论(0) 编辑

摘要: 1.数据库镜像 通过新数据库镜像方法,将记录档案传送性能进行延伸。您将可以使用数据库镜像,通过将自动失效转移建立到一个待用服务器上,增强您SQL服务器系统的可用性。2.在线恢复 使用SQL2005版服务器,数据库管理人员将可以在SQL服务器运行的情况下,执行恢复操作。在线恢复改进了SQL服务器的可用性,因为只有正在被恢复的数据是无法使用的,而数据库的其他部分依然在线、可供使用。3.在线检索操作 在...阅读全文

posted @ 2009-07-13 15:26 AmyQiu 阅读(52) 评论(0) 编辑

摘要: 物理日志文件:这个比较好理解,实实在在的东西,数据库目录下面的.ldf文件就是,有些人喜欢改后缀,感觉不大好,数据库的事务日志记录就在这里面虚拟日志:对于一个或多个连续的物理日志文件,SQL SERVER在这些文件的内部又划分成了多个小的文件,称为虚拟日志文件,他是日志文件收缩和日志截断的最小单位,比如物理日志文件是400M,内部划分了4个100M的虚拟文件,收缩时你得到的是300M,200M,不...阅读全文

posted @ 2009-07-13 15:22 AmyQiu 阅读(138) 评论(0) 编辑

摘要: Master   Master数据库保存有放在SQLSERVER实体上的所有数据库,它还是将引擎固定起来的粘合剂。由于如果不使用主数据库,SQLSERVER就不能启动,所以你必须要小心地管理好这个数据库。因此,对这个数据库进行常规备份是十分必要的。   这个数据库包括了诸如系统登录、配置设置、已连接的SERVER等信息,以及用于该实体的其他系统和用户数据库的一般信息。主数据库还存有扩展存储过程,它...阅读全文

posted @ 2009-07-13 15:21 AmyQiu 阅读(45) 评论(0) 编辑

2009年6月30日

摘要: [代码][代码]阅读全文

posted @ 2009-06-30 16:15 AmyQiu 阅读(38) 评论(0) 编辑

2009年6月23日

摘要: [代码][代码]阅读全文

posted @ 2009-06-23 17:54 AmyQiu 阅读(149) 评论(0) 编辑

2009年4月25日

摘要: [代码]阅读全文

posted @ 2009-04-25 16:20 AmyQiu 阅读(77) 评论(0) 编辑

摘要: [代码]阅读全文

posted @ 2009-04-25 15:46 AmyQiu 阅读(30) 评论(0) 编辑

2009年4月24日

摘要: [代码]阅读全文

posted @ 2009-04-24 16:30 AmyQiu 阅读(24) 评论(0) 编辑

摘要: [代码]阅读全文

posted @ 2009-04-24 11:49 AmyQiu 阅读(327) 评论(0) 编辑

2009年4月17日

摘要: [代码]阅读全文

posted @ 2009-04-17 14:28 AmyQiu 阅读(27) 评论(0) 编辑

2009年4月16日

摘要: [代码]阅读全文

posted @ 2009-04-16 09:52 AmyQiu 阅读(35) 评论(0) 编辑

摘要: [代码]阅读全文

posted @ 2009-04-16 09:51 AmyQiu 阅读(230) 评论(0) 编辑

摘要: [代码]阅读全文

posted @ 2009-04-16 09:50 AmyQiu 阅读(194) 评论(0) 编辑

2009年4月14日

摘要: SQL Server 2005的XmL数据类型基础一、引言XML数据类型,可使用XML作为表和视图中的列,可用于T-SQL语句中或作为存储过程的参数。可以直接在数据库中存储、查询和管理XML文件。还能规定你的XML必须遵从的模式。在SQL Server 2005中,除了提供机制以校验你的数据库中的XML类型之外,它还允许你描述要被存储的复杂数据类型并且提供一个引擎来强制施加这些规则。二、使用XML...阅读全文

posted @ 2009-04-14 14:45 AmyQiu 阅读(46) 评论(0) 编辑

2009年4月7日

摘要: 1.操作系统:排除可能是木马或病毒等。2.应用系统:(a)重要表的索引建立和使用,(b)执行效率低的SQL导致查询或更新进程的阻塞,直至引发的死锁现象。这都将成为引发SQL Server响应异常缓慢,CPU占用率高居不下的主要原因。(1).首先执行sp_lock,排除X锁和IX锁。(2).查询是否是由于重要表引起的CPU占用率高:SELECT TOP 5 total_worker_time, la...阅读全文

posted @ 2009-04-07 14:58 AmyQiu 阅读(61) 评论(0) 编辑

摘要: SQL Server操作系统相关动态管理对象sys.dm_os_performance_counters:视图返回SQL Server直接相关的性能统计。SELECT (CAST(SUM(CASE LTRIM(RTRIM(counter_name)) WHEN 'Buffer cache hit ratio' THEN CAST(cntr_value AS INTEGER) ELSE NULL E...阅读全文

posted @ 2009-04-07 14:34 AmyQiu 阅读(66) 评论(0) 编辑

2009年4月3日

摘要: www.connectionstrings.com阅读全文

posted @ 2009-04-03 10:51 AmyQiu 阅读(11) 评论(0) 编辑

2009年4月1日

摘要: 监测SQL Server数据库服务器:当怀疑计算机硬件是影响SQL Server运行性能的主要原因时,可以通过SQL Server Performance Monitor监视相应硬件的负载,以证实您的猜测并找出系统瓶颈。下文将介绍一些常用的分析对象及其参数。   Memory: Page Faults / sec  如果该值偶尔走高,表明当时有线程竞争内存。如果持续很高,则内存可能是瓶颈。  Pr...阅读全文

posted @ 2009-04-01 10:14 AmyQiu 阅读(62) 评论(0) 编辑