SQL average Percentage by group - sql

I have table1 with some sample record (would have more than 100 records)
lastname courtesy
Lane 3
Lane 4
Lane 5
Lane 12
Santana 4
Santana 5
My query to get the ave percent group by lastname and did not group by lastname
you can see Lane and Santana still not group.
Please correct the query. Thank you.
SELECT
lastname,
courtesy,
percentage = AVG(courtesy) * 100 / SUM(AVG(courtesy)) OVER (PARTITION BY lastname)
FROM echo
GROUP BY
lastname, courtesy
the results
lastname courtesy percentage
Lane 3 12
Lane 4 16
Lane 5 20
Lane 12 50
Santana 4 44
Santana 5 55
the results look like this
lastname percentage
Lane 16 (4/24)
Satana 22 (2/9)
count (lastname) / sum (courtesy) group by lastname

I think this is the logic that you want:
SELECT lastname,
SUM(SUM(courtesy)) OVER (ORDER BY lastname),
COUNT(*) * 1.0 / SUM(courtesy)
FROM echo
GROUP BY lastname;

Related

Calculate every participant's points, based on winners result in age group

The winner of the competition stage in the respective age group is the participant who made the distance fastest. Each participant in the respective group receives for each stage points. The points obtained are calculated by dividing the time of the group winner by the time of the participant and multiplied by 1000. The total score is calculated by the sum of the points of the 5 best stages.
I have participant data like this.
AgeGroup Start_Nr First_name Last_Name Distance_result
-------------------------------------------------------------
M30 5 John Lala 180
M35 1 Paul Baba 175
M35 6 Patric Ziza 192
M30 3 Peter Mikel 190
S30 2 Sandra Lilua 250
S30 4 Julia Parker 260
And I want to calculate and display point like this
Age_Group|Start_Nr|First_name|Last_Name|Distance_result|Points
----------------------------------------------------------
M30 5 John Lala 180 1000
M30 3 Peter Mikel 190 947
M35 6 Patric Ziza 175 1000
M35 1 Paul Babas 185 946
S30 2 Sandra Lilua 250 1000
S30 4 Julia Parker 260 962
Each winner in age group gets 1000points, others in that age group get points calculating -> (MIN(Distance_result)/(Distance_result) * 1000)
SELECT [Age_group],
[Start_number] ,
[First_name],
[Last_name],
[Stage_Nr],
[Distance_result], (180/[Distance_result]*1000) AS Points,
DENSE_RANK() OVER (PARTITION BY [Age_group] ORDER BY [Distance_result] ASC) AS PlaceRank
FROM [ParticipantDetails].[dbo].[ParticipantForm]
How can I use MIN(Distance_result) in each age group to do point calculations?
In my solution I can calculate points only inserting MIN(Distance_result) manually, but even then it's not correct to other age groups. In every age group there is a different best result.
In my Example I have solved your problem with a subquery.
I have also added round to remove the decimals and before that I have added cast to get some result, otherwise I would receive only 1 or 0, or you can do this: p1.[Distance_result] instead of cast...
SELECT [AgeGroup],
[Start_Nr] ,
[First_name],
[Last_name],
[Distance_result],
(select round(min(p2.Distance_result)/p1.[Distance_result] * 1000, 0) AS Points
from ParticipantForm p2
where p2.AgeGroup = p1.AgeGroup
group by p2.AgeGroup) as Points,
DENSE_RANK() OVER (PARTITION BY [AgeGroup] ORDER BY [Distance_result] ASC) AS PlaceRank
FROM [ParticipantForm] p1
Here you can see the demo.
You can use a sub-query to get the best times and join the table to it.
From a performance point of view it is preferable to have a sub-query in the join which is run only once than a row-level sub-query which is run for every line.
We avoid the need to cast as float and use round() by doing the multiplication by 1000 before the division.
SELECT
[Age_group],
[Start_number] ,
[First_name],
[Last_name],
[Stage_Nr],
[Distance_result],
(b.best*1000)/[Distance_result AS Points,
DENSE_RANK() OVER (PARTITION BY [Age_group] ORDER BY [Distance_result] ASC) AS PlaceRank
FROM [ParticipantDetails].[dbo].[ParticipantForm]
JOIN ( SELECT [Age_group] AgeGroup,
MIN([Distance_result]) AS best
FROM [ParticipantDetails].[dbo].[ParticipantForm]
GROUP BY [Age_group]) AS b
ON b.[AgeGroup] = [ParticipantForm].[Age_group];
Try calculating the minimum Distance_Result partitioned by Age_Group. Then calculate the points.
WITH cte AS (
SELECT *
, DENSE_RANK() OVER (PARTITION BY [Age_Group] ORDER BY [Distance_Result] ASC) AS Place_Rank
, MIN(Distance_Result) OVER (PARTITION BY [Age_Group] ORDER BY [Distance_Result] ASC) AS Min_Distance
FROM [ParticipantForm]
)
SELECT [Age_group]
, [First_Name]
, [Last_Name]
, [Start_Number]
, [Distance_Result]
, CAST(ROUND( [Min_Distance] * 1000.0 / [Distance_Result], 0 ) AS INT) AS Points
FROM cte
Results:
Age_group
First_Name
Last_Name
Start_Number
Distance_Result
Points
M30
John
Lala
5
180
1000
M30
Peter
Mikel
3
190
947
M35
Paul
Baba
1
175
1000
M35
Patric
Ziza
6
192
911
S30
Sandra
Lilua
2
250
1000
S30
Julia
Parker
4
260
962
db<>fiddle here

Count occurrences with exclude criteria

I have a Table
City ID
Austin 123
Austin 123
Austin 123
Austin 145
Austin 145
Chicago 12
Chicago 12
Houston 24
Houston 45
Houston 45
Now I want to count the occurrences of all Citis with different ids so since Chicago has only one id (=12) I am not interested in Chicago and it should not appear in the resultset that should looks like this:
city Id Occurrences
Austin 123 3
Austin 145 2
Houston 34 1
Houston 45 2
I am able to get myself an overview with
select city, Id from Table
group by city, Id
But I am not sure how to only select the once having different ids and to count them.
Could anyone help me out here?
You can use window functions and aggregation:
select city, id, occurences
from (
select city, id, count(*) occurences, count(*) over(partition by city) cnt_city
from mytable
group by city, id
) t
where cnt_city > 1

SQL query to get only rows match the condition based on two separated columns under one 'group by'

The simple SELECT query would return the data as below:
Select ID, User, Country, TimeLogged from Data
ID User Country TimeLogged
1 Samantha SCO 10
1 John UK 5
1 Andrew NZL 15
2 John UK 20
3 Mark UK 10
3 Mark UK 20
3 Steven UK 10
3 Andrew NZL 15
3 Sharon IRL 5
4 Andrew NZL 25
4 Michael AUS 5
5 Jessica USA 30
I would like to return a sum of time logged for each user grouped by ID
But for only ID numbers where both of these values Country = UK and User = Andrew are included within their rows.
So the output in the above example would be
ID User Country TimeLogged
1 John UK 5
1 Andrew NZL 15
3 Mark UK 30
3 Steven UK 10
3 Andrew NZL 15
First you need to identify which IDs you're going to be returning
SELECT ID FROM MyTable WHERE Country='UK'
INTERSECT
SELECT ID FROM MyTable WHERE [User]='Andrew';
and based on that, you can then filter to aggregate the expected rows.
SELECT ID,
[User],
Country,
SUM(Timelogged) as Timelogged
FROM mytable
WHERE (Country='UK' OR [User]='Andrew')
AND ID IN( SELECT ID FROM MyTable WHERE Country='UK'
INTERSECT
SELECT ID FROM MyTable WHERE [User]='Andrew')
GROUP BY ID, [User], country;
So, you have described what you need to write almost perfectly but not quite. Your result table indicates that you want Country = UK OR User = Andrew, rather than AND
You need to select and group by, then include a WHERE:-
Select ID, User, Country, SUM(Timelogged) as Timelogged from mytable
WHERE Country='UK' OR User='Andrew'
Group by ID, user, country

Order pairs/triplets of rows given the sum of a column

I'd like to order pairs (or group of 3,4 etc.) of rows given the SUM of a certain value.
The rows are consecutive based on the concatenation of Name+Surname+Age
To better understand given the following table:
ID Name Surname Age Salary
------------------------------
1 John Smith 30 2
2 John Smith 30 10
3 Rick James 22 300
4 Rick James 22 1000
5 Rick James 22 5
6 Mike Brown 50 200
7 Mike Brown 50 20
I'd like to have a final table that should be ordered DESC by the sum of Salary of each Name+Surname+Age and keeping the rows with same Name+Surname+Age next to each others despite the ID column is different. This would be the expected result:
ID Name Surname Age Salary
------------------------------
3 Rick James 22 300
4 Rick James 22 1000
5 Rick James 22 5
6 Mike Brown 50 200
7 Mike Brown 50 20
1 John Smith 30 2
2 John Smith 30 10
As you can see the rows with Name+Surname+Age = "Rick Jams 22" are on the top since their total sum would be 1305, followed by "Mike Brown 50" (sum = 220) and "John Smith 30" (sum = 12).
Additionally, the number of rows has to be the same in the resulting table.
How can I do that using Oracle SQL?
Thanks for any help
SELECT t.*,
COALESCE(SUM(salary) OVER (PARTITION BY name, surname, age), 0) ss
FROM mytable t
ORDER BY
ss DESC
Try this:
SELECT ID, Name, Surname, Age, Salary
FROM (
SELECT ID, Name, Surname, Age, Salary,
SUM(Salary) OVER (PARTITION BY Name, Surname, Age) AS sum_of_sal
FROM mytable) t
ORDER BY sum_of_sal DESC, ID
The query uses the window version of SUM in order to calculate the sum of salaries per Name, Surname, Age partition. We can use this field in an outer query to do the sorting.
or try this
SELECT ID, Name, Surname, Age, Salary
FROM mytable
ORDER BY SUM(Salary) OVER (PARTITION BY Name, Surname, Age) DESC, ID

selecting a row using MIN or ROWNUM

I have a oracle table which is similar to the one below which stores people's lastname firstname and age. If last name is same people belong to same family.
LastName FirstName Age
===========================
1 miller charls 20
2 miller john 30
3 anderson peter 45
4 Bates andy 50
5 anderson gary 60
6 williams mark 15
I need to write a oracle sql query to
select youngest person from each family. output shd select rows 1,3,4 and 6
How do I do this ?
Another way, a bit shorter:
select lastname
, max(firstname) keep(dense_rank first order by age) as first_name
, max(age) keep(dense_rank first order by age) as age
from you_table_name
group by lastname
order by lastname
Result:
LASTNAME FIRST_NAME AGE
-------- ---------- ----------
Bates andy 50
anderson peter 45
miller charls 20
williams mark 15
And SQLFiddle Demo
DENSE_RANK() is a ranking function which generates sequential number and for ties the number generated is the same. I prefer to use DENSE_RANK() here considering that a family can have twins, etc.
SELECT Lastname, FirstName, Age
FROM
(
SELECT Lastname, FirstName, Age,
DENSE_RANK() OVER (PARTITION BY LastName ORDER BY Age) rn
FROM tableName
) a
WHERE a.rn = 1
SQLFiddle Demo
With Standard SQL I would do as this...
select *
from family f1
where (
select count(*)
from family f2
where
f2.lastname = f1.lastname
and
f2.age <= f1.age) <= 1
order by lastname;
This SQL gives you possibilities to pick x youngest/oldest in a family. Just modify the f2.age <= f1.age to e.g. f2.age >= f1.age, and the <= 1 to e.g. <=10 (to get top 10 youngest/oldest in a family).
SQLfiddle