2017-01-05 3 views
1

У меня есть ситуация, когда я хочу запросить несколько атрибутов (всего ~ 8 в целом) и включить промежуточные итоги. Это своего рода результат, что я хочу:SQL Rollup эквивалент в MDX

╔═══════╦═════════╦════════╦═════════╗ 
║ Attr1 ║ Attr2 ║ Attr3 ║ Measure ║ 
╠═══════╬═════════╬════════╬═════════╣ 
║ All ║ All  ║ All ║ 50%  ║ 
║ Foo ║ All  ║ All ║ 25%  ║ 
║ Bar ║ All  ║ All ║ 90%  ║ 
║ Foo ║ Anna ║ All ║ 42%  ║ 
║ Foo ║ Brian ║ All ║ 12%  ║ 
║ Bar ║ Charles ║ All ║ 10%  ║ 
║ Bar ║ Dory ║ All ║ 112% ║ 
║ Foo ║ Anna ║ Box ║ 58%  ║ 
║ Foo ║ Anna ║ Circle ║ 13%  ║ 
║ ... ║ ...  ║ ... ║ ...  ║ 
╚═══════╩═════════╩════════╩═════════╝ 

Теперь я могу получить почти там делать что-то вроде этого:

select 
    {[Measures].[Measure]} on columns, 
    nonempty({ 
     [Dim1].[Attr1].allmembers * 
     [Dim2].[Attr2].allmembers * 
     [Dim3].[Attr3].allmembers 
    }) on rows 
from [Cube] 

Однако, это, конечно, заставляет меня набор, который включает в себя элементы, которые выглядеть следующим образом:

╔═══════╦═════════╦════════╦═════════╗ 
║ Attr1 ║ Attr2 ║ Attr3 ║ Measure ║ 
╠═══════╬═════════╬════════╬═════════╣ 
║ Foo ║ All  ║ Box ║ 25%  ║ 
║ Bar ║ All  ║ Circle ║ 90%  ║ 
║ Foo ║ Anna ║ Box ║ 16%  ║ 
║ Bar ║ Charles ║ Circle ║ 78%  ║ 
║ ... ║ ...  ║ ... ║ ...  ║ 
╚═══════╩═════════╩════════╩═════════╝ 

что я не хочу, - я мог бы жить с ними, за исключением того, что с 8 размерами он делает это пойти немного сумасшедшим с перекрестным соединением (это дает мне ошибку о том, набор с более чем 4 бил левый кортеж в нем ...). Теперь, если бы я писал SQL я мог бы сделать что-то простое, как:

select 
    Dim1.Attr1, 
    Dim2.Attr2, 
    Dim3.Attr3, 
    Sum(Measures.Measure) as Measure 
group by 
    Dim1.Attr1, 
    Dim2.Attr2, 
    Dim3.Attr3 
with rollup 

Но я не могу найти простой способ воспроизвести это в MDX. Я могу вручную построить каждый уровень с накопительным пакетом что-то вроде этого:

select 
    {[Measures].[Measure]} on columns, 
    nonempty(
     { 
      {[Dim1].[Attr1].[All]} * 
      {[Dim2].[Attr2].[All]} * 
      {[Dim3].[Attr3].[All]} 
     } + 
     { 
      {[Dim1].[Attr1].[Attr1].allmembers} * 
      {[Dim2].[Attr2].[All]} * 
      {[Dim3].[Attr3].[All]} 
     } + 
     { 
      {[Dim1].[Attr1].[Attr1].allmembers} * 
      {[Dim2].[Attr2].[Attr2].allmembers} * 
      {[Dim3].[Attr3].[All]} 
     } + 
     { 
      {[Dim1].[Attr1].[Attr1].allmembers} * 
      {[Dim2].[Attr2].[Attr2].allmembers} * 
      {[Dim3].[Attr3].[Attr3].allmembers} 
     } 
    ) on rows 
from [Cube] 

Но это уже становится утомительным только с тремя размерами - с указанием 9 групп них будут противно. Итак - есть ли способ сделать это в сжатом виде в MDX, или мне просто нужно пойти с долговременным решением?

С точки зрения предыдущих исследований, я наткнулся на кучу ответов, как this one, которые говорят, чтобы использовать WITH MEMBER заявление, чтобы создать общую строку - но это бессмысленно для меня, так как это приводит к тому же перекрестное соединение поведения что я пытаюсь избежать с помощью функции allmembers.

Edit: это последняя (дезинфицировать) версия кода, в том числе @ предложение Данилом о NonEmptyCrossJoin:

NON EMPTY { 
    NONEMPTYCROSSJOIN(
     {[Dim1].[Attribute].[All]} * 
     {[Dim2].[Attribute].[All]} * 
     {[Dim3].[Attribute].[All]} * 
     {[Dim4].[Attribute].[All]} * 
     {[Dim6].[Attribute].[All]} * 
     {[Dim7].[Attribute].[All]} * 
     {[Dim8].[Attribute].[All]} * 
     {[Dim9].[Attribute].[All]} * 
     [Dim0].[Attribute].[Attribute].ALLMEMBERS 
    ) + 
    NONEMPTYCROSSJOIN(
     [Dim1].[Attribute].[Attribute].ALLMEMBERS * 
     {[Dim2].[Attribute].[All]} * 
     {[Dim3].[Attribute].[All]} * 
     {[Dim4].[Attribute].[All]} * 
     {[Dim6].[Attribute].[All]} * 
     {[Dim7].[Attribute].[All]} * 
     {[Dim8].[Attribute].[All]} * 
     {[Dim9].[Attribute].[All]} * 
     [Dim0].[Attribute].[Attribute].ALLMEMBERS 
    ) + 
    NONEMPTYCROSSJOIN(
     [Dim1].[Attribute].[Attribute].ALLMEMBERS * 
     [Dim2].[Attribute].[Attribute].ALLMEMBERS * 
     {[Dim3].[Attribute].[All]} * 
     {[Dim4].[Attribute].[All]} * 
     {[Dim6].[Attribute].[All]} * 
     {[Dim7].[Attribute].[All]} * 
     {[Dim8].[Attribute].[All]} * 
     {[Dim9].[Attribute].[All]} * 
     [Dim0].[Attribute].[Attribute].ALLMEMBERS 
    ) + 

    ... 

} 
+0

Пробовал ли вы использовать свою меру в качестве второго аргумента функции NonEmpty(), чтобы уменьшить размер сшитого набора? Это не поможет решить вашу проблему «выборочных итогов, таких как WITH ROLLUP», но может по крайней мере сделать возможным перекрестное соединение. – SebTHU

ответ

1

Я не могу видеть способ обойти, используя, по крайней мере один перекрестное соединение (хотя включение меры, которую вы заинтересованы в функции NonEmpty(), см. комментарий - может помочь в решении проблемы с перекрестной связью/из памяти).

Суммы в стиле SQL ROLLUP исключают определенные комбинации ALL и non-ALL, основанные на порядке столбцов в предложении GROUP BY. (В вашем примере это исключение отображается как аккуратный треугольный шаблон ALL в вашем результирующем наборе). MDX не делает этого: на самом деле он не заботится о порядке наборов в вашем кросс-соединении.

Есть способ сделать это, сделав MDX осведомленным об этом заказе. Это немного сложный, но может быть проще (или более эффективные), чем длинный «handbuilt» подход вы пытались:

WITH 
MEMBER Measures.DimensionsAllPattern AS 
    CASE WHEN [Dimension1].[Hierarchy].CurrentMember.Properties("LEVEL_NUMBER")="0" THEN "1" ELSE "0" END + 
    CASE WHEN [Dimension2].[Hierarchy].CurrentMember.Properties("LEVEL_NUMBER")="0" THEN "1" ELSE "0" END + 
    CASE WHEN [Dimension3].[Hierarchy].CurrentMember.Properties("LEVEL_NUMBER")="0" THEN "1" ELSE "0" END + 
    ... etc up to dimension 8... 
MEMBER AllPatternStrNum AS VBA!Cstr(VBA!CLng(Measures.DimensionsAllPattern)) 

SELECT 
{Measures.DimensionsAllPattern,Measures.AllPatternStrNum} ON 0, 
FILTER 
    (CROSSJOIN 
     ([Dimension1].[Hierarchy].AllMembers, 
     [Dimension2].[Hierarchy].AllMembers, 
     .... etc 
     ) 
    , 
    (Measures.AllPatternStrNum="0") OR 
    (Measures.AllPatternStrNum=VBA!String(VBA!Len(Measures.AllPatternStrNum),"1")) 
    ) 
ON 1 
FROM TheCube 

Что это делает это:

  1. Для каждой комбинации членов из размеров, строя строку, соответствующую шаблону All/Non-All, на основе определенного порядка измерений. Например, {All, Something, All, Something} будет кодироваться как 1010.

  2. Второй вычисляемый элемент преобразует эту меру в ряд, а затем обратно в строку: так что 1010 будет в конечном итоге, как 1010, но 0011 будет в конечном итоге, как 11 (простой способ зачистки от ведущих нулей)

  3. Затем сшитый набор фильтруется в соответствии с этим вторым элементом. Он должен либо быть эквивалентным 0 (вообще нет значений ALL), либо быть строкой из 1 с той же длины, что и ее длина.

(Примечание. Я не включил в мой пример какие-либо предметы NonEmpty или меры, которые вы действительно хотите увидеть).

Возможно, вам нужно будет обернуть фильтрованный набор в ORDER (установить, что-то, BASC), чтобы он выглядел так, как вы хотите.

+0

Забыл включить в мой пример непустой (хотя я не включаю в него второй аргумент, чтобы это могло помочь). Я * думаю *, что это то, что жалуется на 4 миллиарда кортежей, хотя, поэтому решение в вашем ответе может быть способом пойти - это довольно умно! Я вернусь здесь, чтобы согласиться, если это сработает :) – Jeff

+0

Ну, это почти работает - я успешно его работаю на 5 слоев, но гораздо больше, и он начинает жаловаться, что 'Операция Auto Exist превышает допустимый предел памяти и отменяется. «Если я не получу лучшего ответа в течение выходных, я соглашусь с вашим ответом, поскольку он технически выполняет эту работу (и лучше синтаксически, чем длинная рука в моем вопросе) – Jeff

+0

Похоже, это может быть просто проблема с размером перекрестного набора, независимо от наших попыток получить из нее набор ROLLUP. Работает ли это, если вы просто переходите к 8 измерениям и показываете меру без каких-либо умных вещей? – SebTHU

1

Недавно я опубликовал статью об этом. Чтобы избежать проблем с перекрестными соединениями, я бы рекомендовал обернуть наборы с помощью функции NonEmptyCrossJoin. Подробнее читайте здесь: http://blog.soft-prestidigitation.com/the-totals-and-the-nonemptycrossjoin-function.html

+0

Это круто - мы все-таки закончили подход с длинным рукавом, так как мы могли бы получить умеренную производительность - я попробую это, чтобы увидеть, улучшает ли производительность, потому что это было бы полезно. Это не позволяет нам делать это без написания массивного запроса, но он выглядит все более и более похожим на то, что этого не произойдет. Благодаря! – Jeff

+0

Похоже, что это не улучшило производительность значительно - я не уверен, почему, но я думаю, что это ситуационная вещь. – Jeff

+0

Не могли бы вы предоставить код, который используете? –

2

Одним из способов ускорения этого типа MDX является не делать все за один шаг. То же самое относится к SQL. Используйте субкубы.

CREATE SUBCUBE [CubeName] AS 
SELECT {SomeMeasures} ON COLUMNS, 
{ 
    CROSSJOIN({Field1.ALLMEMBERS}, 
      {Field2.ALLMEMBERS}, 
        More as needed     
           ) } 
ON ROWS 
FROM [CubeName] 

Довольно странно, SubCube должен иметь то же имя, что и куб/перспектива, которую вы используете.

Вы можете создать столько подкубов, сколько вам нужно, все с тем же именем, а затем сделать окончательную инструкцию SELECT из суженного куба.

Опустите субкуб после этого.

Смежные вопросы