MDX running total with cross join - mdx

MDX newbie question
I am using the FoodMart example database to express my problem.
I need to show running total of the "Unit Sales" measure on the column
with "Month", "Year", and "Product Name" dimensions on the rows:
Year Month Product | Unit Sales UnitSoldIncludngThisProdTillDate
|
2014 Jan P1 | 4 4
P2 | 2 6
P3 | 0 6
Feb P1 | 1 7
P2 | 0 7
P3 | 3 10
2015 Jan P1 | 7 17
...... and so on
Without the cross join with Product, the query runs fine.
However, joining with Product does not give what I want.
How to solve this problem?
The MDX query that I am running is
AGGREGATE(YTD(), [Measures].[Unit Sales])
SELECT {[Measures].[Unit Sales], MEASURES.YTDDEMO} ON 0,
NON EMPTY {[Time].[Month].Members * [Product].[ProductLevel].Members} ON 1
FROM [TestFoodMart]
Added on 10th June 2015
I have been using the correct syntax etc.
Without the Join I get the following:
| | Unit Sales | Sales To Date |
+------+-----------+------------+---------------+
| 2013 | April | 45,049 | 45,049 |
| | August | 44,777 | 89,826 |
| | December | | 89,826 |
| | February | 44,431 | 134,257 |
| | January | 46,313 | 180,570 |
| | July | 46,671 | 227,241 |
| | June | 45,611 | 272,852 |
| | March | 46,334 | 319,186 |
| | May | 45,085 | 364,271 |
| | November | 53,807 | 418,078 |
| | October | 43,945 | 462,023 |
| | September | 47,964 | 509,987 |
With the Join I get the following:
| Unit Sales | YTDDEMO |
+------+-----------+---------------------------------------------+------------+---------+
| 2013 | April | ADJ Rosy Sunglasses | 38 | 38 |
| | | Akron City Map | 29 | 29 |
| | | Akron Eyeglass Screwdriver | 34 | 34 |
| | | American Beef Bologna | 28 | 28 |
| | | American Chicken Hot Dogs | 25 | 25 |
|
As you can see, the aggregation is not working

You're missing the WITH line of code:
WITH MEMBER MEASURES.YTDDEMO AS
AGGREGATE(YTD(), [Measures].[Unit Sales])
SELECT
{[Measures].[Unit Sales], MEASURES.YTDDEMO} ON 0,
NON EMPTY
{[Time].[Month].Members * [Product].[ProductLevel].Members} ON 1
FROM [TestFoodMart]
Please be aware of the Remarks section in the MSDN definition of the YTD function: https://msdn.microsoft.com/en-us/library/ms146039.aspx
If a member expression is not specified, the default is the current
member of the first hierarchy with a level of type Years in the first
dimension of type Time in the measure group. The Ytd function is a
shortcut function for the PeriodsToDate function where the Type
property of the attribute hierarchy on which the level is based is set
to Years. That is, Ytd(Member_Expression) is equivalent to
PeriodsToDate(Year_Level_Expression,Member_Expression). Note that this
function will not work when the Type property is set to FiscalYears.
If the type property of your year attribute hierarchy is not set to time, then the YTD function will not work.
Does this equivalent version work?
WITH
MEMBER MEASURES.YTDDEMO AS
Aggregate
(
PeriodsToDate([Time].[Year]) <<//change to what your year level is
,[Measures].[Unit Sales]
)
SELECT
NON EMPTY
{
[Measures].[Unit Sales]
,MEASURES.YTDDEMO
} ON 0
,NON EMPTY
{[Time].[Month].Members * [Product].[ProductLevel].Members} ON 1
FROM [Adventure Works];

Could you try this, I think it's only a syntax problem.
AGGREGATE(YTD(), [Measures].[Unit Sales])
SELECT {[Measures].[Unit Sales], MEASURES.YTDDEMO} ON 0,
NON EMPTY ([Time].[Month].Members * [Product].[ProductLevel].Members) ON 1
FROM [TestFoodMart]

Related

How do I use a historic value as at a particular month when there are no values for the given month?

I have 2 SQL Server tables.
PurchaseOrderReceivingLine (PORL) is a table that contains every receipt from a purchase order. This has hundreds of entries per month.
PartyRelationshipScore (PRS) is a table with a party (supplier) reference number (that is used to join to the PORL table) and a score out of 10 for relationship and price. It also has a date field for when the score is updated so we have a history of the updates.
What I want to achieve is a supplier summary for each month. So I would have Supplier #, TotalValue, LateParts etc. I'm fine with creating the code for that. What I'm struggling with is getting the score for the given month if there are no values for that month.
So, for example I might have a value of 5 on the 1st August. Then it doesn't change until the 1st October when it is increased to 6.
On the grouping, September will have a TotalValue & a LateParts value but because there are no records in September in the PRS table, it will return a NULL value. I need it to get the last value recorded and return that (in this case August's 5). So it will return;
Aug 2019 - 5
Sep 2019 - 5
Oct 2019 - 6
Thanks in advance.
PORL Table
+-------+----------------+-------+-------+
| PORL# | Date (UK) | Value | Party |
+-------+----------------+-------+-------+
| 1 | 1/8/2019 | 100 | 6 |
| 2 | 1/8/2019 | 250 | 6 |
| 3 | 1/9/2019 | 1000 | 6 |
| 4 | 1/10/2019 | 2000 | 6 |
+-------+----------------+-------+-------+
PRS Table
+-------------+------------+-------------------+------------+
| DateChanged (UK) | Party | RelationShipScore | PriceScore |
+-------------+------------+-------------------+------------+
| 1/8/2019 | 6 | 5 | 5 |
| 1/10/2019 | 6 | 6 | 7 |
+------------------+-------+-------------------+------------+
Preferred outcome
+----------+-------+------+------------+-------------------+------------+
| Supplier | Month | Year | TotalValue | RelationshipScore | PriceScore |
+----------+-------+------+------------+-------------------+------------+
| 6 | 8 | 2019 | 350 | 5 | 5 |
| 6 | 9 | 2019 | 1000 | 5 | 5 |
| 6 | 10 | 2019 | 2000 | 6 | 7 |
+----------+-------+------+------------+-------------------+------------+
The relationshipscore & pricescore for month 9 are based on it not changing from month 8.
I think this helps
select Supplier = T.Party
, Month = DATEPART(MONTH,T.[Date])
, Year = DATEPART(YEAR,T.[Date])
, T.TotalValue
, R.RelationShipScore
, R.PriceScore
from ( Select P.[Party],P.[Date],[TotalValue] = sum(P.[Value])
from PurchaseOrderReceivingLine P
group by P.[Party],P.[Date] ) T
outer apply ( select top 1 RelationShipScore , PriceScore
from PartyRelationshipScore
where Party = T.Party
and DateChanged <= T.[Date]
Order by DateChanged desc ) R

Slicing account balance data in BigQuery to generate a debit report

I have a collection of account balances over time:
+-----------------+------------+-------------+-----------------------+
| account_balance | department | customer_id | timestamp |
+-----------------+------------+-------------+-----------------------+
| 5 | A | 1 | 2019-02-12T00:00:00 |
| -10 | A | 1 | 2019-02-13T00:00:00 |
| -35 | A | 1 | 2019-02-14T00:00:00 |
| 20 | A | 1 | 2019-02-15T00:00:00 |
+-----------------+------------+-------------+-----------------------+
Each record shows the total account balance of a customer at a specified timestamp. The account balance increases e.g. to 20 from -35, when a customer tops-up his account with 55. As a customer uses a services, his account balances decreases e.g. from 5 to -10.
I want to aggregate this data in two ways:
1) Get the debit, credit and balance (credit-debit) of a department per month and year. The results from April should be a summary of all previous months:
+---------+--------+-------+------------+-------+--------+
| balance | credit | debit | department | month | year |
+---------+--------+-------+------------+-------+--------+
| 5 | 10 | -5 | A | 1 | 2019 |
| 20 | 32 | -12 | A | 2 | 2019 |
| 35 | 52 | -17 | A | 3 | 2019 |
| 51 | 70 | -19 | A | 4 | 2019 |
+---------+--------+-------+------------+-------+--------+
A customer's account balance might not change every month. There might be account balance records of customer 1 in February, but not March.
Notes towards the solution:
use EXTRACT(MONTH from timestamp) month
use EXTRACT(YEAR from timestamp) year
GROUP BY month, year, department
2) Get the change of debit, credit and balance of a department by date.
+---------+--------+-------+------------+-------------+
| balance | credit | debit | department | date |
+---------+--------+-------+------------+-------------+
| 5 | 10 | -5 | A | 2019-01-15 |
| 15 | 22 | -7 | A | 2019-02-15 |
| 15 | 20 | -5 | A | 2019-03-15 |
| 16 | 18 | -2 | A | 2019-04-15 |
+---------+--------+-------+------------+-------------+
51 70 -19
When I create a SUM of the deltas, I should get the same values as the last row from results in 1).
Notes towards the solution:
use account_balance - LAG(account_balance) OVER(PARTITION BY department ORDER BY timestamp ASC) delta to compute deltas
Your question is unclear, but it sounds like you want to get the outstanding balance at any given point in time.
The following query does this for 1 point in time.
with calendar as (
select cast('2019-06-01' as timestamp) as balance_calc_ts
),
most_recent_balance as (
select customer_id, balance_calc_ts,max(timestamp) as most_recent_balance_ts
from <table>
cross join calendar
where timestamp < balance_calc_ts -- or <=
group by 1,2
)
select t.customer_id, t.account_balance, mrb.balance_calc_ts
from <table> t
inner join most_recent_balance mrb on t.customer_id = mrb.customer_id and t.timestamp = mrb.balance_calc_ts
If you need to calculate it at a series of points in time, you will need to modify the calendar CTE to return more dates. This is the beauty of CROSS JOINS in BQ!

SQL - divide one column by another

I have the following code:
select c.category
,sum(b.is_open) as open
,count(b.name) as total
from business b inner join category c on b.id=c.business_id
group by c.category
order by sum(b.is_open) desc
limit 10
which gives me following dataset:
+------------------+------+-------+
| category | open | total |
+------------------+------+-------+
| Restaurants | 53 | 71 |
| Shopping | 25 | 30 |
| Food | 20 | 23 |
| Health & Medical | 16 | 17 |
| Home Services | 15 | 16 |
| Beauty & Spas | 12 | 13 |
| Nightlife | 12 | 20 |
| Bars | 11 | 17 |
| Active Life | 10 | 10 |
| Local Services | 10 | 12 |
+------------------+------+-------+
However, if I change line 2 and 3 to:
sum(b.is_open) / count(b.name) as '%'
instead of a specific value, I get zeroes all along. I tried to cast both columns to decimal type (although looks like they have been such in the beginning), did not work. Why can't I get the right results? I am writing my queries in SQLite.
Try using floating point arithmetic instead of integer arithmetic:
1.0 * sum(b.is_open) / count(b.name) as '%'

Comparing SUM of values with different tables in SQL Server

I have two tables holding similar values, and I need to compare the two and find the differences between them:
SQL FIDDLE - http://sqlfiddle.com/#!6/7412e/9
Now you can see there is a difference between between the 2 tables for the figures in Jun-17.
AS you can see (as a total for everyone) table 1 has £75 for June but table 2 has £125 for june.
The result I'm looking for is when amounts are summed together and compared between tables on a monthly basis, if there is a difference in amount between the two tables I want it listed under 'Unknown'.
| MonthYear | Person | Amount | Month total
+-----------+--------+--------+--------------
| Jun-17 | Sam | 25 | 75(Table1)
| Sep-17 | Ben | 50 | 50(Table2)
| Jun-17 | Tom | 50 | 75(Table1)
| Jun-17 | Sam | 25 | 125(Table2)
| Sep-17 | Ben | 50 | 50(Table2)
| Jun-17 | Tom | 50 | 125(Table2)
| Jun-17 | | 50 | 125(Table2)
Now when there is a difference between the amount total over a month I want the difference to be classed as unknown
e.g
| MonthYear | Person | Amount | Month total
+-----------+--------+--------+--------------
| Jun-17 | Sam | 25 | 75(Table1)
| Sep-17 | Ben | 50 | 50(Table2)
| Jun-17 | Tom | 50 | 75(Table1)
| Jun-17 | Sam | 25 | 125(Table2)
| Sep-17 | Ben | 50 | 50(Table2)
| Jun-17 | Tom | 50 | 125(Table2)
| Jun-17 | Unknown| 50 | 125(Table2)
I understand that you could create a case when the person is null to display unknown but i need it to be specifically calculated on the difference between the 2 tables on a monthly calculation.
Does this make sense to anyone, its really hard to explain.
Generally, in any FROM clause a table name can be replaced with another SELECT as long as you give it a corelation name (t1 and t2 in this one):
SELECT t1.MonthYear, t1.AmountT1, t2.AmountT2, t1.amountT1 - isnull(t2.amountT2, 0) as Unknown'
from
( SELECT
MonthYear,
SUM(Amount) AS [AmountT1]
FROM
Invoice
GROUP BY MonthYear) t1
left outer join
( SELECT
MonthYear,
SUM(Amount) AS [AmountT2]
FROM
Invoice2
GROUP BY MonthYear) t2 on t2.MonthYear = t1.MonthYear

SQL - How do I query for re-admissions in TSQL?

I'm trying to figure out how to query for readmissions on Server 2008r2. Here is the basic structure of the visit table. There are other fields but none that I thought would be helpful. One issue is that some of these may be transfers instead of discharges which I have no easy way to deduce but that issue can be ignored for now. I tried my hand at this but I guess my understanding of SQL needs more work. I tried to find any info I could online but none of the queries lead me to a useful conclusion or I just didn't understand. Any suggestions would be appreciated.
EDIT: Readmission is if a patient returns within 30 days of previous discharge.
+---------+--------+-----------------+-----------------+
| VisitID | UID | AdmitDT | DischargeDT |
+---------+--------+-----------------+-----------------+
| 12 | 2 | 6/17/2013 6:51 | 6/17/2013 6:51 |
| 16 | 3 | 6/19/2013 4:48 | 6/21/2013 13:35 |
| 18 | 3 | 6/11/2013 12:08 | 6/11/2013 12:08 |
| 21 | 3 | 6/12/2013 14:40 | 6/12/2013 14:40 |
| 22 | 3 | 6/13/2013 10:00 | 6/14/2013 12:00 |
| 25 | 2 | 6/11/2013 16:13 | 6/11/2013 16:13 |
| 30 | 1 | 6/20/2013 8:35 | 6/20/2013 8:35 |
| 31 | 7 | 6/13/2013 6:12 | 6/13/2013 6:12 |
| 34 | 3 | 6/12/2013 8:40 | NULL |
| 35 | 1 | 6/12/2013 8:52 | NULL |
| 38 | 2 | 6/12/2013 10:10 | 6/12/2013 10:10 |
+---------+--------+-----------------+-----------------+
Attempt at Code:
SELECT N2.*
FROM visitTable AS N1
INNER JOIN
visitTable AS N2 ON N1.UID = N2.UID
WHERE N1.EncounterID <> N2.EncounterID AND ( N2.AdmitDT BETWEEN N1.DischargeDT and DATEADD(DD,30, N1.DischargeDT))
Here's a start:
sqlfiddle
new fiddle
It gets each visit for each UID in order of admitDT, then pairs each visit with the next visit in that result. If the current admit date is between the last discharge date and 30 days from then, select it. There are some weird points though - UID 1 is shown to have been admitted on 6/12/2012 and never discharged, but then admitted again on 6/20/2013 and discharged the same day.
edit: restructured a bit to reduce the number of joins
WITH cte AS (
SELECT visitid,uid,dischargedt,admitdt,
row_number()over(partition BY uid ORDER BY admitdt) AS r
FROM t
)
SELECT
c1.visitid AS v1, c2.visitid AS v2,
c1.uid,
c1.dischargedt as [Discharged from first visit],
c2.admitdt as [Admitted to next visit]
FROM cte c1
INNER JOIN cte c2 ON c1.uid=c2.uid
WHERE c1.visitid<>c2.visitid
AND c1.r+1=c2.r
AND c2.admitdt BETWEEN c1.dischargedt AND dateadd(d,30,c1.dischargedt )
ORDER BY c1.uid
Results:
| V1 | V2 | UID | DISCHARGED FROM FIRST VISIT | ADMITTED TO NEXT VISIT |
|----|----|-----|-----------------------------|-----------------------------|
| 25 | 38 | 2 | June, 11 2013 16:13:00+0000 | June, 12 2013 10:10:00+0000 |
| 38 | 12 | 2 | June, 12 2013 10:10:00+0000 | June, 17 2013 06:51:00+0000 |
| 18 | 34 | 3 | June, 11 2013 12:08:00+0000 | June, 12 2013 08:40:00+0000 |
| 21 | 22 | 3 | June, 12 2013 14:40:00+0000 | June, 13 2013 10:00:00+0000 |
| 22 | 16 | 3 | June, 14 2013 12:00:00+0000 | June, 19 2013 04:48:00+0000 |
try this: (Show me the visits where the admission date is after discharge for another earlier visit by the same patient)
Select * From visits v
Where Exists (Select * From Visits
Where uid = v.uid
and v.AdmitDT > DischargeDT)
You have not explained any business rules so I'll take a guess. A readmission is when multiple UID appear, and it is every record except the first one
Here is another method using windowing functions.
SELECT VT.*
FROM visitTable VT
INNER JOIN
(
SELECT VisitID, ROW_NUMBER() OVER (PARTITION BY UID ORDER BY AdmitDT) VisitCount
FROM visitTable
) RA
ON RA.VisitCount > 1 AND RA.VisitID = VT.VisitID