MDX query for nestings tops - mdx

Help with mdx query plz (Essbase 11.1.1)
I have 3 dimensions
[YEAR]
[SF]
[OPR_KIND]
1 level hierarchy for all
Measure: [Accounts].[FCT_OPR_SUM]
I have to find:
top 2 years, for each year 3 top SF, and for each SF 1 top OPR_KIND
for 1 nested top I do:
WITH
SET top2Year as 'TopCount( [YEAR].Generations(2).MEMBERS,2)'
SELECT {[Accounts].[FCT_OPR_SUM]} ON COLUMNS,
NON EMPTY
{
generate(top2Year,
crossjoin( top2Year,topcount( [SF].Generations(2).MEMBERS,3)
)
)
}
ON ROWS
FROM cube1f.cube1f
How to do for 2 or more nested tops?

You can do it with something close to this:
Generate(TopCount([YEAR].Generations(2).MEMBERS, 2, [Accounts].[FCT_OPR_SUM]),
{[YEAR].CurrentMember} * Generate(TopCount([SF].Generations(2).MEMBERS, 3, [Accounts].[FCT_OPR_SUM]),
{[SF].CurrentMember} * TopCount('your OPR_KIND set', 1, [Accounts].[FCT_OPR_SUM])
)
)

Related

Power BI - Using Dax to Filter Based on a Group By

I am new to DAX.
Let's pretend I have a table that looks like this:
Table A:
status delivered sold
late 10 50
late 20 300
early 5 500
Let's pretend I am using this SQL query:
with cte_1 as (
select
status, count(*) as [row_count]
from [table a]
group by [status]
having count(*) > 1
)
select *
from [table a] as p1
inner join [cte_1] as p2
on p1.[status] = p2.[status]
What would be the dax equivalent of this?
The SQL query return the Table A rows with the status that occurrs at least twice in the table, adding the count of the number of rows with the same status. In Power BI we can write a calculated table that adds the count of the rows of the same status and then filter out those with a count less than 2
Result =
FILTER(
ADDCOLUMNS(
'Table A',
"row_count",
CALCULATE(
COUNTROWS( 'Table A' ),
ALLEXCEPT( 'Table A', 'Table A'[Status] )
)
),
[row_count] > 1
)

To fetch parent and its children connected thru one attribute

Query to fetch the SEC_CODE of parent its child and all grand children. Children are connected through PREV_SEC value.
According to above image,
1. Here if sec_code is 1 then the query should return 1, 3, 4 7, 8
2. if sec_code is 2 then query return 2.
3. if sec_code is 4 then query return 4,7,8
You can use recursive CTE to get the desired output like following query.
WITH CTE2 (SEC_CODE,SEC_NAME,PREV_SEC)
AS (
SELECT SEC_CODE,SEC_NAME,PREV_SEC
FROM YOURTABLE
WHERE SEC_CODE = 1
UNION ALL
SELECT C1.SEC_CODE,C1.SEC_NAME,C1.PREV_SEC
FROM YOURTABLE C1
INNER JOIN CTE2 C2 ON C1.PREV_SEC = C2.SEC_CODE
)
SELECT *
FROM CTE2
Note: Replace YOURTABLE with the actual table name in above query.
If you strictly need an Oracle specific solution, You may try below query having CONNECT BY clause -
SELECT SEC_CODE,SEC_NAME,PREV_SEC
FROM YOURTABLE
START WITH SEC_CODE = 1
CONNECT BY PRIOR PREV_SEC = SEC_CODE;

MDX queries. How can to solve "IN" in "where" part

I have sql query and i need this query execute in olap cube .
select count(distinct mi.id) from [MTD_DEV].[dbo].[MenuItemAttributes] as m
inner join [dbo].[MenuItemOlds] as mi
on mi.id = m.MenuItemId
inner join [dbo].[RestaurantlistItems] as rl
on rl.RestaurantId = mi.RestaurantId
where m.AttributeId = 31 and rl.RestaurantListId = 69 and mi.PeriodId = 99 and m.MenuItemId in (select MenuItemId from [MTD_DEV].[dbo].[MenuItemAttributes] where AttributeId = 6
and i have working mdx query and I need to add operator 'IN' or something another solution for this query
SELECT CROSSJOIN(
{[Measures].[Menu Item Olds Count],[Measures].[Restaurantlist Items Count]},
{[Periods].[Id].[99],[Periods].[Id].[93],[Periods].[Id].[75]}) ON COLUMNS,
{[Menu Item Olds].[id]} ON ROWS
FROM [MTD DEV]
where (
{[Restaurant Lists].[Id].[69]},
{[Attributes].[Id].[6]} ,
{[Attribute Categories].[Id].[5]} -- or can use the same parameter {[Attributes].[Id].[31]}
)
for better understanding:
https://drive.google.com/file/d/0B3rw0YPItJIIa3FfNEtrVC04SVU/view?usp=sharing
Additional Comments to question
In ms sql I have to slice MenuItemOlds by some parameter m.AttributeId = 31
annd then from result I have to slice again for parameter AttributeId = 6.
In Sql it looks like this:
select count(distinct mi.id) from [MTD_DEV].[dbo].[MenuItemAttributes] as m
inner join [dbo].[MenuItemOlds] as mi on mi.id = m.MenuItemId
where m.AttributeId = 31 and m.MenuItemId in (select MenuItemId from [MTD_DEV].[dbo].[MenuItemAttributes] where AttributeId = 6
I have problem in OLAP Cube.
How I see to solve this problem :
1.I get all data where AttributeId = 31
SELECT CROSSJOIN(
{[Measures].[Menu Item Olds Count],[Measures].[Restaurantlist Items Count]},
{[Periods].[Id].[99],[Periods].[Id].[93],[Periods].[Id].[75]}) ON COLUMNS,
{[Menu Item Olds].[id]} ON ROWS
FROM [MTD DEV]
where ({[Attributes].[Id].[31]})
the result of this - all catering menu items
After this , in this collection of Menu Items, I need to find all menu items where {[Attributes].[Id].[6]} (kids menu)
When i am trying to execute such query :
SELECT CROSSJOIN(
{[Measures].[Menu Item Olds Count],[Measures].[Restaurantlist Items Count]},
{[Periods].[Id].[99],[Periods].[Id].[93],[Periods].[Id].[75]}) ON COLUMNS,
{[Menu Item Olds].[id]} ON ROWS
FROM [MTD DEV]
where (
{[Attributes].[Id].[6]} ,
{[Attributes].[Id].[31]}
)
I get result , in which i have Menu items with AttributeId.[6] + menuItem with attributeId.[31]
for example:
count of menu items with AttributeId.[6] = 11000 items
count of menu items with AttributeId.[31] = 724000items
and result is 724000+11000 = 735000 but i don`t need it
i need to find all items with AttributeId.[31], and in this collection i need to find items with AttributeId.[6]
The right result of query must be less than 11000 items
Does NonEmpty with intersect work as an alternative?
SELECT
{
[Measures].[Menu Item Olds Count]
,[Measures].[Restaurantlist Items Count]
}
*
{
[Periods].[Id].[99]
,[Periods].[Id].[93]
,[Periods].[Id].[75]
} ON COLUMNS
,Intersect
(
NonEmpty
(
[Menu Item Olds].[id].[id].MEMBERS
,(
[Attributes].[Id].[31]
,{
[Measures].[Menu Item Olds Count]
,[Measures].[Restaurantlist Items Count]
}
)
)
,NonEmpty
(
[Menu Item Olds].[id].[id].MEMBERS
,(
[Attributes].[Id].[6]
,{
[Measures].[Menu Item Olds Count]
,[Measures].[Restaurantlist Items Count]
}
)
)
) ON ROWS
FROM [MTD DEV];
This would be a classical case for Exists, slightly complicated as you have a many-to-many relationship between Menu Item Olds and Attributes:
SELECT CROSSJOIN(
{[Measures].[Menu Item Olds Count],[Measures].[Restaurantlist Items Count]},
{[Periods].[Id].[99],[Periods].[Id].[93],[Periods].[Id].[75]})
ON COLUMNS,
Exists(Exists([Menu Item Olds].[id].[id].Members,
{[Attributes].[Id].[31]}
),
{[Attributes].[Id].[6]}
)
ON ROWS
FROM [MTD DEV]

View to identify grouped values or object

As an example I have 5 objects. An object is the red dots bound together or adjacent to each other. In other words X+1 or X-1 or Y+1 or Y-1.
I need to create a MS SQL VIEW with will contain the first XY coordinate of each object like:
X,Y
=======
1. 1,1
2. 1,8
3. 4,3
4. 5,7
5. 6,5
I can't figure out how to group it in a VIEW (NOT using stored procedure). Anybody have any idea would be of great help.
Thanks
The other answer is already pretty long, so I'm leaving it as-is. This answer is much better, simpler and also correct whereas the other one has some edge-cases that will produce a wrong answer - I shall leave that exercise to the reader.
Note: Line breaks are added for clarity. The entire block is a single query
;with Walker(StartX,StartY,X,Y,Visited) as (
select X,Y,X,Y,CAST('('+right(X,3)+','+right(Y,3)+')' as Varchar(Max))
from puzzle
union all
select W.StartX,W.StartY,P.X,P.Y,W.Visited+'('+right(P.X,3)+','+right(P.Y,3)+')'
from Walker W
join Puzzle P on
(W.X=P.X and W.Y=P.Y+1 OR -- these four lines "collect" a cell next to
W.X=P.X and W.Y=P.Y-1 OR -- the current one in any direction
W.X=P.X+1 and W.Y=P.Y OR
W.X=P.X-1 and W.Y=P.Y)
AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'
)
select X, Y, Visited
from
(
select W.X, W.Y, W.Visited, rn=row_number() over (
partition by W.X,W.Y
order by len(W.Visited) desc)
from Walker W
left join Walker Other
on Other.StartX=W.StartX and Other.StartY=W.StartY
and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
where Other.X is null
) Z
where rn=1
The first step is to set up a "walker" recursive table expression that will start at every
cell and travel as far as it can without retracing any step. Making sure that cells are not revisited is done by using the visited column, which stores each cell that has been visited from every starting point. In particular, this condition AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%' rejects cells that it has already visited.
To understand how the rest works, you need to look at the result generated by the "Walker" CTE by running "Select * from Walker order by StartX, StartY" after the CTE. A "piece" with 5 cells appears in at least 5 groups, each with a different (StartX,StartY), but each group has all the 5 (X,Y) pieces with different "Visited" paths.
The subquery (Z) uses a LEFT JOIN + IS NULL to weed the groups down to the single row in each group that contains the "first XY coordinate", defined by the condition
Other.StartX=W.StartX and Other.StartY=W.StartY
and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
The intention is for each cell that can be visited starting from (StartX, StartY), to compare against each other cell in the same group, and to find the cell where NO OTHER cell is on a higher row, or if they are on the same row, is to the left of this cell. This still leaves us with too many results, however. Consider just a 2-cell piece at (3,4) and (4,4):
StartX StartY X Y Visited
3 4 3 4 (3,4) ******
3 4 4 4 (3,4)(4,4)
4 4 4 4 (4,4)
4 4 3 4 (4,4)(3,4) ******
2 rows remain with the "first XY coordinate" of (3,4), marked with ******. We only need one row, so we use Row_Number and since we're numbering, we might as well go for the longest Visited path, which would give us as many of the cells within the piece as we can get.
The final outer query simply takes the first rows (RN=1) from each similar (X,Y) group.
To show ALL the cells of each piece, change the line
select X, Y, Visited
in the middle to
select X, Y, (
select distinct '('+right(StartX,3)+','+right(StartY,3)+')'
from Walker
where X=Z.X and Y=Z.Y
for xml path('')
) PieceCells
Which give this output
X Y PieceCells
1 1 (1,1)(2,1)(2,2)(3,2)
3 4 (3,4)(4,4)
5 6 (5,6)
7 5 (7,5)(8,5)(9,5)
8 1 (10,1)(8,1)(8,2)(9,1)(9,2)(9,3)
Ok. Its little bit hard. But in any case, I'm sure that in a simpler way this problem can not be solved.
So we have table:
CREATE Table Tbl1(Id int, X int, Y int)
INSERT INTO Tbl1
SELECT 1,1,1 UNION ALL
SELECT 2,1,2 UNION ALL
SELECT 3,1,8 UNION ALL
SELECT 4,1,9 UNION ALL
SELECT 5,1,10 UNION ALL
SELECT 6,2,2 UNION ALL
SELECT 7,2,3 UNION ALL
SELECT 8,2,8 UNION ALL
SELECT 9,2,9 UNION ALL
SELECT 10,3,9 UNION ALL
SELECT 11,4,3 UNION ALL
SELECT 12,4,4 UNION ALL
SELECT 13,5,7 UNION ALL
SELECT 14,5,8 UNION ALL
SELECT 15,5,9 UNION ALL
SELECT 16,6,5
And here is select query
with cte1 as
/*at first we make recursion to define groups of filled adjacent cells*/
/*as output of cte we have a lot of strings like <X>cell(1)X</X><Y>cell(1)Y</Y>...<X>cell(n)X</X><Y>cell(n)Y</Y>*/
(
SELECT id,X,Y,CAST('<X>'+CAST(X as varchar(10))+'</X><Y>'+CAST(Y as varchar(10))+'</Y>' as varchar(MAX)) info
FROM Tbl1
UNION ALL
SELECT b.id,a.X,a.Y,CAST(b.info + '<X>'+CAST(a.X as varchar(10))+'</X><Y>'+CAST(a.Y as varchar(10))+'</Y>' as varchar(MAX))
FROM Tbl1 a JOIN cte1 b
ON ((((a.X=b.X+1) OR (a.X=b.X-1)) AND a.Y=b.Y) OR (((a.Y=b.Y+1) OR (a.Y=b.Y-1)) AND a.X=b.X))
AND a.id<>b.id
AND
b.info NOT LIKE
('%'+('<X>'+CAST(a.X as varchar(10))+'</X><Y>'+CAST(a.Y as varchar(10))+'</Y>')+'%')
),
cte2 as
/*In this query, we select only the longest sequence of cell connections (first filter)*/
/*And we convert the string to a new standard (x,y | x,y | x,y |...| x,y) (for further separation)*/
(
SELECT *, ROW_NUMBER()OVER(ORDER BY info) cellGroupId
FROM(
SELECT REPLACE(REPLACE(REPLACE(REPLACE(info,'</Y><X>','|'),'</X><Y>',','),'<X>',''),'</Y>','') info
FROM(
SELECT info, MAX(LEN(info))OVER(PARTITION BY id)maxlen FROM cte1
) AS tmpTbl
WHERE maxlen=LEN(info)
)AS tmpTbl
),
cte3 as
/*In this query, we separated strings like (x,y | x,y | x,y |...| x,y) to many (x,y)*/
(
SELECT cellGroupId, CAST(LEFT(XYInfo,CHARINDEX(',',XYInfo)-1) as int) X, CAST(RIGHT(XYInfo,LEN(XYInfo)-CHARINDEX(',',XYInfo)) as int) Y
FROM(
SELECT cellGroupId, tmpTbl2.n.value('.','varchar(MAX)') XYinfo
FROM
(SELECT CAST('<r><c>' + REPLACE(info,'|','</c><c>')+'</c></r>' as XML) n, cellGroupId FROM cte2) AS tmpTbl1
CROSS APPLY n.nodes('/r/c') tmpTbl2(n)
) AS tmpTbl
),
cte4 as
/*In this query, we finally determined group of individual objects*/
(
SELECT cellGroupId,X,Y
FROM(
SELECT cellGroupId,X,Y,ROW_NUMBER()OVER(PARTITION BY X,Y ORDER BY cellGroupId ASC)rn
FROM(
SELECT *,
MAX(SumOfAdjacentCellsByGroup)OVER(PARTITION BY X,Y) Max_SumOfAdjacentCellsByGroup_ByXY /*calculated max value of <the sum of the cells in the group> by each cell*/
FROM(
SELECT *, SUM(1)OVER(PARTITION BY cellGroupId) SumOfAdjacentCellsByGroup /*calculated the sum of the cells in the group*/
FROM cte3
)AS TmpTbl
)AS TmpTbl
/*We got rid of the subgroups (i.e. [(1,2)(2,2)(2,3)] its subgroup of [(1,2)(1,1)(2,2)(2,3)])*/
/*it was second filter*/
WHERE SumOfAdjacentCellsByGroup=Max_SumOfAdjacentCellsByGroup_ByXY
)AS TmpTbl
/*We got rid of the same groups (i.e. [(1,1)(1,2)(2,2)(2,3)] its same as [(1,2)(1,1)(2,2)(2,3)])*/
/*it was third filter*/
WHERE rn=1
)
SELECT X,Y /*result*/
FROM(SELECT a.X,a.Y, ROW_NUMBER()OVER(PARTITION BY cellGroupId ORDER BY id)rn
FROM cte4 a JOIN Tbl1 b ON a.X=b.X AND a.Y=b.Y)a /*connect back*/
WHERE rn=1 /*first XY coordinate*/
Let's assume your coordinates are stored in X,Y form, something like this:
CREATE Table Puzzle(
id int identity, Y int, X int)
INSERT INTO Puzzle VALUES
(1,1),(1,2),(1,8),(1,9),(1,10),
(2,2),(2,3),(2,8),(2,9),
(3,9),
(4,3),(4,4),
(5,7),(5,8),(5,9),
(6,5)
This query then shows your Puzzle in board form (run in TEXT mode in SQL Management Studio)
SELECT (
SELECT (
SELECT CASE WHEN EXISTS (SELECT *
FROM Puzzle T
WHERE T.X=X.X and T.Y=Y.Y)
THEN 'X' ELSE '.' END
FROM (values(0),(1),(2),(3),(4),(5),
(6),(7),(8),(9),(10),(11)) X(X)
ORDER BY X.X
FOR XML PATH('')) + Char(13) + Char(10)
FROM (values(0),(1),(2),(3),(4),(5),(6),(7)) Y(Y)
ORDER BY Y.Y
FOR XML PATH(''), ROOT('a'), TYPE
).value('(/a)[1]','varchar(max)')
It gives you this
............
.XX.....XXX.
..XX....XX..
.........X..
...XX.......
.......XXX..
.....X......
............
This query done in 4 stages will give you the result of the TopLeft cell, if you define it as the Leftmost cell of the TopMost row.
-- the first table expression joins cells together on the Y-axis
;WITH FlattenOnY(Y,XLeft,XRight) AS (
-- start with all pieces
select Y,X,X
from puzzle
UNION ALL
-- keep connecting rightwards from each cell as far as possible
select B.Y,A.XLeft,B.X
from FlattenOnY A
join puzzle B on A.Y=B.Y and A.XRight+1=B.X
)
-- the second table expression flattens the results from the first, so that
-- it represents ALL the start-end blocks on each row of the Y-axis
,YPieces(Y,XLeft,XRight) as (
--
select Y,XLeft,Max(XRight)
from(
select Y,Min(XLeft)XLeft,XRight
from FlattenOnY
group by XRight,Y)Z
group by XLeft,Y
)
-- here, select * from YPieces will return the "blocks" such as
-- Row 1: 1-2 & 8-10
-- Row 2: 2-3 (equals Y,XLeft,XRight of 2,2,3)
-- etc
-- the third expression repeats the first, except it now combines on the X-axis
,FlattenOnX(Y,XLeft,CurPieceXLeft,CurPieceXRight,CurPieceY) AS (
-- start with all pieces
select Y,XLeft,XLeft,XRight,Y
from YPieces
UNION ALL
-- keep connecting rightwards from each cell as far as possible
select A.Y,A.XLeft,B.XLeft,B.XRight,B.Y
from FlattenOnX A
join YPieces B on A.CurPieceY+1=B.Y and A.CurPieceXRight>=B.XLeft and B.XRight>=A.CurPieceXLeft
)
-- and again we repeat the 2nd expression as the 4th, for the final pieces
select Y,XLeft X
from (
select *, rn2=row_number() over (
partition by Y,XLeft
order by CurPieceY desc)
from (
select *, rn=row_number() over (
partition by CurPieceXLeft, CurPieceXRight, CurPieceY
order by Y)
from flattenOnX
) Z1
where rn=1) Z2
where rn2=1
The result being
Y X
----------- -----------
1 1
1 8
4 3
5 7
6 5
Or is your representation in flat form something like this? If it is, give us a shout and I'll redo the solution
create table Puzzle (
row int,
[0] bit, [1] bit, [2] bit, [3] bit, [4] bit, [5] bit,
[6] bit, [7] bit, [8] bit, [9] bit, [10] bit, [11] bit
)
insert Puzzle values
(0,0,0,0,0,0,0,0,0,0,0,0,0),
(1,0,1,1,0,0,0,0,0,1,1,1,0),
(2,0,0,1,1,0,0,0,0,1,1,0,0),
(3,0,0,0,0,0,0,0,0,0,1,0,0),
(4,0,0,0,1,1,0,0,0,0,0,0,0),
(5,0,0,0,0,0,0,0,1,1,1,0,0),
(6,0,0,0,0,0,1,0,0,0,0,0,0),
(7,0,0,0,0,0,0,0,0,0,0,0,0)

SQL query to get the top "n" scores out of a list

I'd like to find the different ways to solve a real life problem I had: imagine to have a contest, or a game, during which the users collect points. You have to build a query to show the list of users with the best "n" scores.
I'm making an example to clarify. Let's say that this is the Users table, with the points earned:
UserId - Points
1 - 100
2 - 75
3 - 50
4 - 50
5 - 50
6 - 25
If I want the top 3 scores, the result will be:
UserId - Points
1 - 100
2 - 75
3 - 50
4 - 50
5 - 50
This can be realized in a view or a stored procedure, as you want. My target db is Sql Server. Actually I solved this, but I think there are different way to obtain the result... faster or more efficent than mine.
Untested, but should work:
select * from users where points in
(select distinct top 3 points from users order by points desc)
Here's one that works - I don't know if it's more efficient, and it's SQL Server 2005+
with scores as (
select 1 userid, 100 points
union select 2, 75
union select 3, 50
union select 4, 50
union select 5, 50
union select 6, 25
),
results as (
select userid, points, RANK() over (order by points desc) as ranking
from scores
)
select userid, points, ranking
from results
where ranking <= 3
Obviously the first "with" is to set up the values, so you can test the second with, and final select work - you could start at "with results as..." if you were querying against an existing table.
How about:
select top 3 with ties points
from scores
order by points desc
Not sure if "with ties" works on anything other the SQL Server.
On SQL Server 2005 and up, you can pass the "top" number as an int parameter:
select top (#n) with ties points
from scores
order by points desc
Actually a modification to the WHERE IN, utilizing an INNER JOIN will be much faster.
SELECT
userid, points
FROM users u
INNER JOIN
(
SELECT DISTINCT TOP N
points
FROM users
ORDER BY points DESC
) AS p ON p.points = u.points
#bosnic, I don't think that will work as requested, I'm not that familiar with MS SQL but I would expect it to return only 3 rows, and ignore the fact that 3 users are tied for 3rd place.
Something like this should work:
select userid, points
from scores
where points in (select top 3 points
from scores
order by points desc)
order by points desc
#Rob#37760:
select top N points from users order by points desc
This query will only select 3 rows if N is 3, see the question. "Top 3" should return 5 rows.
#Espo thanks for the reality check - added the sub-select to correct for that.
I think the easiest response is to:
select userid, points from users
where points in (select distinct top N points from users order by points desc)
If you want to put that in a stored proc which takes N as a parameter, then you'll either have to do read the SQL into a variable then execute it, or do the row count trick:
declare #SQL nvarchar(2000)
set #SQL = "select userID, points from users "
set #SQL = #SQL + " where points in (select distinct top " + #N
set #SQL = #SQL + " points from users order by points desc)"
execute #SQL
or
SELECT UserID, Points
FROM (SELECT ROW_NUMBER() OVER (ORDER BY points DESC)
AS Row, UserID, Points FROM Users)
AS usersWithPoints
WHERE Row between 0 and #N
Both examples assume SQL Server and haven't been tested.
#Matt Hamilton
Your answer works with the example above but would not work if the data set was 100, 75, 75, 50, 50 (where it would return only 3 rows). TOP WITH TIES only includes the ties of the last row returned...
Crucible got it (assuming SQL 2005 is an option).
Hey I found all the other answers bit long and inefficient
My answer would be:
select * from users order by points desc limit 0,5
this will render top 5 points
Try this
select top N points from users order by points desc