Eu tenho um cenário onde tenho Date
dimensão e Time
dimensão junto com algumas outras dimensões. A dimensão de tempo contém apenas Hours
porque não houve necessidade de salvar dados em minuto ou segundo nível.
Uma situação exige que eu calcule o valor Average
de Max
certas horas do dia. Por exemplo, preciso Max
do valor entre 01:00 e 04:00 horas e a média desses valores máximos no período de tempo determinado (01 de janeiro de 2015 - 03 de janeiro de 2015 neste exemplo).
Estou filtrando Date
e Time
dimensões (juntamente com outras dimensões) como Subselect
From
cláusulas em vez de usar Where
.
Se os Time
membros da dimensão permanecerem os mesmos para cada valor de Date
dimensão selecionado, então Max
, Avg
e StDev
fornecer o resultado correto, mas se eu tiver que excluir algumas horas de um dos Date
membros da dimensão, Max
resultará em erro Avg
e StDev
fornecerá valores incorretos.
Consulte o código SQL abaixo para criar o exemplo que usarei nesta pergunta:
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
A MDX
consulta que funciona corretamente é:
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]
)
Ele exibe o resultado corretamente como:
Observe que os itens incluídos no Eixo 1 não são usados e a consulta real retorna apenas uma linha. Eles são apenas para este exemplo.
A consulta que não funciona como esperado é:
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]
)
Aqui, excluí Horas 01:00
e a coluna '02:00 from Date
3 January 2015 . The maximum value (70) is at hour
01:00 of
3 January 2015 but is still being used to calculate Avg and
StDev , and
Max` está dando erro, conforme mostra a imagem:
Aqui, Média e Desvio Padrão estão incorretos.
Por favor, ajude-me a encontrar uma maneira de calcular corretamente os resultados neste segundo caso e explique por que Max está dando erro e Avg
as StDev
funções ainda estão calculando, incluindo o valor máximo que não está incluído na Subselect
cláusula filtrada.
Uma solução alternativa que não é ideal é adicionar uma nova DateTime
dimensão que tenha Date
e Time
, e usar essa DateTime
dimensão na Subselect
cláusula e utilizar a Time
dimensão existente para calcular Max
e Date
dimensionar para obter média e desvio padrão, como no código abaixo :
Crie e preencha a DateTime
dimensão:
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)
A MDX
consulta retornando o resultado correto:
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]
)
Mas prefiro evitar a criação de uma nova dimensão, além das duas dimensões existentes contendo os mesmos dados. Em segundo lugar, quero evitar colocar os dados no aplicativo e calcular o resultado nos métodos da API. Então a preferência é fazer com que as funções Max
funcionem corretamente.Avg
StDev
Usando o SQLServer 2012.