MDX : Combine two role playing dimension, with multi values - mdx

I'm working since few days on a MDX question and I dont see any issues...
Here is the context :
I have a fact table :
+----------+--------+-------------+------------+------------------+
| Line num | Amount | Line Type | Date | DateConfirmation |
+----------+--------+-------------+------------+------------------+
| 1 | 100 | Reservation | 01/01/2016 | 12/01/2016 |
| 2 | 50 | Reservation | 01/01/2016 | Empty |
| 3 | 80 | Reservation | 20/12/2015 | 01/01/2016 |
| 4 | 30 | DirectSales | 01/01/2016 | 01/01/2016 |
+----------+--------+-------------+------------+------------------+
So in SSAS i have designed a cube with
Amount measure
Date dimension
Date Confirmation dimension
Then 2 date dimensions are role playing dimensions
What I need is to combine, when analysis by Date, all the reservations plus the reservations that have been confirmed at the same date of currentMember.
So i've writted this MDX :
CREATE MEMBER CURRENTCUBE.[Sales].[Type].[All].[Confirmed Reservations]
AS NULL ,
VISIBLE = 1;
Scope ( [Sales].[Type].[All].[Confirmed Reservations] );
Scope( MeasureGroupMeasures("Sales") , [Date].[Hierarchy].Members , [Date].[Date].Members
, [Date Confirmation].[Hierarchy].[All] , [Date Confirmation].[Date].[All] );
This = ([Sales].[Type].&[Reservation], StrToMember("[Date Confirmation].[Hierarchy]." + Right(MemberToStr([Date].[Hierarchy].CurrentMember), Len(MemberToStr([Date].[Hierarchy].CurrentMember)) - Len("[Date].[Hierarchy].") ) ), [Date].[Hierarchy].[All] );
End Scope;
End Scope;
The expected result, if I analyse the sales & reservations with the Date dimension at 01/01/2016 is
+------------------------+-----------+
| Reservation | 150 (1+2) |
| DirectSales | 30 (4) |
| Confirmed Reservations | 80 (3) |
+------------------------+-----------+
This works perfectly if I select in Excel only one date. But It produce very bad result when more than one date is select.
All your suggestions will be very helpfull for me !
Many thanks at all :)

Instead of trying to tackle this in MDX I would suggest a simpler approach. If your current fact table query in the DSV is:
Select LineNum, Amount, LineType, Date, DateConfirmation
From YourFact
I would change it to:
Select LineNum, Amount, LineType, Date, DateConfirmation
From YourFact
UNION ALL
Select LineNum, Amount, 'Confirmed Reservations' as LineType, DateConfirmation as Date, DateConfirmation
From YourFact
WHERE DateConfirmation is not null
Then you shouldn't need any MDX.

Related

SQL/Power BI Joins without common column

So I have the following problem:
I have 2 tables, one containing different bids for a product_type, and one containing the price, date etc. to which the product was sold.
The tables look like this:
Table bids:
+----------+---------------------+---------------------+--------------+-------+
| Bid_id | Start_time | End_time | Product_type | price |
+----------+---------------------+---------------------+--------------+-------+
| 1 | 18.01.2020 06:00:00 | 18.01.2020 06:02:33 | blue | 5 € |
| 2 | 18.01.2020 06:00:07 | 18.01.2020 06:00:43 | blue | 7 € |
| 3 | 18.01.2020 06:01:10 | 19.01.2020 15:03:15 | red | 3 € |
| 4 | 18.01.2020 06:02:20 | 18.01.2020 06:05:44 | blue | 6 € |
| | | | | |
+----------+---------------------+---------------------+--------------+-------+
Table sells:
+---------+---------------------+--------------+--------+
| Sell_id | Sell_time | Product_type | Price |
+---------+---------------------+--------------+--------+
| 1 | 18.01.2020 06:00:31 | Blue | 6,50 € |
| 2 | 18:01.2020 06:51:03 | Red | 2,50 € |
| | | | |
+---------+---------------------+--------------+--------+
The sell_id and the bid_id have no relation with each other.
What I want to find out is, what is the maximum bid to the time we sold the product_type. So if we take sell_id 1, it should check, which bids for this specific product_type were active during the sell_time (in this case bid_id 1 and 2) and give back the higher price (in this case bid_id 2).
I tried to solve this problem in Power Bi, however, I was not able to get a solution. I assume, that I have to work with SQL-Joins to solve it.
Is it possible, to join based on criteria instead of matching columns? Something like:
SELECT bids.start_time, bids.end_time, bids.product_type, MAX(bids.price), sells.sell_time, sells.product_type, sells.price
FROM sells
INNER JOIN bids ON bids.start_time<sells.sell_time AND bids.end_time > sells.sell_time;
I am sorry if this question is confusing, I am still new to this sorry. Thanks in advance for ANY help!
Your sample data Sell_time should be 18.01.2020, right? You Can try this code (can be resource-intensive in relation to the amount of data due to Cartesian joins). If you are sure that Sell day is always in Bid Start day, then you can add date column to yours tables and use additional TREATAS(VALUE(bids[day], sells[day])
Test =
VAR __tretasfilter =
TREATAS ( VALUES ( bids[Product_type] ), sells[Product_type] )
RETURN
SUMMARIZE (
FILTER (
SUMMARIZECOLUMNS (
sells[Sell_id],
bids[Price],
bids[Start_time],
sells[Sell_time],
bids[End_time],
sells[Product_type],
__tretasfilter
),
[Start_time] <= [Sell_time]
&& [End_time] >= [Sell_time]
),
sells[Sell_id],
"MaxPrice", MAX ( bids[Price] )
)

Query columns based on values of table

I'm new to DB and SQL so I don't know if there is anything new I need to try for this. I want to solve this table my senior has given to me:
Passbook(Table Name)
Date | Amount | Type
-----------------------------
14/3/19 | 48000 | Debit |
13/2/19 | 75000 | Credit|
9/7/19 | 65000 | Credit|
12/6/19 | 15000 | Debit |
Now I have to generate a query in this manner:
Month | Debit | Credit
------------------------------
13/2/19 | 0 | 75000
14/3/19 | 48000 | 0
12/6/19 | 15000 | 0
9/7/19 | 0 | 65000
Here my Passbook table value has become the columns for query and IDK how to generate it in this manner
Anyone help me do this please
for monthly sorting, I'm supposed to use ORDER BY clause, I suppose
Now I have to generate a query in that manner.
A basic pivot query should work here:
SELECT
Format(Month([Date])) AS Month,
SUM(IIF(Type = 'Debit', Amount, 0)) AS Debit,
SUM(IIF(Type = 'Credit', Amount, 0)) AS Credit
FROM yourTable
GROUP BY
Format(Month([Date]));
If you instead want date level output, then aggregate the by the Date column directly.

Impala SQL Stockpiling Algorithm

I have prescription drug data that has a prescription date and the number of days supplied for that prescription. I am trying estimate actually drug intake dates which can be different then prescription date if people (1) refill their prescription before their current prescription is done or (2) they lost their current prescription and so need a refill.
Below is sample data for 1 patient:
| patient_id | rx_start_date | days_supply |
|------------|---------------|-------------|
| 1 | 1/10/2013 | 3 |
| 1 | 1/11/2013 | 3 |
| 1 | 1/14/2013 | 3 |
Without adjusting for stockpiling the end dates are calculated as rx_start_date + days_supply - 1 see:
| patient_id | rx_start_date | days_supply | rx_end_date |
|------------|---------------|-------------|-------------|
| 1 | 1/10/2013 | 3 | 1/12/2013 |
| 1 | 1/11/2013 | 3 | 1/13/2013 |
| 1 | 1/14/2013 | 3 | 1/16/2013 |
As you can see the start date for the 2nd prescription is overlapped by the first prescription. If we assume that they filled their prescription early then the actual intake date for the 2nd prescription should start on 1/13/2013. But moving the end date of the 2nd prescription causes an overlap over the 3rd prescription and so that must be moved as well. See the expected resulting table below:
| patient_id | rx_start_date | days_supply | rx_end_date |
|------------|---------------|-------------|-------------|
| 1 | 1/10/2013 | 3 | 1/12/2013 |
| 1 | 1/13/2013 | 3 | 1/15/2013 |
| 1 | 1/16/2013 | 3 | 1/18/2013 |
The other case is we might say if the current prescription overlaps the next one by more than 50% than we assume they lost their prescription and the 2nd prescription start date is the actual intake date. This means though that we need to truncate the current prescription to end when the 2nd one starts.
The algorithm is relatively simple using a non-sql iterative solution but I'm having trouble with a generic sql solution since adjusting dates at time X could potentially cause a cascading effect that adjust many other dates. I'm using Impala SQL so recursive CTE's are not an option and I'd like this to work on other databases so database specific functions are not ideal either.
The following should give you what you are looking for, so long as there are no gaps in the treatment regime:
with aggs as (select d1.patient_id, d1.rx_start_dt, sum(ds.days_supply) days_supply, min(ds.rx_start_dt) + sum(ds.days_supply) - 1 end_dt
from drugs d1
inner join drugs ds
on ds.patient_id = d1.patient_id and ds.rx_start_dt <= d1.rx_start_dt
group by d1.patient_id, d1.rx_start_dt)
select patient_id, coalesce(lag(end_dt+1) over (partition by patient_id order by rx_start_dt),rx_start_dt) start_dt, end_dt
from aggs;
Using the given sample data, this gives as output:
ID Start End
1 2013-01-10 2013-01-12
1 2013-01-13 2013-01-15
1 2013-01-16 2013-01-18
This was tested on Oracle, but all functions used appear to also be available in impala so should work there too.

Access 2016 & SQL: Totaling two columns, then subtracting them

Say I have a MoneyIN and a MoneyOUT column. I wish to total these entire columns up so I have a sum of each, then I wish to subtract the total of the MoneyOUT column from the total of the MoneyIN column. I also want to display a DateOF column and possibly a description (I think I can do that by myself).
This would be the original database where I get my information from:
+-------------+------------------+---------+----------+-----------+
| Location ID | Location Address | Date Of | Money In | Money Out |
+-------------+------------------+---------+----------+-----------+
| 1 | blah | date | 10.00 | 0.00 |
| 2 | blah | date | 2,027.10 | 27.10 |
| 2 | blah | date | 0.00 | 2000.00 |
| 1 | blah | date | 0.00 | 10.00 |
| 3 | blah | date | 5000.00 | 0.00 |
+-------------+------------------+---------+----------+-----------+
I would like to be able to type in a location ID and then have results show up (in this example I type 2 for the location)
+---------+----------+-----------+------+
| Date Of | Money In | Money Out | |
+---------+----------+-----------+------+
| date | 2027.10 | 27.10 | |
| date | 0 | 2000 | |
| Total: | 2027.10 | 2027.10 | 0 |
+---------+----------+-----------+------+
I have tried other solutions (One of which was pointed out below), however, they don't show the sum of each entire column, they simply subtract MoneyOUT from MoneyIN for each row. As of now, I am trying to do this in a query, but if there is a better way, please elaborate.
I am extremely new to SQL and Access, so please make the explanation understandable for a beginner like me. Thanks so much!
This is a table referred to below.
+-------------+-------+----------+-----------+-----------+
| Location ID | Date | Money IN | Money Out | Total Sum |
+-------------+-------+----------+-----------+-----------+
| 1 | date | 300 | 200 | |
| 1 | date | 300 | 200 | |
| 1 | date | 300 | 200 | |
| 1 | total | 900 | 600 | 300 |
+-------------+-------+----------+-----------+-----------+
The following should give you what you want:
SELECT DateOf, MoneyIn, MoneyOut, '' AS TotalSum FROM YourTable
UNION
SELECT 'Total', SUM(MoneyIn) AS SumIn, SUM(MoneyOut) AS SumOut,
SUM(MoneyIn - MoneyOut) AS TotalSum FROM YourTable
Edit:
You do not need to alter very much to achieve what you want. In order to get Access to prompt for a parameter when running a query, you give a name for the parameter in square brackets; Access will then pop-up a window prompting the user for this value. Also this parameter can be used more than once in the query, without Access prompting for it multiple times. So the following should work for you:
SELECT DateOf, MoneyIn, MoneyOut, '' AS TotalSum
FROM YourTable
WHERE LocationID=[Location ID]
UNION
SELECT 'Total', SUM(MoneyIn) AS SumIn, SUM(MoneyOut) AS SumOut,
SUM(MoneyIn - MoneyOut) AS TotalSum FROM YourTable
WHERE LocationID=[Location ID];
However, looking at your table design, I strongly encourage you to change it. You are including the address on every record. If you have three locations, but 100 records, then on average you are unnecessarily repeating each address more than 30 times. The "normal" way to avoid this would be to have a second table, Locations, which would have an ID and an Address field. You then remove address from YourTable, and in its place create a one-to-many relationship between the ID in Locations and the LocationID in YourTable.
It's a little unclear exactly what you expect without sample data, but I think this is what you want:
SELECT DateOf, SUM(MoneyIN) - SUM(MoneyOut)
FROM YourTable
GROUP BY DateOf
This will subtract the summed total of MoneyOut from MoneyIn at each distinct DateOf
Updated Answer
A UNION will let you append a 'Totals' record to the bottom of your result set:
SELECT *
FROM (
SELECT CAST(DateOf as varchar(20)) as DateOf, MoneyIn, MoneyOut, '' as NetMoneyIn
FROM YourTable
UNION
SELECT 'Total:', SUM(MoneyIn), SUM(MoneyOut), SUM(MoneyIN) - SUM(MoneyOut)
FROM YourTable
) A
ORDER BY CASE WHEN DateOf <> 'Total:' THEN 0 ELSE 1 END, DateOf
Some notes.. I used a derived table to ensure that the 'Total' record is last. Also casted DateOf to a string (assuming it is a date), otherwise you will have issues writing the string 'Total:' to that column.

SQL Conditional sum and grouping

I have a query that's trying to sum up a patient's length of stay at a hospital. Here is an example of the data
| Patient | Admission_ID | Admission_Event_ID | Admission_Event_Type | Start Date | End Date | Duration | Linked_Admission |
| P0001 | ADM0001 | AE1 | (formal) Separation | 2012-12-18 | 2012-12-18 | 0 | ADM0002 |
| P0001 | ADM0001 | AE2 | Statistical Admission | 2012-12-17 | 2012-12-18 | 1 | ADM0002 |
| P0001 | ADM0002 | AE3 | Statistical Separation| 2012-12-17 | 2012-12-17 | 0 | NULL |
| P0001 | ADM0002 | AE4 | (formal) Admission | 2012-11-30 | 2012-12-17 | 17 | NULL |
| P0002 | ADM0003 | AE5 | (formal) Admission | 2012-11-30 | 2012-12-25 | 25 | NULL |
. . .
EDIT: Forgot to mention, there is a column that links the admission ID (only used when the patient is statistically separated and admitted)
By definition, the length of stay is calculated for each patient from the start of their admission until they are separation (statistical separations and admission carry on with the admission, but they're given a new Admission ID
A report is run to find out the average length of stay (ALOS) for the hospital and it's unit, the user selects two dates to report between. I've used a CTE (lets call it CTESep) to get all the patient's that have been formally separated between the reporting period. I then use another CTE (called CTEAdmissions) to get all the admissions of the patients within CTESep. This is where I get stuck.
I need to sum up the Durations of the patient to get their total length of stay for that admission (which is a combination of ADM0001 and ADM0002) so the total LOS will be 18, rather than 17 and 1.
My idea was to
ORDER BY Patient
, End_Date DESC
, adm_id
, CASE WHEN
Admission_Event_Type = '(formal) Separation ' THEN 1
WHEN Admission_Event_Type = 'Statistical Admission ' THEN 2
WHEN Admission_Event_Type = 'Statistical Separation' THEN 3
WHEN Admission_Event_Type = '(formal) Admission ' THEN 4
END ASC
Then sum up the duration on based on a condition. The condition rule is 'Start summing up the duration of each patient's admission from a formal separation to a formal admission'. Which I'm not sure how to do.
I've tried:
SELECT SUM(Duration) OVER(PARTITION BY Patient) AS 'Sum'
But that will give me the total LOS for the patient across ALL their admissions (if they have more than one separation within that reporting period)
I've also tried
SELECT SUM(Duration) OVER(PARTITION BY Patient, Admission_ID) AS 'Sum'
But of course that gives me the LOS of a patient between a formal admission and a statistical separation (and not the LOS by its actual definition).
Anyone got a different way of tackling this problem? By the way, using Sybase
How about this:
select patientid,
admissionid,
datediff(day,
max(case when Admission_Event_Type = '(formal) Separation ' then startdate end),
max(case when Admission_Event_Type = '(formal) Admission ' then enddate end)
) as total_length
from data
group by patientid, admissionid