Translate window function into DAX - dax

i have a logic within a stored procedure that I'd like to translate into DAX.
Since i am rather new and I am probably searching with the wrong keywords, i cannot figure out how to transform this windowing function into propper dax.
select FIRST_VALUE(v) OVER(PARTITION BY partid ORDER BY somedate DESC, somenumber DESC) as myval
FROM table1
The query takes the first value of a group flagged wit hthe same partid (respecting the descending order of first somedate and then somenumber).
Can you please hint me into the right direction?

The following would give you one value based on the highest somedate and somenumber. If you bring in the someid as a slicer then you should get what you're looking for.
=
CALCULATE (
MAX ( 'Table'[Column] ),
FILTER (
'Table',
[somedate] = CALCULATE ( MAX ( [somedate] ), ALL ( 'Table' ) )
),
FILTER (
'Table',
[somedate] = CALCULATE ( MAX ( [somenumber] ), ALL ( 'Table' ) )
)
)

Related

ALL context removal does not apply

I have defined the following table :
The following measure does not work as expected :
VAR lt_MAX_DATE =
FILTER(
ALL( dim_CALENDAR[DATE] ) ,
dim_CALENDAR[DATE] = MAX( dim_CALENDAR[DATE] )
)
VAR lv_MAX_YEAR_DAY =
CALCULATE(
VALUES( dim_CALENDAR[YEAR_DAY] ) ,
ALL( dim_CALENDAR ) ,
lt_MAX_DATE
)
RETURN
lv_MAX_YEAR_DAY
as I get :
In my mind, I should get 10 for both rows of the Max Date ALL column, but this is not the case.
My question is : why does a dim_CALENDAR[YEAR] context apply even though I have used the ALL() function?
Thank you for your help.
If you want the DAX measure to return the max Year_day of maxYear by ignoring no matter what year is currently visible in the filter context, you can achieve it in many ways. Two of them are as below.
Measure1 =
VAR mxYr =
CALCULATE ( MAX ( dim_Calendar[Year] ), ALL ( dim_Calendar[Year] ) )
RETURN
CALCULATE (
MAX ( dim_Calendar[Year_day] ),
dim_Calendar[Year]=mxYr
)
Measure2 =
VAR mxYr =
CALCULATE ( MAX ( dim_Calendar[Year] ), ALL ( dim_Calendar[Year] ) )
RETURN
CALCULATE (
MAX ( dim_Calendar[Year_day] ),
TREATAS ( { mxYr }, dim_Calendar[Year] )
)
This expression
VAR lt_MAX_DATE =
FILTER(
ALL( dim_CALENDAR[DATE] ) ,
dim_CALENDAR[DATE] = MAX( dim_CALENDAR[DATE] )
)
is the same as
VAR YEARinMatrixRow = SELECTEDVALUE[dim_CALENDAR[YEAR]]
VAR lt_MAX_DATE =
CALCULATETABLE (
FILTER(
ALL( dim_CALENDAR[DATE] ) ,
dim_CALENDAR[DATE] = MAX( dim_CALENDAR[DATE] )
)
,dim_CALENDAR[YEAR] = YEARinMatrixRow
)
So, the result of the expression is the last date of the selected (in Row) year. You get a Row filtering.
The second expression filters the table by
ALL( dim_CALENDAR )
,lt_MAX_DATE
According to DAX priorities, first works ALL(dim_CALENDAR) , then you apply filter - lt_MAX_DATE
So, you get the dim_CALENDAR table, simply filtered by the last day of the year in a matrix row where you get a single value with the VALUES( dim_CALENDAR[YEAR_DAY] ).
You overite ALL( dim_CALENDAR ) by the - lt_MAX_DATE
Normaly, you will get an error with lv_MAX_YEAR_DAY syntax. Calculate returns scalar value, while VALUES() function returns a table even with 1 cell.
As #Mik says, you are restricting the context with
dim_CALENDAR[DATE] = MAX( dim_CALENDAR[DATE] )

How to pass a filtered table to RankX

I would like RankX to rank products for Var2 and only consider/see products who have a value for Var1 (so only A and B and not C).
Here is an example datamodel including the resulting pivottable
And here are the measures I use:
SUM Value = SUM ( Data[Value] )
Var1 Check = CALCULATE ( COUNTROWS ( Data ), Variable[Variable] = "Var1" )
RankX = RANKX ( ALL ( 'Product' ), [SUM Value] )
RankX Filter = IF ( ISBLANK ( [Var1 Check] ), BLANK (), [RankX] )
The idea is that my filtered RankX function (RankX Filter) shows 2 instead of 3 for Product B as only A and B should be considered.
Using DAX Studio I managed to filter my product table accordingly but I don't know whether this the right approach nor how to pass that filtered table to a RankX function.
FILTER (
ADDCOLUMNS (
'Product',
"Var1Check", CALCULATE ( COUNTROWS ( Data ), Variable[Variable] = "Var1" )
),
[Var1Check] = 1
)
You should just be able to put the filtered table as the first argument in your RANKX function. Something along these lines:
RANKX ( FILTER ( 'Product', Variable[Variable] = "Var1" ), [SUM Value] )

How to display data with pivot in oracle?

I stuck an hours to display the data in weekly result, but I'm always fail to display that. I'm new in pivot.
Please help me.
Thank you!
Sample data need to produce:
Data,03/10/2014,200,150,186,172
Data,03/16/2014,144,115,181,157
Data,03/22/2014,130,198,136,393
Here's the script:
select 'Data,'''
||(
select listagg(SIM_id,''',''') within group (order by 1)
from (
select distinct substr(dst_channel,1,14) as SIM_id
from table1
where dst_channel like 'SIP/item__-%'
)
)
from dual;
select 'Data'
||','||dtime_day
||','||g1_cnt
||','||g2_cnt
||','||g3_cnt
||','||g4_cnt
from (
select 'Data'
,to_char(d.dtime_day,'MM/dd/yyyy') as dtime_day
,substr(c.dst_channel,1,14) as dst_channel
from table2 d
left join table1
on c.call_date = d.dtime_day
where c.dst_channel like 'SIP/item%'
and c.status like 'ANSWERED%'
and d.dtime_day between trunc(sysdate, 'IW')-(12*7) and trunc(sysdate) -1
)
pivot(
count(dst_channel) as cnt
for dst_channel in (
'SIP/item01' as g1,'SIP/item02' as g2,'SIP/item03' as g3,'SIP/item04' as g4
)
)
order by dtime_day
Result of the query above:
Data,03/10/2014,147,103,86,76
Data,03/11/2014,144,115,81,57
Data,03/12/2014,130,98,59,39

self join with max value

I am have a table with 500k transactions. I want to fetch the last balance for a particular date. So I have have returned a query like below.
SELECT curr_balance
FROM transaction_details
WHERE acct_num = '10'
AND is_deleted = 'N'
AND ( value_date, srl_num ) IN(
SELECT MAX( value_date ), MAX( srl_num )
FROM transaction_details
WHERE TO_DATE( value_date, 'dd/mm/yyyy' )
<= TO_DATE( ADD_MONTHS( '05-APR-2012', 1 ), 'dd/mm/yyyy' )
AND acct_num = '10'
AND is_deleted = 'N'
AND ver_status = 'Y' )
AND ver_status = 'Y'
This has to be executed for incrementing of 12 months to find the last balance for each particular month. But this query is having more cpu cost, 12 times it is taking huge time. How to remodify the above query to get the results in faster way. Whether this can be broken into two part in PL/SQL to achieve the performance. ?
Try:
select * from(
SELECT value_date, srl_num, curr_balance
FROM transaction_details
WHERE acct_num = '10'
AND is_deleted = 'N'
AND ver_status = 'Y'
row_number() over (partition by trunc(value_date - interval '5' day,'MM')
order by srl_num desc
) as rnk
)
where rnk = 1;
You'll get a report with the ballance on last srl_num on each month in your table.
The benefit is that your approach scans the table 24 times for 12 months report and my approach scans the table once.
The analytic function gets the rank of record in current month(partition by clause) ordering the rows in the month after srl_num.
You don't have to query your table twice. Try using analytic functions
SELECT t.curr_balance
-- , any other column you want as long it is in the subselect.
FROM (
SELECT
trans.curr_balance
, trans.value_date
-- any other column you want
, trans.srl_num
, MAX(trans.srl_num) OVER(PARTITION BY trans.value_date, trans.srl_num) max_srl_num
, MAX(trans.value_date) OVER(PARTITION BY trans.value_date, trans.srl_num) max_date
FROM transaction_details trans
WHERE TO_DATE( value_date, 'dd/mm/yyyy' ) <= TO_DATE( ADD_MONTHS( '01-APR-2012', 1 ), 'dd/mm/yyyy' )
AND acct_num = '10'
AND is_deleted = 'N'
AND ver_status = 'Y'
) t
WHERE t.max_date = t.value_date
AND t.max_srl_num = t.srl_num
A couple of thoughts.
Why do you have TO_DATE( value_date...? Isn't your data type DATE? this might be breaking your index if you have one in that column.
Note that (this is a wild guess) if your srl_num is not the highest for the latest date, you will have incorrect results and might not return any rows.

How do I get rowcount of a cte in a separate dataset?

I have identified a way to get fast paged results from the database using CTEs and the Row_Number function, as follows...
DECLARE #PageSize INT = 1
DECLARE #PageNumber INT = 2
DECLARE #Customer TABLE (
ID INT IDENTITY(1, 1),
Name VARCHAR(10),
age INT,
employed BIT)
INSERT INTO #Customer
(name,age,employed)
SELECT 'bob',21,1
UNION ALL
SELECT 'fred',33,1
UNION ALL
SELECT 'joe',29,1
UNION ALL
SELECT 'sam',16,1
UNION ALL
SELECT 'arthur',17,0;
WITH cteCustomers
AS ( SELECT
id,
Row_Number( ) OVER(ORDER BY Age DESC) AS Row
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT
name,
age,
Total = ( SELECT
Count( id )
FROM cteCustomers )
FROM cteCustomers
INNER JOIN #Customer cust
/*This is where I choose the columns I want to read, it returns really fast!*/
ON cust.id = cteCustomers.id
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
Using this technique the returned results is really really fast even on complex joins and filters.
To perform paging I need to know the Total Rows returned by the full CTE. I have "Bodged" this by putting a column that holds it
Total = ( SELECT
Count( id )
FROM cteCustomers )
Is there a better way to return the total in a different result set without bodging it into a column? Because it's a CTE I can't seem to get it into a second result set.
Without using a temp table first, I'd use a CROSS JOIN to reduce the risk of row by row evaluation on the COUNT
To get total row, this needs to happen separately to the WHERE
WITH cteCustomers
AS ( SELECT
id,
Row_Number( ) OVER(ORDER BY Age DESC) AS Row
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT
name,
age,
Total
FROM cteCustomers
INNER JOIN #Customer cust
/*This is where I choose the columns I want to read, it returns really fast!*/
ON cust.id = cteCustomers.id
CROSS JOIN
(SELECT Count( *) AS Total FROM cteCustomers ) foo
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
However, this isn't guaranteed to give accurate results as demonstrated here:
can I get count() and rows from one sql query in sql server?
Edit: after a few comments.
How to avoid a CROSS JOIN
WITH cteCustomers
AS ( SELECT
id,
Row_Number( ) OVER(ORDER BY Age DESC) AS Row,
COUNT(*) OVER () AS Total --the magic for this edit
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT
name,
age,
Total
FROM cteCustomers
INNER JOIN #Customer cust
/*This is where I choose the columns I want to read, it returns really fast!*/
ON cust.id = cteCustomers.id
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
Note: YMMV for performance depending on 2005 or 2008, Service pack etc
Edit 2:
SQL Server Central shows another technique where you have reverse ROW_NUMBER. Looks useful
#Digiguru
OMG, this really is the wholy grail!
WITH cteCustomers
AS ( SELECT id,
Row_Number() OVER(ORDER BY Age DESC) AS Row,
Row_Number() OVER(ORDER BY id ASC)
+ Row_Number() OVER(ORDER BY id DESC) - 1 AS Total /*<- voodoo here*/
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT name, age, Total
/*This is where I choose the columns I want to read, it returns really fast!*/
FROM cteCustomers
INNER JOIN #Customer cust
ON cust.id = cteCustomers.id
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
So obvious now.

Resources