selecting a row using MIN or ROWNUM - sql

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

Related

I'd like some help to write sql code to return a list of customer data items ranked by frequency (high to low)

The table I am querying has several thousand rows and numerous fields - I'd like the code to return the top 10 values for a handful of the fields, namely: Forename, Surname and City - I'd also like to see a count of the values returned.
For example
Ranking
Forename
FName Frequency
Surname
SName Frequency
City
City Frequency
1
Liam
830,091
Smith
2,353,709
New York
2,679,785
2
Mary
708,390
Johnson
1,562,990
Los Angeles
413,359
3
Noah
639,592
Williams
792,306
Chicago
393,511
4
Patricia
568,410
Brown
743,346
Houston
367,496
5
William
557,049
Jones
633,933
Phoenix
336,929
6
Linda
497,138
Miller
503,523
Philadelphia
304,638
7
James
490,665
Davis
503,115
San Antonio
255,142
8
Barbara
418,312
Garcia
468,683
San Diego
238,521
9
Logan
399,947
Rodriguez
461,816
Dallas
232,718
10
Elizabeth
399,737
Wilson
436,843
San Jose
213,483
The returned list should be interpreted thus:
The most frequently occurring forename in the table is Liam - with 830,091 instances,
The 5th most frequently occurring forename is William - with 557,049 instances,
The 8th most frequently occurring city is San Diego - with 238,521 instances
...and so on
(N.b. the table does not show there are 2.7m Liams in New York - just that there are 830,091 Liams in the entire table - and that there are 2,679,785 New York addresses in the entire table)
The following produces what I need - but just for the first field (Forename) - I'd like to be able to do the same for three fields
SELECT Forename, COUNT(Forename) AS FName_Frequency
FROM Customer_Table
GROUP BY Forename
ORDER BY FName_Frequency DESC
limit 10
Thanks in anticipation
I would just put this in separate rows:
select 'forename', forename, count(*) as freq
from customer_table
group by forename
order by freq desc
fetch first 10 rows only
union all
select 'surname', surname, count(*) as freq
from customer_table
group by surname
order by freq desc
fetch first 10 rows only
union all
select 'city', city, count(*) as freq
from customer_table
group by city
order by freq desc
fetch first 10 rows only;
Note that this uses Standard SQL syntax, because you have not tagged with the question with the database you are using. You can also put this in separate columns, using:
select max(case when which = 'forename' then col end),
max(case when which = 'forename' then freq end),
max(case when which = 'surname' then col end),
max(case when which = 'surname' then freq end),
max(case when which = 'city' then col end),
max(case when which = 'city' then freq end)
from ((select 'forename' as which, forename as col, count(*) as freq,
row_number() over (order by count(*) desc) as seqnum
from customer_table
group by forename
) union all
(select 'surname' as which, surname, count(*) as freq
row_number() over (order by count(*) desc) as seqnum
from customer_table
group by surname
) union all
(select 'city', city, count(*) as freq,
row_number() over (order by count(*) desc) as seqnum
from customer_table
group by city
)
) x
group by seqnum;

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

Is it possible to SELECT N first rows from database using ROWNUM if some rows have the same value?

I'm trying to select N first rows from a database using ROWNUM. The problem is that if I want to select 6 first values of age with names of people with this age and some of the have the same value of age not every person will be shown.
For example
name age
Peter 15
Mark 22
Kelly 17
Mike 17
George 17
If I want to show people with 2 biggest values of age ROWNUM will show Mark and Kelly or Mike or George. Not all three of them. The expected result is:
Name age
Mark 22
Kelly 17
Mike 17
George 17
Is it possible to get this result using ROWNUM? Not RANK, DENSE_RANK, JOIN, correlated subquery but ROWNUM?
You can try something like this:
select *
from test
where age in (
select age
from (
select age
from test
group by age
order by age desc
)
where rownum <=2
)
The right solution dense_rank(), but you can do it with just row_number() and some subqueries:
select t.*
from t
where t.age in (select age
from (select age
from t t2
order by age desc
) x
where rownum <= 2
);
In Oracle 12+, you can simplify this:
select t.*
from t
where t.age in (select age
from t t2
order by age desc
fetch first 2 rows only
);

SQL Server select first instance of ranked data

I have a query that creates a result set like this:
Rank Name
1 Fred
1 John
2 Mary
2 Fred
2 Betty
3 John
4 Betty
4 Frank
I need to then select the lowest rank for each name, e.g.:
Rank Name
1 Fred
1 John
2 Mary
2 Betty
4 Frank
Can this be done within TSQL?
SELECT MIN(Rank) AS Rank, Name
FROM TableName
GROUP BY Name
yes
select name, min(rank)
from nameTable
group by name
As Paul + Kevin have pointed out, simple cases of returning a value from an aggregate can be extracted using MIN / MAX etc (just note that RANK is a reserved word)
In a more general / complicated case, e.g. where you need to find the second / Nth highest rank, you can use PARTITIONs with ROW_NUMBER() to do ranking and then filter by the rank.
SELECT [Rank], [Name]
FROM
(
SELECT [RANK], [Name],
ROW_NUMBER() OVER (PARTITION BY [Name] ORDER BY [Rank]) as [RowRank]
FROM [MyTable]
) AS [MyTableReRanked]
WHERE [RowRank] = #N
ORDER BY [Rank];

query to display the highest salaries of employees by age

Can you help me? Need to get a list of the highest salaries of employee with the sample by age,first name and last name
Input:
Age FirstName LastName SAL
---------- ---------- ---------- ----------
30 Andy Donald 175
31 Petr Pess 295
30 John Jacky 453
31 Bob Bobby 385
29 Eric Rice 957
Answer should be
Age FirstName LastName SAL
---------- ---------- ---------- ----------
31 Bob Bobby 385
30 John Jacky 453
29 Eric Rice 957
Thanks in advance
If you have window/analytic functions available (you don't mention an RDBMS in the OP), you can do the following:
SELECT * FROM (
SELECT Age, FirstName, LastName, SAL
, DENSE_RANK() OVER (PARTITION BY Age ORDER BY SAL DESC) AS ranknum
FROM employees
) WHERE ranknum = 1
This will work even when two or more employees of the same age have the same salary - both will be returned. It will also allow you to get the 2nd highest salary, etc., if you want (just change ranknum = 1 to ranknum = 2, etc.).
Edit: FYI, this will work in Oracle, SQL Server, and PostgreSQL at least.
Get all employees for which there is no employee with the same age and with a higher salary:
SELECT *
FROM employees e1
WHERE NOT EXISTS (
SELECT 1
FROM employees e2
WHERE e1.age = e2.age
AND e1.sal < e2.sal
)
ORDER BY age DESC
If two employees have the same age and salary, both will be returned... This query will work on any database
If you are using MySQL, the following should work:
select * from
(select * from myTable order by age desc, sal desc) sq
group by age
(Although it won't return multiple rows for employees of the same age on the same salary.)