How to add up unique values from table in SQL - mysql

I have a table in SQL that looks like this
Person brand brand_spend category category_spend
0 p1 b1 20 c1 100
1 p1 b2 50 c1 100
2 p2 b1 25 c2 40
3 p1 b3 30 c1 100
4 p1 b2 15 c2 70
I need to tag every customer based on percentage spend he has made at a brand based on total category spend where that brand is present.
So essentially I would want to tag Person p1 for brand b1' as percentage spend at 'b1 which should be calculated as 45/ 140
How this can be achieved. If I roll up on brand level to find total category spend then I think duplicates rows would add up.
I just want to find customer's Spend at a brand based on total spend at Brand for all categories where that brand is present.

You need grouping on two levels, as shown here:
select person, brand, sum(brand_spend) personspend, spendbrandallcats,
round(sum(brand_spend)/spendbrandallcats,3) pbratio from tbl t
inner join ( -- brand statistics: sum of all spends per brand in all categories
select brand br, sum(casp) spendbrandallcats from tbl
inner join ( -- category statistics: total category sums
select category ca, sum(brand_spend) casp from tbl group by category
) catspend ON ca=category
group by brand
) brandstats on br=brand
group by person,brand
These are the results:
person brand personspend spendbrandallcats pbratio
p1 b1 20 140 0.143
p1 b2 65 140 0.464
p1 b3 30 100 0.3
p2 b1 25 140 0.179
See the little demo here: https://rextester.com/SVNH27609

Sometimes things are not as complicated as it seems and a simple query does the trick.
select person , brand , sum(prsn_brand_spend) ,
sum(category_spend) , (sum(prsn_brand_spend)/sum(category_spend)) as perc_spend
from tbl group by person , brand
The Results are
person brand brand_spend category_spend perc
p1 b1 20 100 0.200000
p1 b2 65 170 0.382353
p1 b3 30 100 0.300000
p2 b1 25 40 0.625000

Related

Calculate percentages with 2 fields in the GROUP BY clause - SQL

I have the following results that list different departments within different offices. Each departments hours and cost has been calculated for each office.
OfficeCode Department Total Hours Total Costs
---------------------------------------------------------
OFC-UK Buying 44 50850
OFC-UK Design 42 30008
OFC-UK R&D 28 70600
OFC-AZ Buying 52 30801
OFC-AZ Design 34 50080
OFC-BG Buying 25 40030
OFC-BG Design 37 10020
The above results come from the following query:
SELECT office.office_code,
department.department_description,
CAST(SUM(timesheet_entry.timesheet_entry_hour) + (SUM(timesheet_entry.timesheet_entry_minute)/60) as float) as total_hours,
SUM((timesheet_entry.timesheet_entry_hour + ((timesheet_entry.timesheet_entry_minute)/60)) * person.person_rate) as 'total_cost'
FROM timesheet_entry
INNER JOIN user
ON user.user_obj = timesheet_entry.user_obj
INNER JOIN person
ON person.person_obj = user.person_obj
INNER JOIN department
ON department.department_obj = user.department_id
INNER JOIN office
ON office.office_id = timesheet_entry.office_id
GROUP BY timesheet_entry.office_code, department.department_description
ORDER BY office.office_code, department.department_description;
I need to calculate the percentages of the total_hours and total_cost fields, per department, per office. Such as an example of the desired results:
OfficeCode Department Total Hours HoursPercentage Total Costs CostPercentage
-----------------------------------------------------------------------------------------------
OFC-UK Buying 44 38.59 50850 33.57
OFC-UK Design 42 36.84 30008 19.81
OFC-UK R&D 28 24.56 70600 46.61
OFC-AZ Buying 52 etc etc etc
OFC-AZ Design 34 etc etc etc
OFC-BG Buying 25 etc etc etc
OFC-BG Design 37 etc etc etc
I tried to put the results into a temp-table, and then calculate the percentages ontop of that with the GROUP BY, but it seems it doesnt take the groups into consideration. Is there a better, and accurate, way to do this?
CREATE TEMPORARY TABLE OfficeDepartmentTotalsTbl
SELECT office.office_code,
department.department_description,
CAST(SUM(timesheet_entry.timesheet_entry_hour) + (SUM(timesheet_entry.timesheet_entry_minute)/60) as float) as total_hours,
SUM((timesheet_entry.timesheet_entry_hour + ((timesheet_entry.timesheet_entry_minute)/60)) * person.person_rate) as 'total_cost'
FROM timesheet_entry
INNER JOIN user
ON user.user_obj = timesheet_entry.user_obj
INNER JOIN person
ON person.person_obj = user.person_obj
INNER JOIN department
ON department.department_obj = user.department_id
INNER JOIN office
ON office.office_id = timesheet_entry.office_id
GROUP BY timesheet_entry.office_code, department.department_description
ORDER BY office.office_code, department.department_description;
SELECT
OfficeDepartmentTotalsTbl.office_code,
OfficeDepartmentTotalsTbl.department_description,
OfficeDepartmentTotalsTbl.total_hours,
OfficeDepartmentTotalsTbl.total_hours * 100 /
(SELECT SUM(OfficeDepartmentTotalsTbl.total_hours) FROM OfficeDepartmentTotalsTbl) AS 'department_percentage_of_total_hours',
OfficeDepartmentTotalsTbl.total_cost,
OfficeDepartmentTotalsTbl.total_cost * 100 / (SELECT
SUM(OfficeDepartmentTotalsTbl.total_cost)
FROM
OfficeDepartmentTotalsTbl) AS 'department_percentage_of_total_cost'
FROM
OfficeDepartmentTotalsTbl
GROUP BY OfficeDepartmentTotalsTbl.office_code, OfficeDepartmentTotalsTbl.department_description;
DROP TEMPORARY TABLE OfficeDepartmentTotalsTbl;

(Retention) How to divide all values by value in first row across different categories of users in SQL?

I have the following table:
Day
Category
Count
D1
A
10
D1
B
20
D2
A
8
D2
B
10
D3
A
6
D3
B
5
I'm trying to create a percentage column by dividing the values in the third column (Count) by the value for D1 across all categories in the second column (Category; in this case 10 and 20 for A and B respectively). This should output something like:
Day
Category
Count
Pct
D1
A
10
100%
D1
B
20
100%
D2
A
8
80%
D2
B
10
50%
D3
A
6
60%
D3
B
5
25%
The furthest I got is the code below, but I can't figure out how to do the division by category.
SELECT
day,
category,
count,
count/(SELECT count FROM table WHERE day = 'D1')*100 AS pct
FROM
table
ORDER BY 1
)
This is the same as Asgar's query but with the unnecessary table derivation removed -
SELECT
`t1`.*,
ROUND((`t1`.`count` / `t2`.`count`) * 100) `pct`
FROM `table` `t1`
JOIN `table` `t2`
ON `t1`.`category` = `t2`.`category`
AND `t2`.`day` = 'D1'
ORDER BY 1, 2;
this should do what you ask:
SELECT
day,
category,
count,
count/(SELECT count
FROM table as sub
WHERE day = 'D1'
AND sub.category = main.category)*100 AS pct
FROM
table as main
I assumed that the denominator will always just be based on "D1", and that combinations of day-category will always be unique.
This should word accurately for you:
SELECT
main.*,
ROUND(((main.Count/d2.Count)*100),2)
FROM
(SELECT * FROM day_table d1) main
JOIN day_table d2 ON d2.Category=main.Category AND d2.Day='D1'
ORDER BY
main.Day,
main.Category

How to create new column and new row based on two tables?

I have two tables:
Table 1
MARKET ATC3 ATC4 PRODUCT BOOLEAN FLAG JOINING COLUMN
A1 B1 B1C1 D1 1 ATC4
A2 B1 B1C2 D2 1 ATC4
A2 B1 B1C3 ATC4
FAMILY A B1 ATC3
Table 2:
PRODUCT ATC3 ATC4 VALUES
D1 B1 B1C1 10
D1 B1 B1C1 20
D2 B1 B1C2 15
D2 B1 B1C2 25
D2 B1 B1C2 10
D3 B1 B1C3 5
My desired output:
PRODUCT ATC3 ATC4 VALUES MARKET VALUES
D1 B1 B1C1 10 A1 10
D1 B1 B1C1 20 A1 20
D2 B1 B1C2 15 A2 15
D2 B1 B1C2 25 A2 25
D2 B1 B1C2 10 A2 10
D3 B1 B1C3 5 A2 5
ALL D1+D2+D3 FAMILY A 85
The idea is, Table 2 has many rows and products but does not have Market. Table 1 helps you find out which product in Table 2 belongs to which Market-based on the Joining column. For example, There are 3 Markets present in Table 1, I want to then assign a new column Market in Table 2 such that all PRODUCTS in Table 2 with the ATC4 code of B1C1 belongs to the Market A1. Why? Because in Table 1, it says that Market A1 should follow the Joining Column of ATC4 - which corresponds to the code B1C1. In Table 1, we also provided a Product column, this is just for our purpose of identifying our own companies product name. Now if you see that for Table 1, there are two rows of Market A2, with different ATC4, this is very normal, because maybe Product D2 and D10 belong to Market A2, but both may contain different ATC4!
There is also one more nuance to it, we have Family A! This is merely a combination of A1+A2, but in my Table 2, there is no such row value that sums up to Family A. So I need to achieve two things:
I want to make a new column Market in Table 2 so that each product is mapped to the market.
I want to create extra rows to account for the Market Family A (A1+A2) and call the product Name "Lovely Family A" or something. The above table 3 provides an expected output.
Since I am new to SQL, I tried to first use CASE Statements, to map slowly one by one, but soon it gets tedious and I wonder if there's some tricks.
My CASE looks like this
,CASE WHEN ATC4 LIKE '%B1C1%' THEN 'A1'
WHEN ATC4 LIKE '%B1C2%' OR ATC4 LIKE '%B1C3%' THEN 'A2' ELSE ATC4 END AS MARKET_NAME
but have yet to figure out how to add the additional row where I can sum up A1+A2.
You seem to want something like this:
with rows as (
select PRODUCT, ATC3, ATC4, VALUES
from table2
union all
select 'ALL D1+D2+D3', ATC3, NULL, SUM(VALUES)
from table2
group by ATC3
)
select r.*, t1.market
from rows r join
table1 t1
on (t1.joining_column = 'ATC3' and t1.atc3 = r.atc3) or
(t1.joining_column = 'ATC4' and t1.atc4 = r.atc4);
I see no reason to repeat the values column. And values is a really bad name for a column because it is a SQL keyword.

mysql query to show results above an average that uses two tables in its sum

I want to have a query that displays only contract costs above average along with the contract id and I create my average with this:
SELECT AVG(average_cost)
FROM (
SELECT SUM(contracts.hours*staff.rate) AS average_cost
FROM contracts
INNER JOIN staff ON contracts.staff_id = staff.staff_id
GROUP BY contracts.contract_id
) AS inner;
I would like the results to show contract_id as well as group by it, even if it all it shows is the contract_id for the results above the average. Just having a hard time getting a query to show results above average with this average that is more complicated than I'm use to. Help appreciated, thanks.
edit* with some sample data, the tables are more complex than this but I hope this helps with some understanding
contracts
contract_id hours staff_id
55 30 10
45 25 11
43 30 12
41 12 11
67 20 12
49 20 13
staff
staff_id rate
10 50
11 45
12 80
13 45
So the average contract cost of above is 1,344.167 or there abouts. For this example I would want the results to show the contract_id for the contracts that cost above the average.
results
contract_id
55
43
67
Here is one method:
SELECT c.contract_id, SUM(c.hours*s.rate) AS cost
FROM contracts c INNER JOIN
staff s
ON c.staff_id = s.staff_id
GROUP BY c.contract_id
HAVING cost > (SELECT AVG(average_cost)
FROM (SELECT SUM(c.hours*s.rate) AS average_cost
FROM contracts c INNER JOIN
staff s
ON c.staff_id = s.staff_id
GROUP BY c.contract_id
) sc
);
As much I understand with your question, I think you are looking for this:
select contract_id
from contracts join staff on (contracts.staff_id = staff.staff_id)
where (hours*rate) >
(select avg(hours*rate)
from contracts join staff on (contracts.staff_id = staff.staff_id)
)
I have my example that can help you:
SELECT first_name, last_name, invoice_amount
FROM students
INNER JOIN invoices
ON students.id_student = invoices.id_student
WHERE invoice_amount > (select avg(invoice_amount) from invoices)

SSRS top n rows in every value of a Group

I am new to SSRS. I have a requirement to display top 3 products based on sales for each country where country is a column grouping, in a tablix.So the report should look like this ( the countries should appear as columns with products and total sales underneath.Unfortunately I am unable to type the below as expected)
UK
Prod1 100
Prod3 70
Prod4 50
Spain
Prod2 80
Prod3 55
Prod4 30
Italy
Prod3 45
Prod1 20
Prod4 15
Top N filter applied at the column group does not work as it seems to be ignoring it completely. Top N cannot be applied at the Table level as it will only display Top 3 across all countries. The data is sourced from a cube.
Any help is much appreciated
This is something you should solve in your select query and that shouldn't be too hard.
I don't know your database layout so I'll try and explain it with a simple example:
Sales Table
SalesId
CustomerId
Date
Total
SalesDetails table
SalesDetailId
SalesId
Product
Quantity
CustomersTable
CustomerId
Country
Name
SELECT DISTINCT(c.Country), q.Product, q.Quantity
FROM Customers c
JOIN (SELECT c1.Country, d1.Product, SUM(d1.Quantity) as Quantity
FROM SalesDetails d1
JOIN Sales s1 ON s1.SalesId = d1.SalesId
JOIN Customer c1 ON c1.CustomerId = s1.CustomerId
GROUP BY d1.Product, c1.Country) q ON q.Country = c.Country
WHERE q.Product IN (SELECT TOP 3 d2.Product
FROM SalesDetails d2
JOIN Sales s2 ON s2.SalesId = d2.SalesId
JOIN Customer c2 ON c2.CustomerId = s2.CustomerId
WHERE c2.Country = c.Contry
GROUP BY d2.Product, c2.Country
ORDER BY SUM(d2.Quantity) DESC)
ORDER BY c.Country
I hope this is of use to you, if you share your actual database layout I'm willing to update my example based on yours.