与 UNION 和 DESCENDANTS 结合使用的 Analysis Service 中 Measures 的错误聚合

Dav*_*vid 2 ssas mdx

更新:当我试图提供一个简单的例子来重现这个问题时,我在我的数据库中发现了引入这个问题的调平器。因此,我对这个问题进行了更新,以反映这一新发现。您将在下面找到用于设置重现此问题的数据库的脚本。

我在服务器版本 2008 和 2012 中遇到了 Analysis Services 的奇怪行为。

在我的 AS 数据库中,我定义了一个带有基本度量的多维数据集,如下所示:

AggregateFunction: Min;
Name: Existing Data
Run Code Online (Sandbox Code Playgroud)

如您所见,聚合函数是Min

包含相应existing_data列的事实表仅包含01值。目标是在数据选择包含链接到零值的类别时立即返回零。该矩阵表示存储在另一个事实表中的真实事实中已经/尚未为其收集数据的区域,因此我们可以区分结果是否为零,因为在所选区域中没有任何案例,或者因为在所选区域中尚未收集到任何病例的地区。

当我现在运行以下查询时,我为每个选定的类别获得 1:

WITH
   SET [adhoc] AS 'UNION(
     DESCENDANTS([Gebiete].[Hierarchie].[Bezirk].[010],[Gebiete].[Hierarchie].[Landkreis]),
     DESCENDANTS([Gebiete].[Hierarchie].[Bundesland].[10],[Gebiete].[Hierarchie].[Landkreis])
   )'
SELECT [adhoc] ON 0
FROM [Testdb]
WHERE ([Measures].[Existing Data])
Run Code Online (Sandbox Code Playgroud)

结果

01001    01002    10041    10042
1        1        1        1
Run Code Online (Sandbox Code Playgroud)

当我现在将此查询更改为以下内容时,我希望一个包含 1 的单元格作为结果,因为该Min()函数聚合的所有值都是 1(与之前的结果相比):

WITH
    SET [adhoc] AS 'UNION(
      DESCENDANTS([Gebiete].[Hierarchie].[Bezirk].[010],[Gebiete].[Hierarchie].[Landkreis]),
      DESCENDANTS([Gebiete].[Hierarchie].[Bundesland].[10],[Gebiete].[Hierarchie].[Landkreis])
    )'
    MEMBER [Gebiete].[Hierarchie].[adhoc] AS 'Aggregate([adhoc])'
SELECT {[Gebiete].[Hierarchie].[adhoc]} ON 0
FROM [Testdb]
WHERE ([Measures].[Existing Data]);
Run Code Online (Sandbox Code Playgroud)

但相反,我得到

adhoc
0
Run Code Online (Sandbox Code Playgroud)

当我改变聚合函数来显式调用Min[Gebiete].[Hierarchie].[adhoc]成员我得到的理想结果

adhoc
1
Run Code Online (Sandbox Code Playgroud)

询问:

WITH
    SET [adhoc] AS 'UNION(
      DESCENDANTS([Gebiete].[Hierarchie].[Bezirk].[010],[Gebiete].[Hierarchie].[Landkreis]),
      DESCENDANTS([Gebiete].[Hierarchie].[Bundesland].[10],[Gebiete].[Hierarchie].[Landkreis])
    )'
    MEMBER [Gebiete].[Hierarchie].[adhoc] AS 'Min([adhoc])'
SELECT {[Gebiete].[Hierarchie].[adhoc]} ON 0
FROM [Testdb]
WHERE ([Measures].[Existing Data]);
Run Code Online (Sandbox Code Playgroud)

我追踪到这与事实数据如何链接到 Gebiete 维度以及如何填充该维​​度有关。该维度有 Landkreis(最低层)、Bezirk 和 Bundesland 三层,但事实表仅与中间层(Bezirk)相关联。请参阅此所有相关表的图表。

OLAP 源数据库表

当我在01分支下方填充具有多个节点的此维度的所有三个级别时,就会出现问题。

Gebiete 维度填充了导致问题的项目

我是否改为在01分支下方只有一个节点和分支下方有一个或多个节点来填充维度,10上述查询返回预期结果。

Gebiete 维度填充了导致问题的项目

所以我认为这可能是 UNION、DESCENDANTS 和 Aggregate 的组合以及维度表的填充方式的错误。您能否确认一下,我是否正确并且这确实是一个错误,或者我的 MDX 查询是否错误?

您可以在下面找到设置重现问题的数据库的所有信息:

-- Fact
if exists (select * from dbo.sysobjects where id = object_id(N'dbo.fact_falldaten_existing_data') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table dbo.fact_falldaten_existing_data;

-- Area
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sf_gebiet_landkreis]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[sf_gebiet_landkreis];
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sf_gebiet_bezirk]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[sf_gebiet_bezirk];
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sf_gebiet_bundesland]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[sf_gebiet_bundesland];

GO

create table dbo.sf_gebiet_bundesland(
    id numeric(7, 0) primary key,
    shortname varchar(255) null,
    longname varchar(255) null,
    description varchar(255) null,
    geo_id numeric(10, 0) null,
    gkz varchar(10) null,
    parent_id numeric(7, 0)
);

create table dbo.sf_gebiet_bezirk(
    id numeric(7, 0) primary key,
    shortname varchar(255) null,
    longname varchar(255) null,
    description varchar(255) null,
    geo_id numeric(10, 0) null,
    gkz varchar(10) null,
    parent_id numeric(7, 0) references sf_gebiet_bundesland(id)
);

create table dbo.sf_gebiet_landkreis(
    id numeric(7, 0) primary key,
    shortname varchar(255) null,
    longname varchar(255) null,
    description varchar(255) null,
    geo_id numeric(10, 0) null,
    gkz varchar(10) null,
    parent_id numeric(7, 0) references sf_gebiet_bezirk(id)
);

GO

INSERT dbo.sf_gebiet_bundesland (id, shortname, longname, description, geo_id, parent_id) VALUES (2034, '01', 'Schleswig-Holstein', '', 16, 2032);
INSERT dbo.sf_gebiet_bundesland (id, shortname, longname, description, geo_id, parent_id) VALUES (2043, '10', 'Saarland', '', 9, 2032);

INSERT dbo.sf_gebiet_bezirk (id, shortname, longname, description, geo_id, parent_id) VALUES (2051, '010', 'Schleswig-Holstein alle Bezirke', '', 441, 2034);
INSERT dbo.sf_gebiet_bezirk (id, shortname, longname, description, geo_id, parent_id) VALUES (2079, '100', 'Saarland alle Bezirke', '', 446, 2043);

INSERT dbo.sf_gebiet_landkreis (id, shortname, longname, description, geo_id, parent_id) VALUES (2539, '01001', 'Flensburg', '', 881, 2051);
INSERT dbo.sf_gebiet_landkreis (id, shortname, longname, description, geo_id, parent_id) VALUES (2540, '01002', 'Kiel', '', 882, 2051);

INSERT dbo.sf_gebiet_landkreis (id, shortname, longname, description, geo_id, parent_id) VALUES (2858, '10041', 'Stadtverband Saarbrücken', '', 1200, 2079);
INSERT dbo.sf_gebiet_landkreis (id, shortname, longname, description, geo_id, parent_id) VALUES (2859, '10042', 'Merzig-Wadern', '', 1201, 2079);

-- Fact Data
CREATE table dbo.fact_falldaten_existing_data(
    bezirk          numeric(7)  references sf_gebiet_bezirk(id),
    existing_data   numeric(1)
);

GO

INSERT INTO fact_falldaten_existing_data (bezirk,existing_data) VALUES(2079,1);
INSERT INTO fact_falldaten_existing_data (bezirk,existing_data) VALUES(2051,1);

-- Comment this out to solve the issue
INSERT dbo.sf_gebiet_bezirk (id, shortname, longname, description, geo_id, parent_id) VALUES (2951, '01', 'Schleswig-Holstein unbekannter Bezirk', '', 3006, 2034);
INSERT dbo.sf_gebiet_landkreis (id, shortname, longname, description, geo_id, parent_id) VALUES (2502, '01', 'Schleswig-Holstein unbekannter Landkreis', '', 3000, 2951);
INSERT INTO fact_falldaten_existing_data (bezirk,existing_data) VALUES(2951,0);

--INSERT dbo.sf_gebiet_bezirk (id, shortname, longname, description, geo_id, parent_id) VALUES (2956, '10', 'Saarland unbekannter Bezirk', '', 3011, 2043);
--INSERT dbo.sf_gebiet_landkreis (id, shortname, longname, description, geo_id, parent_id) VALUES (2528, '10', 'Saarland unbekannter Landkreis', '', 3024, 2956);
--INSERT INTO fact_falldaten_existing_data (bezirk,existing_data) VALUES(2956,1);
Run Code Online (Sandbox Code Playgroud)

在此处找到您必须适当更改源数据库和部署目标的 SQL Server Data Tools 项目:SSDT 项目下载

mma*_*rie 5

我尝试使用多维 Contoso 多维数据集复制您所拥有的内容,其度量是“库存中的最小库存天数”,但没有遇到您的问题。我认为这与聚合 [adhoc] 时的当前上下文有关。我没有足够的信息来准确复制。但是在查看MIN()规范时,这句话引起了我的注意:

如果未指定数值表达式,则在集合成员的当前上下文中计算指定的集合,然后从该计算返回最小值。

首先,有几点想法:

  • 请注意,查询中的计算成员不需要单引号。只要您的计算不引发错误,它们就不会受到伤害,但在 SSAS 2005 左右之后不需要它们,并且在某些情况下可以抑制消息。
  • 计算成员在运行时进行评估,因此您无法利用任何缓存。最好将工作移到作用域脚本中并保持计算出的度量简单。或者,如果可能的话,将它们移动到轴中,以便在查询多次运行时将它们缓存起来。如果您有必须快速返回的大型数据集或非常关键的数据,您可能需要更改计算成员的使用。
  • 你与 DESCENDANTS 的联合很好。我能够做类似的事情并产生正确的结果。您可以通过使用集合而不是聚合成员来检查这一点,并确认所有成员都如您所愿。不过,我有点不明白你为什么拥有它。如果我错了,请纠正我,但我认为 Wesser Ems 是下萨克森州的一部分(不过,我对德国地理并不完全了解)。如果 Wesser Ems 是下萨克森州的一部分,那么您可以只做下萨克森州的后代,而不是做工会。这既不存在也不存在,因为你们的工会没有任何问题。我只是想尽可能地简化查询。
  • SSAS 真的很宽容。当您没有指定完全合格的成员时,它会尝试解释您的意思。例如,您使用 [Has Data] 而不是 [Measures].[HasData],并且您的集合和聚合成员都是 [adhoc]。如果某处还有一个名为 [HasData] 的字段,它可能会混淆您的意思。
  • SSAS 在计算最小值时忽略空值
  • 您已经有一个执行 MIN() ([Measures].[Has Data]) 的度量,因此使用AGGREGATE()将继续执行最小值,如果这是当前上下文中的内容。
  • 与 SQL 中的 WHERE 子句不同,MDX SELECT 语句的 WHERE 子句从不直接过滤查询的 Rows 轴上返回的内容。要过滤查询的行或列轴上显示的内容,您可以使用各种 MDX 函数,例如 FILTER、NONEMPTY 和 TOPCOUNT。例如,如果我在 2006 年没有任何库存,则 2006 仍然作为以下查询中的成员返回:
WITH SET [adhoc] AS 
UNION(后代(后裔)([地理].[地理层次结构].[地区国家名称].[德国], 
[地理].[地理层次].[城市名称]), 
DESCENDANTS([地理].[地理层次结构].[州省名称].[下萨克森州], 
[地理].[地理层次].[城市]))
成员 [Geography].[Geography Hierarchy].[adhoc] as MIN([adhoc])   
SELECT {[Date].[Calendar YQMD].[Calendar Year].members } on 0 
来自 [操作]
where ([Geography].[Geography Hierarchy].[adhoc],[Measures].[Inventory Min Day In Stock])

在我的数据集中,我的事实表对于构成 [Measures].[Inventory Min Day In Stock] 的数据只有 5 的值。这类似于您全为 1。

无论我使用 MIN([adhoc]) 还是 AGGREGATE([ad hoc]),此查询都会为我返回相同的预期答案 (5)

WITH 
SET [adhoc1] AS 
UNION(DESCENDANTS([Geography].[Geography Hierarchy].[Region Country Name].[Germany], [Geography].[Geography Hierarchy].[City Name]), 
DESCENDANTS([Geography].[Geography Hierarchy].[State Province Name].[Lower Saxony], [Geography].[Geography Hierarchy].[City]))
MEMBER [Geography].[Geography Hierarchy].[adhoc] as Min([adhoc1])
SELECT {[Date].[Calendar YQMD].[Calendar Year].[Year 2008] } on 0
from [Operation]
where ([Geography].[Geography Hierarchy].[adhoc],[Measures].[Inventory Min Day In Stock])
Run Code Online (Sandbox Code Playgroud)

我最好的建议是尝试用几种不同的方式重写它。如果计算的度量不是太慢,您可以使用它们来确保您拥有正确的上下文。

WITH 
MEMBER [Geography].[Geography Hierarchy].[adhoc] as Aggregate(UNION(DESCENDANTS([Areas].[Hierarchy].[Region].[Weser Ems],[Areas].[Hierarchy].[City]),DESCENDANTS([Areas].[Hierarchy].[State].[Lower Saxony],[Areas].[Hierarchy].[City])))
MEMBER [Measures].[Test] as Min([Geography].[Geography Hierarchy].[adhoc],[Measures].[HasData])
SELECT {[Time].[Hierarchy].[Year].[2008] } on 0
from [Incidence]
where ([Measures].[Test])
Run Code Online (Sandbox Code Playgroud)