web-dev-qa-db-ja.com

MDX Max、Average、およびStDev関数が正しい結果を提供しない

DateディメンションとTimeディメンションが他のいくつかのディメンションと一緒にあるシナリオがあります。分または第2レベルでデータを保存する必要がなかったため、時間ディメンションにはHoursのみが含まれます。

状況によっては、特定の時間帯の値のAverageMaxを計算する必要があります。たとえば、01:00から04:00までの値のMaxと、指定された期間(この例では2015年1月1日から2015年1月3日)のこれらの最大値の平均が必要です。

Dateを使用する代わりにTimeおよびSubselectディメンションを(他のディメンションとともに)FromWhere句としてフィルタリングしています。

TimeディメンションのメンバーがDateディメンションの選択された値ごとに同じままである場合、MaxAvg、およびStDevは正しい結果を示します、ただし、Dateディメンションメンバーの1つから数時間を除外する必要がある場合、Maxはエラーになり、AvgStDevは誤った値を示します。

この質問で使用する例を作成するには、以下のSQLコードを参照してください。

_create database TestDateTimeDimensions
GO

USE [TestDateTimeDimensions]
GO

create table DimDate (
DateId int not null PRIMARY KEY,
Date [Date] not null
)


insert into DimDate
select 20150101, CONVERT(DATE, '2015-01-01')
UNION ALL
select 20150102, CONVERT(DATE, '2015-01-02')
UNION ALL
select 20150103, CONVERT(DATE, '2015-01-03')
UNION ALL
select 20150104, CONVERT(DATE, '2015-01-04')

create table DimTime(
TimeId int not null PRIMARY KEY,
[Time] Time not null
)

insert into DimTime
select 0, CONVERT(TIME, '00:00:00')
UNION ALL
select 1, CONVERT(TIME, '01:00:00')
UNION ALL
select 2, CONVERT(TIME, '02:00:00')
UNION ALL
select 3, CONVERT(TIME, '03:00:00')
UNION ALL
select 4, CONVERT(TIME, '04:00:00')
UNION ALL
select 5, CONVERT(TIME, '05:00:00')
UNION ALL
select 6, CONVERT(TIME, '06:00:00')
UNION ALL
select 7, CONVERT(TIME, '07:00:00')
UNION ALL
select 8, CONVERT(TIME, '08:00:00')
UNION ALL
select 9, CONVERT(TIME, '09:00:00')
UNION ALL
select 10, CONVERT(TIME, '10:00:00')
UNION ALL
select 11, CONVERT(TIME, '11:00:00')
UNION ALL
select 12, CONVERT(TIME, '12:00:00')
UNION ALL
select 13, CONVERT(TIME, '13:00:00')
UNION ALL
select 14, CONVERT(TIME, '14:00:00')
UNION ALL
select 15, CONVERT(TIME, '15:00:00')
UNION ALL
select 16, CONVERT(TIME, '16:00:00')
UNION ALL
select 17, CONVERT(TIME, '17:00:00')
UNION ALL
select 18, CONVERT(TIME, '18:00:00')
UNION ALL
select 19, CONVERT(TIME, '19:00:00')
UNION ALL
select 20, CONVERT(TIME, '20:00:00')
UNION ALL
select 21, CONVERT(TIME, '21:00:00')
UNION ALL
select 22, CONVERT(TIME, '22:00:00')
UNION ALL
select 23, CONVERT(TIME, '23:00:00')

CREATE TABLE Fact (
Id int identity not null PRIMARY KEY,
DateId int not null REFERENCES DimDate(DateId),
TimeId int not null REFERENCES DimTime(TimeId),
value int not null
)

insert into Fact

select DateId, TimeId, ROUND(Rand(CONVERT(varbinary, NEWID())) * 100, 2)
FROM DimDate, DimTime
_

正しく機能するMDXクエリは次のとおりです。

_WITH
MEMBER MaxMember as MAX(TimeSet, [Measures].[Value])
MEMBER AvgOfMax as Avg(DateSet, MAX(TimeSet, [Measures].[Value]))
MEMBER StDevOfMax as StDev(DateSet, MAX(TimeSet, [Measures].[Value]))

SET DateSet as EXISTING [Dim Date].[Date Id].[Date Id]
SET TimeSet as EXISTING [Dim Time].[Time Id].[Time Id]

select
{
    [Measures].[Value], MaxMember, AvgOfMax, StDevOfMax
}
on 0,
{
    DateSet * TimeSet
} on 1

FROM 
(
    SELECT ({[Dim Date].[Date Id].&[20150101] : [Dim Date].[Date Id].&[20150103]}, {[Dim Time].[Time Id].&[1] : [Dim Time].[Time Id].&[4]})
    on 0
    FROM [Test Date Time Dimensions]
)
_

結果は次のように正しく表示されます。

Correct Result

軸1に含まれるアイテムは使用されず、実際のクエリは1行のみを返すことに注意してください。これらはこの例のためだけのものです。

期待どおりに機能しないクエリは次のとおりです。

_WITH
MEMBER MaxMember as MAX(TimeSet, [Measures].[Value])
MEMBER AvgOfMax as Avg(DateSet, MAX(TimeSet, [Measures].[Value]))
MEMBER StDevOfMax as StDev(DateSet, MAX(TimeSet, [Measures].[Value]))

SET DateSet as EXISTING [Dim Date].[Date Id].[Date Id]
SET TimeSet as EXISTING [Dim Time].[Time Id].[Time Id]

select
{
    [Measures].[Value], MaxMember, AvgOfMax, StDevOfMax
}
on 0

,
{
    DateSet * TimeSet
} on 1

FROM 
(
    SELECT {({[Dim Date].[Date Id].&[20150101] : [Dim Date].[Date Id].&[20150102]} * {[Dim Time].[Time Id].&[1] : [Dim Time].[Time Id].&[4]})
    , ([Dim Date].[Date Id].&[20150103] * [Dim Time].[Time Id].&[3] : [Dim Time].[Time Id].&[4])}
    on 0
    FROM [Test Date Time Dimensions]
)
_

ここでは、時間_01:00_と'02:00 _from Date_ 3 2015年1月. The maximum value (70) is at hour 01:00 of3 2015年1月_but is still being used to calculate Avg and_ StDev _, and_ Max`列でエラーが発生していることを除外しました、画像に示されているように:

Error

ここでは、平均偏差と標準偏差の両方が正しくありません。

この2番目のケースで結果を正しく計算する方法を見つけるのを手伝ってください。また、Maxがエラーを出し、Avg関数とStDev関数がまだ計算している理由を説明してください。フィルタリングされたSubselect句。

私が思いついた理想的とは言えない回避策は、DateTimeDateの両方を持つ新しいTimeディメンションを追加し、このDateTimeディメンションを使用することです。以下のコードのように、Subselect句で、既存のTimeディメンションを使用してMaxおよびDateディメンションを計算し、平均および標準偏差を取得します。

DateTimeディメンションを作成して入力します。

_create table DimDateTime
(
    DateTimeId int not null PRIMARY KEY,
    [Date] Date not null,
    [Time] Time not null 
)

insert into DimDateTime

select ((DateId * 100) + TimeId) DateTimeId, DimDate.Date, DimTime.Time
from DimDate, DimTime

alter table Fact add DateTimeId int References DimDateTime(DateTimeId)

update Fact set DateTimeId = ((DateId * 100) + TimeId)
_

正しい結果を返すMDXクエリ:

_WITH
MEMBER MaxMember as MAX(TimeSet, [Measures].[Value])
MEMBER AvgOfMax as Avg(DateSet, MAX(TimeSet, [Measures].[Value]))
MEMBER StDevOfMax as StDev(DateSet, MAX(TimeSet, [Measures].[Value]))

SET DateSet as NONEMPTY ([Dim Date].[Date Id].[Date Id])
SET TimeSet as NONEMPTY ([Dim Time].[Time Id].[Time Id])

select
{
    [Measures].[Value], MaxMember, AvgOfMax, StDevOfMax
}
on 0,
{
    NONEMPTY(DateSet * TimeSet)
} on 1

FROM 
(
    SELECT ({[Dim Date Time].[Date Time Id].&[2015010101] : [Dim Date Time].[Date Time Id].&[2015010104],
            [Dim Date Time].[Date Time Id].&[2015010201] : [Dim Date Time].[Date Time Id].&[2015010204],
            [Dim Date Time].[Date Time Id].&[2015010303] : [Dim Date Time].[Date Time Id].&[2015010304]})
    on 0
    FROM [Test Date Time Dimensions]
)
_

ただし、同じデータを含む既存の2つのディメンションに加えて、新しいディメンションを作成することは避けたいと思います。次に、データをアプリケーションに取り込み、APIメソッドで結果を計算することを避けたいと思います。したがって、MaxAvg、およびStDev関数が正しく機能するようにすることをお勧めします。

SQL Server2012の使用。

1
SpeedBirdNine

MDXで試してみましたが、役に立たなかったため、SSASユーザー定義関数を使用して最大の平均を計算する必要がありました。全体でAvgDateSet)を計算するためのセット、(Max)全体でTimeSetを計算するためのセット、および除外されるレコードを含むための3番目のセットMax操作は、パラメーターとしてUDFに渡されます。

基本的なコードは次のようなものです(エラーチェックを除く、コード化されたAvg of Max機能を表示します)。

public static class AvgOfMaxMethods
    {
        public static double AvgOfMax(Set maxAcrossSet, Set avgAcrossSet, Set setToExclude, Expression measureExpression)
        {
            var setToExcludeQueryable = setToExclude.Tuples.OfType<Microsoft.AnalysisServices.AdomdServer.Tuple>();
            IList<double> maxMembers = new List<double>();
            foreach(var avgAcrossTuple in avgAcrossSet.Tuples)
            {
                var max = double.MinValue;
                foreach(var maxAcrossTuple in maxAcrossSet.Tuples)
                {
                    if (!setToExcludeQueryable.Any(Tuple => Tuple.Members[0].UniqueName.Equals(avgAcrossTuple.Members[0].UniqueName) && Tuple.Members[1].UniqueName.Equals(maxAcrossTuple.Members[0].UniqueName)))
                    {
                        TupleBuilder tb = new TupleBuilder(avgAcrossTuple.Members[0]);
                        tb.Add(maxAcrossTuple.Members[0]);
                        var calculatedVal = measureExpression.Calculate(tb.ToTuple()).ToDouble();
                        max = calculatedVal > max ? calculatedVal : max;
                    }
                }
                if (!max.Equals(double.MinValue))
                {
                    maxMembers.Add(max);
                }
            }
            return maxMembers.Average();
        }
    }

これにより、クエリは次のようになります(比較のために元のMaxStDevを保持します)。

WITH
MEMBER MaxMember as MAX(TimeSet, [Measures].[Value])
MEMBER AvgOfMax as Avg(DateSet, MAX(TimeSet, [Measures].[Value]))
MEMBER StDevOfMax as StDev(DateSet, MAX(TimeSet, [Measures].[Value]))
Member MaxFromUDF as UDFPoc.UDFPoc.AvgOfMaxMethods.AvgOfMax(TimeSet, DateSet, SetToExclude, [Measures].[Value])

SET DateSet as EXISTING [Dim Date].[Date Id].[Date Id]
SET TimeSet as EXISTING [Dim Time].[Time Id].[Time Id]
SET SetToExclude as {[Dim Date].[Date Id].&[20150103] * [Dim Time].[Time Id].&[1] : [Dim Time].[Time Id].&[2]}

select
{
    [Measures].[Value], MaxMember, AvgOfMax, StDevOfMax, MaxFromUDF
}
on 0,
{
    DateSet * TimeSet
} on 1

FROM 
(
    SELECT ({[Dim Date].[Date Id].&[20150101] : [Dim Date].[Date Id].&[20150103]}, {[Dim Time].[Time Id].&[1] : [Dim Time].[Time Id].&[4]})
    on 0
    FROM [Test Date Time Dimensions]
)

結果は次のようになります(ここでは、除外されたセットのタプルが赤で強調表示されています)。

enter image description here

ご覧のとおり、UDFは、SettoExcludeおよびMaxを計算するときに、Averageセット内の各タプルのメンバーを除外します。画像に見られるように、除外されたセットを考慮した正しい平均が計算されています。同様に、StDevはこの方法で計算できます。

SSASユーザー定義関数を作成するためのガイドとして this を参照してください。

0
SpeedBirdNine