MDX script to ignore zero's when calculating average - ssas

I am trying to use MDX to create a calculated member for computing averages. I have two measures, [High School GPA] and [Has High School GPA]. [Has High School GPA] is set to 1 if [High School GPA] is numeric, else it's 0. So, in the past, the following calculated member worked just fine.
case
when [MEASURES].[Has High School GPA] is null
or [MEASURES].[Has High School GPA] = 0
then null
else
[MEASURES].[High School GPA] / [MEASURES].[Has High School GPA]
end
Now, I need to exclude [High School GPA] = 0.00 from my average calculation even though [Has High School GPA] will be equal to 1. So, I modified my script as follows.
case
when [MEASURES].[Has High School GPA] IS null
or [MEASURES].[Has High School GPA] = 0
or [MEASURES].[High School GPA] = 0
then null
else
[MEASURES].[High School GPA] / [MEASURES].[Has High School GPA]
end
However, my average is still including the records with [High School GPA] = 0.00. Any suggestion on how I can fix this?

The easiest way to solve this is to create a computed column on that table in the DSV. The calc should be:
NULLIF(GPA,0)
Then on the measures that use that column make sure the NullHandling property is set to Preserve. That should ensure both 0 and Null end up Null.
By the way, in MDX the IS NULL syntax isn't what you want. Use IsEmpty() instead. But in this case it's redundant because Null = 0 in MDX. So the proper MDX should be:
case
when [MEASURES].[Has High School GPA] = 0
or [MEASURES].[High School GPA] = 0
then null
else
[MEASURES].[High School GPA] / [MEASURES].[Has High School GPA]
end

Related

Unable to display a zero for capacity of a region

I am currently trying to solve this question:
Determine the total capacity of the warehouses belonging to suppliers from a given nation in every region. The suppliers’ nation is the United States. If there are no warehouses in a region, then value 0 is printed for that region. Print the region and the capacity sorted alphabetically by region.
This is the expected result:
region capacity
AFRICA 4314
AMERICA 10446
ASIA 0
EUROPE 8402
MIDDLE EAST 10866
So far I have done this for my SQL statement and I have most of it correct however I am not able to get ASIA to print 0 no matter what I tried.
select r_name, sum(w_capacity)
from warehouse,region,nation n1,nation n2,supplier
where w_suppkey = s_suppkey
AND w_nationkey = n2.n_nationkey
AND n1.n_nationkey = s_nationkey
AND n2.n_regionkey = r_regionkey
AND n1.n_name = "United States
group by r_name;
This is my result:
warehouse capacity
AFRICA 4314
AMERICA 10446
EUROPE 8402
MIDDLE EAST 10866
Aliases for the tables used:
Warehouse table:
w_warehousekey decimal(9,0) not null,
w_name char(100) not null,
w_capacity decimal(6,0) not null,
w_suppkey decimal(9,0) not null,
w_nationkey decimal(2,0) not null
I used the TPC-H database. This link shows all the aliases for the tables I have available:
https://docs.snowflake.com/en/user-guide/sample-data-tpch.html
Use left joins to ensure region is always included. The total for regions with no corresponding rows will be null. To overcome that use the IFNULL function.
Example:
Region
RegionID RegionName
1 Europe
2. Americas
Sales
Date RegionID Amount
1/1/2020 1 10
2/1/2020 1 20
SELECT r.RegionName, IFNULL(SUM(s.Amount), 0) AS Amount
FROM region r LEFT JOIN sales s ON r.RegionID= s.RegionID
GROUP BY RegionName
Expected result:
RegionName Amount
Europe 30
Americas 0

Combine specific column values

I have the following result set:
Region
North
South
West
Reason
New Admission
New Admit
Other
Amount
5.00
6.00
8.00
What I am needing to do is combine just the "New Admission" and "New Admit" values from the Reason column with the corresponding amounts.
So what I would like to end up with is this result set:
Region
North
South
West
Reason
New Admission/New Admit
Other
Amount
11.00
8.00
The query that I have created to build the first result set is as follows:
SELECT Region, Reason, CAST(SUM(Amount) AS Decimal(18,2)) AS Amount FROM Table GROUP BY Region, Reason
Just wondering if anyone else has insight into this. I am using SQL Server 2019
This is a screen shot of the actual data from my table
Use a case expression:
SELECT Region,
(CASE WHEN Reason IN ('New Admission', 'New Admit') then 'New Admission/New Admit'
ELSE 'Other'
END) as Reason,
CAST(SUM(Amount) AS Decimal(18,2)) AS Amount
FROM Table
GROUP BY Region,
(CASE WHEN Reason IN ('New Admission', 'New Admit') then 'New Admission/New Admit'
ELSE 'Other'
END);

Calculating Proportions for Baseball-Related Query

Here are my two tables:
BIO - contains player biographical information with the following columns
i. PLAYER_ID
ii. PLAYER_NAME
iii. DATE_OF_BIRTH
iv. TEAM_NAME
PITCHES - contains batter and pitcher statistics by pitch with the following columns
i. GAME_DATE (formatted YYYY-MM-DD, e.g. 2016-01-01)
ii. BATTER_PLAYER_ID
iii. PITCHER_PLAYER_ID
iv. PITCHER_THROW_SIDE (L/R)
v. BATTER_HAND (L/R)
vi. PITCH_TYPE (Changeup, Curveball, Cutter, 4-seam fastball, Knuckleball, 2-Seam
Fastball, Slider, Splitter)
vii. PITCH_CALL (Ball, CatcherInterference, FoulBall, HitByPitch, InPlay, StrikeCalled,
StrikeSwinging)
viii. IN_ZONE (YES/NO)
I want a query that returns the names of players with an in-zone or out-of-zone swinging strike rate of greater than 15% for fastballs for the 2016-2017 seasons, combined. I also want team name, pitcher handedness, and to include cutters and sinkers as fastballs.
Here is what I have so far:
SELECT b.PLAYER_NAME, b.TEAM_NAME, p.PITCHER_THROW_SIDE
FROM BIO AS b INNER JOIN PITCHES AS p
ON b.PLAYER_ID = p.PITCHER_PLAYER_ID
WHERE p.PITCH_TYPE = '4-seam fastball' OR p.PITCH_TYPE = '2-Seam' OR p.PITCH_TYPE = 'Cutter'
AND p.GAME_DATE BETWEEN 2016-01-01 AND 2017-12-31
GROUP BY b.PLAYER_ID
HAVING (Count(IN_ZONE)) ....
I think this is the right idea... but I'm a bit lost now as to how I can include the 15% in-zone/out-of-zone rates.
Thank you for any help.
How do you calculate in-zone or out-of-zone swinging strike rate of greater than 15%?
if NR_YES/total
HAVING sum(case when IN_ZONE='YES' then 1 else 0)/count(*)>0.15
If Nr_Yes/Nr_NO
HAVING sum(case when IN_ZONE='YES' then 1 else 0)/count(*)>3/17
Note that 3/17 is 15/85. Since there is only the possibility of yes and no
Also note that with the sum of 0s and 1s I am actually simulating a
count(*) where IN_ZONE='YES'

sql - dynamically populate columns in a result set based on another column

We want to determine the amount of assets that customers have insured or uninsured.
Basically the insured amount is £85K, and we aportion that accross several account types held by the custimer in order of the accounts liquidity, so for example, a current account is more liquid than a 95 day notice account therefore the current account would receive the money first...
We have the below code that generates the priorities based on the account type and of course we could now loop through the table and determine the insured amount for each account type for each customer, and anything non-insured would become the uninsured amount for that customer.
But is there a way to do this in the main select statement?
declare #Priorities TABLE
(
[refID] [int] IDENTITY(1,1) NOT NULL,
acno nvarchar(10),
Suffix nvarchar(6) null,
Unit nvarchar(5) null,
CT nvarchar(2) null,
LOB nvarchar(2) null,
Participants int null,
VAL float null,
xPriority int null,
insuredAmt float null,
uninsuredAmt float null
)
declare #Count int=0
declare #I int=0
insert into #Priorities
select acno, suffix, unit, CT, LOB, Participants, Val, xPriority =
case
when LOB BETWEEN 'GA' AND 'GN' then 1 -- Current accounts
when LOB IN ( 'HY' , 'H1') then 2 -- Call
when CHARINDEX(LOB, 'HA; HC; HM; HQ; IA; IB; IC; IE;IG; IU;')>0 then 3 -- Instant Access
when LOB='HO' then 4 -- 14 Day notice
when CHARINDEX(LOB, 'DN; HG; HK; HL; HP; HS; HU; IV; IW;')>0 then 5 -- 35-Day notice
when LOB IN ('DN', 'HV') then 6 -- 95-Day notice
when (LOB BETWEEN 'IS' AND 'IT') or (LOB between 'JA' and 'JZ' ) then 7 -- Residual Deals
end,
insuredAmt=null,
uninsuredAmt=null
from actdata
where datadate = '20131212' /*#processdate*/
and CHARINDEX(CT, 'AA;AC;AE;AG;BA;BC;BE;BG;CA;DE;DG;ZA;')=0
and LOB BETWEEN 'GA' AND 'KZ' And val>0
and participants is not null
order by acno
what I'd like to do is calculate the insured amount for each record based on the value of the column xPriority.
Also, we multiply the total insured amount by the number of participants in the account, so if there are 5 participants in the account, then the total insured amount for them all would be 5*85000 = £425000.
the alternative would be a loop or a cursor.
this would be run on a daily basis when the data is brought into the reporting database from the iSeries system by another process.
thanks
Philip
I believe the answer is 'No', but that's based on a particular interpretation of:
"Also, we multiply the total insured amount by the number of participants in the account, so if there are 5 participants in the account, then the total insured amount for them all would be 5*85000 = £425000."
Without multiple participants in an account, I think it would be possible to order the SELECT statement by the customer ID and the CASE statement for xPriority, keep a decreasing running total (down form 85K and resetting for each customer) and calculate insuredAmt and uninsuredAmt accordingly. This would tell you how each individual's insured amount was apportioned across their accounts.
However, with multiple participants you have to resolve the following problem:
Mary has a current account of £40k and a 30 day notice account with £20k.
Bob has a current account of £50k and a 30 day notice account with £20k.
Mary and Bob also have a joint, 14 day notice account of £60k.
The two current accounts are fully insured, as is the 14 day notice account (Mary has £45k and Bob £35k allowance left). The problem is that the coverage of the two 30 day accounts depends on which individual's insurance is apportioned to the joint account (all of Mary's plus a bit of Bob's; all of Bob's plus a bit of Mary's; £30k of each; 9:7 ratio from Mary:Bob?). I don't think this problem is even resolvable in logical terms without reference to the full terms and conditions of the accounts! When it comes to programming it, I'd advise extreme caution. Start by writing inefficient code you can test thoroughly on such cases, then use that as a benchmark for checking code with enhanced efficiency.
Good luck!

Oracle SQL - Filling student graduation requirements with credits earned. (Loop or Creative Join?)

I'm a bit stumped on how to structure this query. I have two tables, one with graduation requirements (GradReq) and one with earned student credits (StoredGrades). GradReq lists each "credit bucket" and the associated course numbers that can fill it. StoredGrades lists the course number and whether the student passed the course. Theoretical examples are below.
GradReq:
CreditBucket CourseNumber
Algebra I 100000
Algebra I 100001
Algebra II 100001
Algebra II 100002
StoredGrades:
CourseNumber Passed StudentID
100001 1 30003
The issue I'm having is that with a standard (left)join both the Algebra I and Algebra II credit buckets will be filled for student 30003 when really that credit can only be used once. Any ideas?
I'm thinking it might be best to loop through each of the credit buckets and evaluate them one at a time, but I'm not sure how that would look. Thanks for any help!
The data model that you currently have can only answer eligibility questions such as "Which requirements does student1 satisfy). When you do an outer join, you get 2 results, which is correct. Student 1 has finished one course, which satisfies his grad requirement for Algebra1 or Algebra2.
The actual credit association that you are trying to implement is usually done in a different entity/table. Something like...
GradReqUsed (or GradReqConsumtion, perhaps?)
CreditBucket CourseNumber StudentID
Algebra I 100001 30003
Which basically means the student will have 100001 counted towards the Algebra1 bucket.