Find Sales Ratio To Target in MDX - html-table

I have an MDX query resulting this value
+-----------+----------+--------------+--------+----------------+------+
| | Sales Cumulative | Sales Ratio To Target |
+ +----------+--------------+--------+----------------+-------
| | Town A | Town B | Town C | Town A | Town B | Town C |
+-----------+----------+--------------+--------+----------------+------+
| Quarter 1 | 10 | 20 | 30 | ? | ? | ? |
| Quarter 2 | 30 | 40 | 60 | ? | ? | ? |
+-----------+----------+--------------+--------+----------------+------+
I dont really know how to calculate in MDX to get the sales ratio to target, To calculate the ratio is simply divided the sales cumulative (measure) with the each town target
My geography dimension look like this,
+----------+----+
| Dim_Geography |
+---------------+
| Province |
| District |
| Town |
| Target |
+---------------+
Currently this is my mdx script for sales cumulative..
WITH
MEMBER [Measures].[SalesCumulative] AS
Sum
(
NULL : [Date].[Quarter].CurrentMember
,[Measures].[Sales]
)
SELECT
{[Measures].[SalesCumulative]}
*
Descendants
(
[Geografi].[District].[All],
,AFTER
) ON 0
,{Descendants([Date].[Quarter].[All],,AFTER)} ON 1
FROM [Cube];

If the target is a property of the geo dimension then maybe extract that into a measure and then divide the two:
WITH
MEMBER [Measures].[SalesCumulative] AS
Sum
(
NULL : [Date].[Quarter].CurrentMember
,[Measures].[Sales]
)
MEMBER [Measures].[Target] AS
[Geografi].[District].[District].CurrentMember.Properties("Target")
MEMBER [Measures].[Ratio] AS
[Measures].[SalesCumulative] / [Measures].[Target]
SELECT
{[Measures].[SalesCumulative],
[Measures].[Ratio] }
*
Descendants
(
[Geografi].[District].[All],
,AFTER
) ON 0
,{Descendants([Date].[Quarter].[All],,AFTER)} ON 1
FROM [Cube];

Related

Calculate total amount PGSQL

query which calculates the total amount in dollars of stolen goods for each month for restricted and neutral items.
I have 2 tables
first
| UPC | item | in_stock | price | ship_day | class |
1 | 101 | 'generator' | 16 | 5999 | '12-1-2065'| 'restricted'
2 | 102 | 'blank tape' | 30 | 3000 | '12-1-2065'| 'neutral'
second
| UPC | unit_stolen |
1 | 101 | 4 |
1 | 401 | 2 |
If I understand correctly, this is basically a join and group by:
select date_trunc('mon', f.ship_day) as yyyymm,
sum(f.price * s.unit_stolen) filter (where f.class = 'restricted'),
sum(f.price * s.unit_stolen) filter (where f.class = 'neutral')
from first f join
second s
on f.upc = s.upc
group by date_trunc('mon', f.ship_day)

Oracle SQL newbie - Add new column that gets occurrence and computations

This post is enhanced version of my previous post here.
Please Note: This is not duplicate post or thread.
I have 3 tables:
1. REQUIRED_AUDITS (Independent table)
2. SCORE_ENTRY (SCORE_ENTRY is One to Many relationship with ERROR table)
3. ERROR
Below are the dummy data and table structure:
REQUIRED_AUDITS TABLE
+-------+------+----------+---------------+-----------------+------------+----------------+---------+
| ID | VP | Director | Manager | Employee | Req_audits | Audit_eligible | Quarter |
+-------+------+----------+---------------+-----------------+------------+----------------+---------+
| 10001 | John | King | susan#com.com | jake#com.com | 2 | Y | FY18Q1 |
| 10002 | John | King | susan#com.com | beth#com.com | 4 | Y | FY18Q1 |
| 10003 | John | Maria | tony#com.com | david#com.com | 6 | N | FY18Q1 |
| 10004 | John | Maria | adam#com.com | william#com.com | 3 | Y | FY18Q1 |
| 10005 | John | Smith | alex#com.com | rose#com.com | 6 | Y | FY18Q1 |
+-------+------+----------+---------------+-----------------+------------+----------------+---------+
SCORE_ENTRY TABLE
+----------------+------+----------+---------------+-----------------+-------+---------+
| SCORE_ENTRY_ID | VP | Director | Manager | Employee | Score | Quarter |
+----------------+------+----------+---------------+-----------------+-------+---------+
| 1 | John | King | susan#com.com | jake#com.com | 100 | FY18Q1 |
| 2 | John | King | susan#com.com | jake#com.com | 90 | FY18Q1 |
| 3 | John | King | susan#com.com | beth#com.com | 98.45 | FY18Q1 |
| 4 | John | King | susan#com.com | beth#com.com | 95 | FY18Q1 |
| 5 | John | King | susan#com.com | beth#com.com | 100 | FY18Q1 |
| 6 | John | King | susan#com.com | beth#com.com | 100 | FY18Q1 |
| 7 | John | Maria | adam#com.com | william#com.com | 99 | FY18Q1 |
| 8 | John | Maria | adam#com.com | william#com.com | 98.1 | FY18Q1 |
| 9 | John | Smith | alex#com.com | rose#com.com | 96 | FY18Q1 |
| 10 | John | Smith | alex#com.com | rose#com.com | 100 | FY18Q1 |
+----------------+------+----------+---------------+-----------------+-------+---------+
ERROR TABLE
+----------+-----------------------------+----------------+
| ERROR_ID | ERROR | SCORE_ENTRY_ID |
+----------+-----------------------------+----------------+
| 10 | Words Missing | 2 |
| 11 | Incorrect document attached | 2 |
| 12 | No results | 3 |
| 13 | Value incorrect | 4 |
| 14 | Words Missing | 4 |
| 15 | No files attached | 4 |
| 16 | Document read error | 7 |
| 17 | Garbage text | 8 |
| 18 | No results | 8 |
| 19 | Value incorrect | 9 |
| 20 | No files attached | 9 |
+----------+-----------------------------+----------------+
I have query that give below output:
+----------+---------------+------------------+------------------+------------------+
| | | Director Summary | | |
+----------+---------------+------------------+------------------+------------------+
| Director | Manager | Audits Required | Audits Performed | Percent Complete |
| King | susan#com.com | 6 | 6 | 100% |
| Maria | adam#com.com | 3 | 2 | 67% |
| Smith | alex#com.com | 6 | 2 | 33% |
+----------+---------------+------------------+------------------+------------------+
Now I would like to add column where I want the number of scores that have an error associated with them divided by total count of scores:
It's not total count of errors divided by count of scores. Instead its count of each occurrence of error and divide by count of score. Please find below example:
Considering
Director:King
Manager:susan#com.com
From SCORE_ENTRY TABLE and ERROR table,
King has 6 entries in SCORE_ENTRY TABLE
6 entries in ERROR TABLE
Instead of 6 entries in ERROR TABLE, I would like to have occurrence of error ie., 3 errors.
Formula to calculate Quality:
Quality = 1 - (sum of error occurrence / total score)*100
For King:
Quality = 1 - (3/6)*100
Quality = 50
Please Note: It's not 1 - (6/6)*100
For Maria:
Quality = 1 - (2/2)*100
Quality = 0
Below is the new output I need with new column called Quality:
+----------+---------------+---------+------------------+------------------+------------------+
| | | | Director Summary | | |
+----------+---------------+---------+------------------+------------------+------------------+
| Director | Manager | Quality | Audits Required | Audits Performed | Percent Complete |
| King | susan#com.com | 50% | 6 | 6 | 100% |
| Maria | adam#com.com | 0% | 3 | 2 | 67% |
| Smith | alex#com.com | 50% | 6 | 2 | 33% |
+----------+---------------+---------+------------------+------------------+------------------+
Below is the query am having (Thanks to #Kaushik Nayak, #APC and others) and need to add new column to this query:
WITH aud(manager_email, director, quarter, total_audits_required)
AS (SELECT manager_email,
director,
quarter,
SUM (CASE
WHEN audit_eligible = 'Y' THEN required_audits
END)
FROM required_audits
GROUP BY manager_email,
director,
quarter), --Total_audits
scores(manager_email, director, quarter, audits_completed)
AS (SELECT manager_email,
director,
quarter,
Count (score)
FROM oq_score_entry s
GROUP BY manager_email,
director,
quarter) --Audits_Performed
SELECT a.director,
a.manager_email manager,
a.total_audits_required,
s.audits_completed,
Round(( ( s.audits_completed ) / ( a.total_audits_required ) * 100 ), 2)
percentage_complete,
a.quarter
FROM aud a
left outer join scores s
ON a.manager_email = s.manager_email
WHERE ( :P4_MANAGER_EMAIL = a.manager_email
OR :P4_MANAGER_EMAIL IS NULL )
AND ( :P4_DIRECTOR = a.director
OR :P4_DIRECTOR IS NULL )
AND ( :P4_QUARTER = a.quarter
OR :P4_QUARTER IS NULL )
ORDER BY a.total_audits_required DESC nulls last
Please let me know if its confusing or need more details. Am open for any suggestions and feedback.
Appreciate any help.
Thanks,
Richa
Update:
Well my first guess has been wrong, and I hope now I'm getting it right.
According to your and shawnt00's comments, you need to compute the count of score entries that have corresponding entries in ERROR table, and use it in quality calculation.
This count you get with the expression:
COUNT ((select max(1) from "ERROR" o where o.score_entry_id=s.score_entry_id)) AS error_occurences
max(1) returns 1 when there is an entry in "ERROR" and NULL otherwise. COUNT skips nulls.
I hope this is clear.
Quality is computed as
(1 - error_occurences/audits_completed)*100%
Below is the full script, where manager_email renamed to manager and oq_score_entry renamed to score_entry.
This is in accordance with your scheme. Also I removed unnecessary WITH column mapping, it just complicates things in this case.
WITH aud AS (SELECT manager, director, quarter, SUM (CASE
WHEN audit_eligible = 'Y' THEN req_audits
END) total_audits_required
FROM required_audits
GROUP BY manager, director, quarter), --Total_audits
scores AS (
SELECT manager, director, quarter,
Count (score) audits_completed,
COUNT ((select max(1) from "ERROR" o where o.score_entry_id=s.score_entry_id)
) error_occurences -- ** Added **
FROM score_entry s
GROUP BY manager, director, quarter
) --Audits_Performed
SELECT a.director,
a.manager manager,
a.total_audits_required,
s.audits_completed,
Round(( 1 - ( s.error_occurences ) / ( s.audits_completed )) * 100, 2), -- ** Added **
Round(( ( s.audits_completed ) / ( a.total_audits_required ) * 100 ), 2)
percentage_complete,
a.quarter
FROM aud a
left outer join scores s ON a.manager = s.manager
WHERE ( :P4_manager = a.manager
OR :P4_manager IS NULL )
AND ( :P4_DIRECTOR = a.director
OR :P4_DIRECTOR IS NULL )
AND ( :P4_QUARTER = a.quarter
OR :P4_QUARTER IS NULL )
ORDER BY a.total_audits_required DESC nulls last
About total_errors:
To add this column you can either use a technique similar to the one used before in scores:
scores AS (
SELECT manager, director, quarter,
count (score) audits_completed,
count ((select max(1) from "ERROR" o where o.score_entry_id=s.score_entry_id )
) error_occurences,
sum ( ( SELECT count(*) from "ERROR" o where o.score_entry_id=s.score_entry_id )
) total_errors -- summing error counts for matched score_entry_ids
FROM score_entry s
GROUP BY manager, director, quarter
)
Or you can rewrite the scores CTE joining score_entry and error, and that would require using DISTINCT on score_entry fields to avoid duplication of rows:
scores AS (
SELECT manager, director, quarter,
count(DISTINCT s.score_entry_id) audits_completed,
count(DISTINCT e.score_entry_id ) error_occurences, -- counting distinct score_entry_ids present in Error
count(e.score_entry_id) total_errors -- counting total rows in Error
FROM score_entry s
LEFT JOIN "ERROR" e ON s.score_entry_id=e.score_entry_id
GROUP BY manager, director, quarter
)
The latter approach is a bit less maintable, since it requires to be careful about unwanted duplication.
Yet another (and may be the most proper) way is to make a separate(third) CTE, but I don't think the query is complex enough to warrant this.
Original answer:
I might be wrong, but it seems to me that by "count of each occurrence of error" you are trying to describe COUNT(DISTINCT expr). That is to count unique occurences of error for each (manager_email, director, quarter).
If so, change the query a bit:
WITH aud(manager_email, director, quarter, total_audits_required)
AS (SELECT manager_email,
director,
quarter,
SUM (CASE
WHEN audit_eligible = 'Y' THEN required_audits
END)
FROM required_audits
GROUP BY manager_email,
director,
quarter), --Total_audits
scores(manager_email, director, quarter, audits_completed, distinct_errors)
AS (SELECT manager_email,
director,
quarter,
Count (score),
COUNT (DISTINCT o.error_id) -- ** Added **
FROM oq_score_entry s join error o on o.score_entry_id=s.score_entry_id
GROUP BY manager_email,
director,
quarter) --Audits_Performed
SELECT a.director,
a.manager_email manager,
a.total_audits_required,
s.audits_completed,
Round(( ( s.distinct_errors ) / ( s.audits_completed ) * 100 ), 2) quality, -- ** Added **
Round(( ( s.audits_completed ) / ( a.total_audits_required ) * 100 ), 2)
percentage_complete,
a.quarter
FROM aud a
left outer join scores s
ON a.manager_email = s.manager_email
WHERE ( :P4_MANAGER_EMAIL = a.manager_email
OR :P4_MANAGER_EMAIL IS NULL )
AND ( :P4_DIRECTOR = a.director
OR :P4_DIRECTOR IS NULL )
AND ( :P4_QUARTER = a.quarter
OR :P4_QUARTER IS NULL )
ORDER BY a.total_audits_required DESC nulls last
The join on your main query will need to include director and quarter once you have more data.
I suppose the easiest way to fix this is to follow the structure you've got and add another table expression joining it to the rest of your results in the same way as the original two.
select manager_email, director, quarter,
100.0 - 100.0 * count (distinct e.score_entry_id) / count (*) as quality
from score_entry se left outer join error e
on e.score_entry_id = se.score_entry_id
group by manager_email, director, quarter
What would have made most of your explanation unnecessary is to have simply said that you want the number of scores that have an error associated with them. It was difficult to draw that out from the information you provided.

SQL Moving a Row to be a column - PIVOT query [duplicate]

This question already has answers here:
Convert Rows to columns using 'Pivot' in SQL Server
(9 answers)
Closed 5 years ago.
Given:
+-----------+---------------+-------------+-------+
| Name | Location | Description | Value |
+-----------+---------------+-------------+-------+
| Company A | Houston | Sales | 100 |
| Company A | Houston | Profit | 50 |
| Company B | San Francisco | Sales | 500 |
| Company B | San Francisco | Profit | 200 |
| Company C | New York | Sales | 200 |
| Company C | San Francisco | Profit | 150 |
+-----------+---------------+-------------+-------+
How do I use SQL to transform it to:
+----------+---------------+--------+---------+
| Name | Location | Sales | Profit |
+----------+---------------+--------+---------+
| CompanyA | Houston | 100 | 50 |
| CompanyB | San Francisco | 500 | 200 |
| CompanyC | New York | 200 | 150 |
+----------+---------------+--------+---------+
Try either 1 or 2 query: http://sqlfiddle.com/#!6/bb33b/7
The second one gives the exact results shown in your question, but my guess is that rather the first one is what you are looking for.
SELECT Name, Location,
Sum( Case Description when 'Sales' Then Value else 0 end ) Sales,
Sum( Case Description when 'Profit' Then Value else 0 end ) Profit
FROM table1
GROUP BY Name, Location
Order by 1,2
;
| Name | Location | Sales | Profit |
|-----------|---------------|-------|--------|
| Company A | Houston | 100 | 50 |
| Company B | San Francisco | 500 | 200 |
| Company C | New York | 200 | 0 |
| Company C | San Francisco | 0 | 150 |
SELECT Name, Min( Location ) As Location,
Sum( Case Description when 'Sales' Then Value else 0 end ) Sales,
Sum( Case Description when 'Profit' Then Value else 0 end ) Profit
FROM table1
GROUP BY Name
Order by 1,2
;
;
| Name | Location | Sales | Profit |
|-----------|---------------|-------|--------|
| Company A | Houston | 100 | 50 |
| Company B | San Francisco | 500 | 200 |
| Company C | New York | 200 | 150 |
Assuming your description values are only ever Sales and Profit, here is a CTE solution. I called the initial table Before.
with cteSales as (select name, value from Before where description = 'Sales')
, cteProfit as (select name, value from Before where description = 'Profit')
select
distinct
name
, location
, (select value from cteSales where name = B.name) as Sales
, (select value from cteProfit where name = B.name) as Profit
from Before B
To transform your data to your desired form, you could do:
SELECT NAME
,LOCATION
,SUM (CASE WHEN DESCRIPTION = 'Sales' THEN Values ELSE 0 END) AS SALES
,SUM (CASE WHEN DESCRIPTION = 'Profit' THEN Values ELSE 0 END) AS PROFIT
FROM yourtable
GROUP BY NAME
,LOCATION
You can use PIVOT function to transform row values into column however it will provide slightly different but more reasonable output -
SELECT Name, Location, Sales, Profit
FROM
(
SELECT *
FROM Table1
) AS SourceTable
PIVOT
(
MIN(value)
FOR Description IN (Sales, Profit)
) AS PivotTable
ORDER BY Name, Location;
Will generate the following output -
+-----------+---------------+-------+--------+
| Name | Location | Sales | Profit |
+-----------+---------------+-------+--------+
| Company A | Houston | 100 | 50 |
+-----------+---------------+-------+--------+
| Company B | San Francisco | 500 | 200 |
+-----------+---------------+-------+--------+
| Company C | New York | 200 | null |
+-----------+---------------+-------+--------+
| Company C | San Francisco | null | 150 |
+-----------+---------------+-------+--------+
You can use this as an alternate solution.

Repeat all rows in left table for each unique ID in other table

I have a team of people who are scored on up to three metrics; sales, leads and Hours.
I have a table (tblScores) in MS Access which holds these scores but only if there is any. (e.g if someone had no sales there would be no entry for them for sales)
| USERID | Metric | Score |
----------------------------------
| 20511 | Sales | 12 |
| 20511 | Leads | 9 |
| 20511 | Hours | 8 |
| 20694 | Sales | 10 |
| 20694 | Hours | 7.5 |
I am trying to create an SQL query that will output three records (each possible metric) for each User in the above table including null values where they don't have an entry for that metric. e.g
| USERID | Metric | Score |
----------------------------------
| 20511 | Sales | 12 |
| 20511 | Leads | 9 |
| 20511 | Hours | 8 |
| 20694 | Sales | 10 |
| 20694 | Leads | Null |
| 20694 | Hours | 7.5 |
I have set up another table (tblMetrics) with just these 3 metrics
| Metric |
---------------
| Sales |
| Leads |
| Hours |
and tried to do a left join on the metric table against the score table
SELECT tblMetrics.*, TblScores.UserID, TblScores.Score
FROM tblMetrics LEFT JOIN TblScores ON tblMetrics.Metric = TblScores.Metric;
but it is still not giving the desired output. Does anyone know if this possible?
You need to do a CROSS JOIN first to generate all combinations, then do the LEFT JOIN to find which one are missing and assign NULL
I check access syntaxis and the CROSS JOIN should be write like this
SELECT DISTINCT M.Metric, S.USERID
FROM tblMetric M, tblScore S
And the Left Join should be
SELECT userMetrc.*, S.Score
FROM ( SELECT DISTINCT M.Metric, S.USERID
FROM tblMetric M, tblScore S
) userMetric
LEFT JOIN tblScore S
ON ( userMetric.USERID = S.USERID
AND userMetric.Metric = S.Metric )

Pivot table returns multiple rows with NULL, results should be grouped on one row

I have the table below which I am looking to pivot so that the descriptions in column 1 become column headers in the new pivot.
Nominal Group | GrpID | Description | Value | CustomerID
---------------+-------+-----------------+-------------+-----------
Balance Sheet | 7 | BS description | 56973.10 | 2
Cost of Sales | 4 | COS description | 55950.17 | 2
Sales | 1 | Sales | -178796.18 | 2
Labour Costs | 5 | Wages | 18596.43 | 2
Overheads | 6 | Rent | 47276.48 | 2
I'm using the code below to get the result set below that:
select * from trialbalancegrouping
PIVOT (Sum(value)
for nominalgroupname in ([Sales],[Cost of Sales],[Labour Costs],[Overheads])) AS PVTtable
-
GrpID | Description | CustomerID | Sales | Cost of Sales | Labour Costs | Overheads
------+---------------+------------+------------+---------------+--------------+-----------
1 | Sales | 2 | -178796.18 | NULL | NULL | NULL
2 |COS Description| 2 | NULL | 55950.17 | NULL | NULL
3 | Labour | 2 | NULL | NULL | 18596.43 | NULL
4 | Overheads | 2 | NULL | NULL | NULL | 47276.48
Ideally, I'd want the output to be one row per customer, like this:
CustomerID | Sales | Cost of Sales | Labour Costs | Overheads
-----------+------------+----------------+--------------+------------
2 | -178796.18 | 55950.17 | 18596.43 | 47276.48
Any columns that are available are passed to the PIVOT function, so all apart from the column aggregated, and the column pivoted are implicitly grouped by, so since GrpID and Description are present, and not included it is grouped by, therefore you get one row per combination of these. You need to limit the columns passed to the pivot function by using a subquery:
SELECT pvt.CustomerID,
pvt.Sales,
pvt.[Cost of Sales],
pvt.[Labour Costs],
pvt.[Overheads]
FROM ( SELECT CustomerID, nominalgroupname, Value
FROM trialbalancegrouping
) AS t
PIVOT
( SUM(Value)
FOR nominalgroupname IN
( [Sales],[Cost of Sales],
[Labour Costs],[Overheads]
)
) AS pvt;