DAX Measure for Cancellations (Cancel Date vs. Book Date) - dax

I have a 'Reservations' table that looks like this;
Reservation ID
Book Date
Status
Cancel Date
Booking Flag
1234
2021-01-01
Active
1
5678
2021-01-01
Active
1
9101
2021-01-01
Cancelled
2021-01-02
1
1121
2021-01-02
Cancelled
2021-01-04
1
3141
2021-01-02
Active
1
The 'Book Date' column is in an active relationship with a Calendar table via. the Date column. Current measures are like this;
Gross Bookings:SUM(Booking Flag)
Cancellations:CALCULATE([Gross Bookings],'Reservations'[Status]="Cancelled")
Net Bookings:[Gross Bookings]-[Cancellations]
I want to be able to produce a table like this;
Date
Gross Bookings
Cancellations
Net Bookings
2021-01-01
3
0
3
2021-01-02
2
1
1
2021-01-03
0
0
0
2021-01-01
0
1
-1
But what I am getting looks like this, as the 'Cancel Date' is not being understood in the Measures;
Date
Gross Bookings
Cancellations
Net Bookings
2021-01-01
3
1
2
2021-01-02
2
1
1
2021-01-03
0
0
0
2021-01-01
0
0
0
How can I use DAX to shift my Cancellation Measure so it corresponds to the correct Cancel Date whilst still respecting my Date relationship?

I believe it is solved now, by creating an Inactive Relationship between 'Cancel Date' and the Calendar Date table, then using this DAX for the Cancellation measure;
CALCULATE([Gross Bookings],'Reservations'[Status]="Cancelled",USERELATIONSHIP('Reservations'[Cancel Date],'Calendar'[Date]))
This seems to be doing the expected output, to be tested further. Worth noting I had to populate book date in the Cancel Date nulls, to create the relationship.

Related

Power Query - Merge tables - Sum values with conditions

I'm new to Power Query and I can't figure out how to do the following:
I have two tables
"REQUESTED"
Date
ReqNumber
Client
SKU
ReqQuantity
Jan-01
Z10
1
A
2
Feb-05
Z11
1
A
3
"SENT"
Date
Client
SKU
Quantity
Jan-15
1
A
1
Feb-02
1
A
3
Mar-10
1
A
5
What I want to achieve is that I want to merfe the tables and allow me to filter by date, showing the corresponding amount requested/sent
For Example:
If I filter dates between Jan-01 / Jan - 31
I should get the following:
"REQUESTED - SENT"
Date
ReqNumber
Client
SKU
ReqQuantity
SentQuantity
Jan-01
Z10
1
A
2
1
If I filter dates between Jan-01 / Feb - 28
I should get the following:
"REQUESTED - SENT"
Date
ReqNumber
Client
SKU
ReqQuantity
SentQuantity
Jan-01
Z10
1
A
2
2
Feb-05
Z11
1
A
3
2
If I filter dates between Jan-01 / Mar- 15
I should get the following:
"REQUESTED - SENT"
Date
ReqNumber
Client
SKU
ReqQuantity
SentQuantity
Jan-01
Z10
1
A
2
2
Feb-05
Z11
1
A
3
3
Is this posiible in Power Query?
Thanks!
I believe your question is a simple one that might get passed over because it is simple.
Two tables "Sent" "Requested"...
Merge is under the Home tab.
Set "Requested" as Left and "Sent" as right. Select date for both.
In the Sent column click on the expand icon.

Calculate total time based on column in Tableau

I have a table like below:
From Date
Issue Id
Issue Id (group)
Status
Till Date
19-07-2021 17:21
4
4
Approved
19-07-2021 17:23
19-07-2021 17:23
4
4
In Progress
19-07-2021 17:23
19-07-2021 17:23
4
4
In Review
19-07-2021 17:25
19-07-2021 17:25
4
4
In Progress
19-07-2021 18:56
19-07-2021 18:56
4
4
In Review
20-07-2021 08:47
20-07-2021 08:47
4
4
Resolved
20-07-2021 14:45
20-07-2021 14:45
4
4
Closed
12-07-2021 10:49
4
4
Open
19-07-2021 17:21
27-04-2016 09:07
3
3
Open
10-01-2017 08:40
10-01-2017 08:40
3
3
Closed
10-01-2017 08:40
3
3
Resolved
10-01-2017 08:40
I need to do the following things:
For Issue Id 4 find the total time in hours or minutes or seconds or days for a particular type of status. For e.g There are 2 In Review rows. So the total time between From Date to Till date will be 17:23 (19-07) till 8:47(20-07).
calculate total time a issue is in between closed and In Review (here Till date for closed issues is unfortunately null).
Basically I am trying to create a dashboard where for each issue i'd i would like to see for how long was a issue "In Review" or "In Progress" before it was closed. So the dashboard will have "Issue Id" in the X axis and "Total Time for Review" or "Total Time for Progress" in the Y axis. For e.g Issue 4 was in a total of 1:31:01 Hours in the "In Progress" state (17:23 to 17:23 on 19th July and 17:25 to 18:56 on 19th July).
I am trying this:
IF [STATUS] = 'In progress' and [STATUS] = 'Closed'
THEN
DATEDIFF('day',[Date Create],[Till Date])
END but it says tables can only be aggregated and using Count function only.
Can someone please help? How can we create a calculated field for the above scenarios.
Think of your IF statements being applied to each row, you cannot have a status that is both in progress and closed.
I would arrange the text table like this:
Columns: Status
Rows: Issue ID (group) | Issue ID
Text Mark: Calculated Field (Named something like Total Time).
That will group all of the statuses together. You can change the aliases of the status if you want to say "Total Time for ..."
Then your calculated field would be:
DATEDIFF("day", [From Date], [Till Date])
And make sure you drag the pill over it is summing it. That will collapse everything at the status level, and then total the days.

FIFO inventory aging report using a single query in T-SQL

I've got an inventory transactions table :
Product
Date
Direction
Quantity
A
Date 1
IN
3
B
Date 2
IN
55.7
A
Date 3
OUT
1
B
Date 3
OUT
8
B
Date 3
IN
2
I can easily get the stock for any date with the following query :
SELECT Product,
SUM(CASE Direction WHEN 'IN' THEN Quantity ELSE -1 * Quantity END)
FROM Transactions
WHERE Date <= '#DateValue#'
GROUP BY Product;
Now my purpose is to get stocks aged like this using the FIFO principle :
Product
Total stock
0-30 days
31-60 days
61-90 days
91+ days
A
3
3
0
0
0
B
34.2
10
14.2
7
3
C
25
20
3
1
1
D
10
2
8
0
0
E
1
0
0
1
0
I am using SQL Server 2016 & SSMS 18.
The solution should be fast as it will be working against a table with 3,000,000+ rows.
A single query is preferred since it will be integrated into an ERP system.
I have yet to find a solution based on a single query after weeks of research. Any help is appreciated. Thanks in advance.

MDX query count Login occurences over time interval

Im puzzle as to how to build my fact and dimensions to procude the following results:
I want to count the number of occurences of logged people for each time interval.
In this case every 30 mins. It would look like this
Example: Person1 login at 10:05:00 and logout at 12:10:00
Person2 login at 10:45:00 and logout at 11:25:00
Person3 login at 11:05:00 and logout at 14:01:00
TimeStart TimeEnd People logged
00:00:00 00:30:00 0
00:30:00 01:00:00 0
...
10:00:00 10:30:00 1
10:30:00 11:00:00 2
11:00:00 11:30:00 3
11:30:00 12:00:00 2
12:00:00 12:30:00 2
12:30:00 13:00:00 1
13:00:00 13:30:00 1
13:30:00 14:00:00 1
14:00:00 14:30:00 0
...
23:30:00 00:00:00 0
So i have a DimTime and DimDate table that contain hour, halfhour, quarterhour
and i have a FactTimestamp table that has the following:
DateLoginID that points to DimDate dateID
DateLogoutID that points to DimDate dateID
TimeLoginID that points to DimTime timeID
TimeLogoutID that points to DimTime timeID
I'd like to know what kind of cube design i would need to achieve that?
Ive done it in sql if that can help:
--Create tmp table for time interval
CREATE TABLE #tmp(
StartRange time(0),
EndRange time(0),
);
--Interval set to 30 minutes
DECLARE #Interval int = 30
-- Example with #Date = 2017-07-27: Set starttime at 2017-07-27 00:00:00
DECLARE #StartTime datetime = DATEADD(HOUR,0, #Date)
--Set endtime at 2017-07-27 23:59:59
DECLARE #EndTime datetime = DATEADD(SECOND,59,DATEADD(MINUTE,59,DATEADD(HOUR,23, #Date)))
--Populate tmp table with the time interval. from midnight to 23:59:59
;WITH cSequence AS
(
SELECT
#StartTime AS StartRange,
DATEADD(MINUTE, #Interval, #StartTime) AS EndRange
UNION ALL
SELECT
EndRange,
DATEADD(MINUTE, #Interval, EndRange)
FROM cSequence
WHERE DATEADD(MINUTE, #Interval, EndRange) <= #EndTime
)
INSERT INTO #tmp SELECT cast(StartRange as time(0)),cast(EndRange as time(0)) FROM cSequence OPTION (MAXRECURSION 0);
--Insert last record 23:30:00 to 23:59:59
INSERT INTO #tmp (StartRange, EndRange) values ('23:30:00','23:59:59');
SELECT tmp.StartRange as [Interval], COUNT(ts.TimeIn) as [Operators]
FROM #tmp tmp
JOIN Timestamp ts ON
--If timeIn is earlier than StartRange OR within the start/end range
(CAST(ts.TimeIn as time(0)) <= tmp.StartRange OR CAST(ts.TimeIn as time(0)) BETWEEN tmp.StartRange AND tmp.EndRange)
AND
--AND If timeOut is later than EndRange OR within the start/end range
CAST(ts.[TimeOut] as time(0)) >= tmp.EndRange OR CAST(ts.[TimeOut] as time(0)) BETWEEN tmp.StartRange AND tmp.EndRange
GROUP BY tmp.StartRange, tmp.EndRange
END
Really any kind of hint as to how to achieve it in mdx would be greatly appreciated.
Honestly, I wouldn't do it in MDX against that table structure. Even if you succeed in getting an MDX query that returns that value, and surely it can be done, it will most likely be tremendously complex and hard to maintain and debug, and will probably require multiple passes on the fact table to get the numbers, hurting performance.
I think this is a clear cut case for a periodic snapshot table. Pick your granularity, but even at 1 min snapshots you get 1440 points of data per day for each tuple of all other dimensions. If your login/logout table is large you may need to decrease this to keep its size manageable. In the end, you get a table with time_id, count_of_logins, and whatever other keys you need to other dimensions, and the query you need is just a filter on which time periods you want (give me all hours of the day, but filter on only minutes 00 and 30 of each hour) and the count of total number of logged in users is trivial.

Active IDs in date ranges

I have requirement to get number of customers active in a month based on their revenue contribution period.
Original Data:
ACCOUNT_ID REVENUE_START_DATE REVENUE_END_DATE
1234 1/14/2010 0:00 4/13/2010 23:59
4567 2/9/2010 0:00 3/8/2010 23:59
1234 5/9/2010 0:00 6/8/2010 23:59
Expected Result
Month Count
Dec-09 0
Jan-10 1
Feb-10 2
Mar-10 2
Apr-10 1
May-10 1
Jun-10 1
Jul-10 0
Aug-10 0
Sep-10
Oct-10
Below is the oracle code I worked on (with help of google) but I am not getting correct result due to overlapping dates. I request the experts to help me with this. (Thanks in Advance)
Current Result:
YEAR_ MONTH_ ACT
2010 January 2
2010 February 3
2010 March 3
2010 April 3
ORACLE CODE:
with tab as
(
select distinct ACCOUNT_ID, billing_start_date as revenue_start_date, billing_end_date as revenue_end_date
from accounts
),
year_tab as
(
select
add_months(min_date, level -1) m
from
(
select min(trunc(revenue_start_date,'YYYY')) min_date, add_months(max(trunc(revenue_end_date,'YYYY')), 12) max_date
from tab
)
connect by level <= months_between(max_date, min_date)
)
select to_char(m,'YYYY') year_,
to_char(m,'Month') month_,
nvl(act, 0) act
from year_tab,
(
select m date_,count(*) act
from tab, year_tab
where m between trunc(revenue_start_date,'MM') and trunc(revenue_end_date,'MM')
group by m
) month_tab
where m = date_(+)
order by m;
It's taken me a while to see why you think there is a problem. With the original three rows of data you supplied, running your query gives exactly your 'expected result'. With the 54 rows of data from your CSV file, the result is 48 rows (covering four years), with non-zero totals from January 2010 to January 2013. The first few rows returned are:
YEAR_ MONTH_ ACT
----- ------------------------------------ ----------
2010 January 2
2010 February 3
2010 March 3
2010 April 3
2010 May 2
But that looks correct:
select * from accounts
where not (billing_start_date > date '2010-02-01'
or billing_end_date < date '2010-01-01');
ACCOUNT_ID BILLING_START_DATE BILLING_END_DATE
---------- ------------------ ------------------
1234 09/01/2010 00:00 08/02/2010 23:59
4567 14/01/2010 00:00 13/04/2010 23:59
2 rows selected
select * from accounts
where not (billing_start_date > date '2010-03-01'
or billing_end_date < date '2010-02-01');
ACCOUNT_ID BILLING_START_DATE BILLING_END_DATE
---------- ------------------ ------------------
1234 09/01/2010 00:00 08/02/2010 23:59
4567 14/01/2010 00:00 13/04/2010 23:59
1234 09/02/2010 00:00 08/03/2010 23:59
3 rows selected
select * from accounts
where not (billing_start_date > date '2010-04-01'
or billing_end_date < date '2010-03-01');
ACCOUNT_ID BILLING_START_DATE BILLING_END_DATE
---------- ------------------ ------------------
4567 14/01/2010 00:00 13/04/2010 23:59
1234 09/02/2010 00:00 08/03/2010 23:59
1234 09/03/2010 00:00 08/04/2010 23:59
3 rows selected
But what I think you wanted wasn't really stressed in the question: 'to get number of customers active'. Assuming that by 'customer' you mean unique account IDs, you just need to modify the count:
select m date_,count(distinct account_id) act
from tab, year_tab
...
... which gives the first few rows as:
YEAR_ MONTH_ ACT
----- ------------------------------------ ----------
2010 January 2
2010 February 2
2010 March 2
2010 April 2
2010 May 1
What you were doing wrong was trying to apply the distinct in your tab subquery; but distinct returns distinct rows, and as the dates were different that wasn't actually reducing the number of rows returned.
Which doesn't quite match your expected result still, but does seem to match the data (if my assumption about what you want is right), and still does give your expected result for you three-row sample.
Another way to write the query, which I find a bit easier to follow, and using ANSI join syntax:
with t as (
select add_months(min_date, level - 1) month_start,
add_months(min_date, level) next_month_start
from (
select trunc(min(billing_start_date),'YYYY') min_date,
add_months(trunc(max(billing_start_date),'YYYY'), 12) max_date
from accounts
)
connect by level <= months_between(max_date, min_date)
)
select to_char(t.month_start,'YYYY') year_,
to_char(t.month_start,'Month') month_,
count(distinct a.account_id) act
from t
left join accounts a on not (billing_start_date > t.next_month_start
or billing_end_date < t.month_start)
group by t.month_start
order by t.month_start;

Resources