Retrieve only the lowest Row from the duplicates [duplicate] - sql

This question already has answers here:
How to select only the first rows for each unique value of a column?
(7 answers)
Closed 2 years ago.
Here I have the table which contains data like this:
Location No Days
----------------------
Callao 1 7
Callao 2 7
CHENNAI 3 6
SINGAPORE 4 30
SINGAPORE 5 7
SINGAPORE 6 7
LOS ANGELES 7 9
HONG KONG 7 11
HONG KONG 7 6
LOS ANGELES 8 6
HONG KONG 9 6
HONG KONG 9 4
LOS ANGELES 9 10
LOS ANGELES 9 9
LOS ANGELES 10 6
Here now I only want the row which has number with lowest days:
I want it like this,
Location No Days
---------------------
Callao 1 7
Callao 2 7
CHENNAI 3 6
SINGAPORE 4 30
SINGAPORE 5 7
SINGAPORE 6 7
HONG KONG 7 6
LOS ANGELES 8 6
HONG KONG 9 4
LOS ANGELES 10 6
I only want to remove duplication No. based on the highest value, I already tried many on my own but nothing is working.
Help me solve this, thanks in advance.

WITH
-- your input, thanks for pasting it in ..
indata(location,no,days) AS (
SELECT 'Callao',1,7
UNION ALL SELECT 'Callao',2,7
UNION ALL SELECT 'CHENNAI',3,6
UNION ALL SELECT 'SINGAPORE',4,30
UNION ALL SELECT 'SINGAPORE',5,7
UNION ALL SELECT 'SINGAPORE',6,7
UNION ALL SELECT 'LOS ANGELES',7,9
UNION ALL SELECT 'HONG KONG',7,11
UNION ALL SELECT 'HONG KONG',7,6
UNION ALL SELECT 'LOS ANGELES',8,6
UNION ALL SELECT 'HONG KONG',9,6
UNION ALL SELECT 'HONG KONG',9,4
UNION ALL SELECT 'LOS ANGELES',9,10
UNION ALL SELECT 'LOS ANGELES',9,9
UNION ALL SELECT 'LOS ANGELES',10,6
)
-- real query starts here, replace "," with "WITH" ..
,
w_filter AS (
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY no ORDER BY days) AS fil
FROM indata
)
SELECT
location
, no
, days
FROM w_filter
WHERE fil=1
ORDER BY 2 ;
-- out location | no | days
-- out -------------+----+------
-- out Callao | 1 | 7
-- out Callao | 2 | 7
-- out CHENNAI | 3 | 6
-- out SINGAPORE | 4 | 30
-- out SINGAPORE | 5 | 7
-- out SINGAPORE | 6 | 7
-- out HONG KONG | 7 | 6
-- out LOS ANGELES | 8 | 6
-- out HONG KONG | 9 | 4
-- out LOS ANGELES | 10 | 6

Related

How retrieve all parent and child rows population in Oracle sql?

I have a table "TB_Population" with some records about the population from all over the world.
at this time I want to calculate each title's population in particular row
and demonstrate each level in that table.
I have this table with the following data:
ID TITLE PARENT_ID POPULATION
1 WORLD 10
2 AFRICA 1 5
3 ASIA 1 10
4 EUROPE 1 4
5 GERMANY 4 6
6 FRANCE 4 10
7 ITALY 4 4
8 JAPAN 3 6
9 MORROCO 2 1
10 SPAIN 4 9
11 INDIA 3 8
12 PORTUGAL 4 2
13 USA 14 10
14 AMERICA 1 10
15 NEWYORK 13 5
The expected output table should be as below
ID TITLE POPULATION LEVEL
1 WORLD 100 1
2 AFRICA 6 2
3 ASIA 24 2
4 EUROPE 35 2
5 GERMANY 6 3
6 FRANCE 10 3
7 ITALY 4 3
8 JAPAN 6 3
9 MORROCO 1 3
10 SPAIN 9 3
11 INDIA 8 3
12 PORTUGAL 2 3
13 USA 15 3
14 AMERICA 25 2
15 NEWYORK 5 4
Thanks and best regards
The tricky part which I see here is you want the LEVEL of title from "BOTTOM TO TOP" and POPULATION from "TOP TO BOTTOM". For example, AMERICA's level has to be 2 which means the LEVEL has to be measured from AMERICA -> WORLD, but AMERICA's population has to be 25 which is the sum of population measured from AMERICA -> NEWYORK. So, I tried this:
SELECT TOP_TO_BOTTOM.TITLE_ALIAS, TOP_TO_BOTTOM.TOTAL_POPULATION, BOTTOM_TO_TOP.MAX_LEVEL FROM
(SELECT TITLE_ALIAS, SUM(POPULATION) AS "TOTAL_POPULATION" FROM
(SELECT CONNECT_BY_ROOT TITLE AS "TITLE_ALIAS", POPULATION
FROM TB_POPULATION
CONNECT BY PRIOR ID = PARENT_ID)
GROUP BY TITLE_ALIAS) "TOP_TO_BOTTOM"
INNER JOIN
(SELECT TITLE_ALIAS, MAX(LEV) AS "MAX_LEVEL" FROM
(SELECT CONNECT_BY_ROOT TITLE AS "TITLE_ALIAS", LEVEL AS "LEV"
FROM TB_POPULATION
CONNECT BY PRIOR PARENT_ID = ID)
GROUP BY TITLE_ALIAS) "BOTTOM_TO_TOP"
ON
BOTTOM_TO_TOP.TITLE_ALIAS = TOP_TO_BOTTOM.TITLE_ALIAS
ORDER BY BOTTOM_TO_TOP.MAX_LEVEL;
You can have a look at the simulation here: https://rextester.com/HFTIH47397.
Hope this helps you

Oracle SQL query to get all agents reporting directy or indirectly to a manager

I have a employee hierarchy table in the below format
EMPLOYEE_NO TEAM_ROLE MANAGER_EMP_NO
10001 Functional_Manager
123 Center_Manager 10001
10 Team_Manager 123
11 Team_Manager 123
12 Team_Manager 123
1 Agent 10
2 Agent 10
5 Agent 11
6 Agent 11
7 Agent 11
8 Agent 12
456 Center_Manager 10001
15 Team_Manager 456
9 Agent 15
I want all agents reporting directly or indirectly to a manager (Team_Manager or Center_Manager or Functional_Manager)
I want to generate the below result with the above data:-
EMPLOYEE_NO TEAM_ROLE AGENT_EMP_NO
10001 Functional_Manager 1
10001 Functional_Manager 2
10001 Functional_Manager 5
10001 Functional_Manager 6
10001 Functional_Manager 7
10001 Functional_Manager 8
10001 Functional_Manager 9
123 Center_Manager 1
123 Center_Manager 2
123 Center_Manager 5
123 Center_Manager 6
123 Center_Manager 7
123 Center_Manager 8
10 Team_Manager 1
10 Team_Manager 2
11 Team_Manager 5
11 Team_Manager 6
11 Team_Manager 7
12 Team_Manager 8
1 Agent 1
2 Agent 2
5 Agent 5
6 Agent 6
7 Agent 7
8 Agent 8
9 Agent 9
I guess this can be done with the help of connect by queries, but I am not quite sure exactly how to do it. Any help is much appreciated.
Please find query below
with your_data as (
select 10001 employee_no,'Functional_Manager' team_role, null manager_emp_no from dual union
select 123,'Center_Manager',10001 from dual union
select 10,'Team_Manager',123 from dual union
select 11,'Team_Manager',123 from dual union
select 12,'Team_Manager',123 from dual union
select 1,'Agent',10 from dual union
select 2,'Agent',10 from dual union
select 5,'Agent',11 from dual union
select 6,'Agent',11 from dual union
select 7,'Agent',11 from dual union
select 8,'Agent',12 from dual union
select 456,'Center_Manager',10001 from dual union
select 15,'Team_Manager',456 from dual union
select 9,'Agent',15 from dual),
t1 as (SELECT manager_emp_no, team_role, employee_no as agent_emp_no, sys_connect_by_path( manager_emp_no, ',' ) path
FROM your_data
start with manager_emp_no is null
CONNECT BY PRIOR employee_no = manager_emp_no),
t2 as (select
t1.manager_emp_no,
t1.team_role,
t1.agent_emp_no,
regexp_substr(t1.path, '[^,]+', 1, commas.column_value) as connect_by
from
t1,
table(cast(multiset(select level from dual connect by level <= length (regexp_replace(t1.path, '[^,]+')) + 1) as sys.OdciNumberList)) commas)
SELECT distinct connect_by_root agent_emp_no as manager_emp_no ,connect_by_root team_role as team_role,agent_emp_no
FROM t2
where agent_emp_no in (select employee_no from your_data where team_role = 'Agent')
CONNECT BY PRIOR agent_emp_no = connect_by
order by manager_emp_no desc nulls first, agent_emp_no asc
Output
"MANAGER_EMP_NO" "TEAM_ROLE" "AGENT_EMP_NO"
10001 "Functional_Manager" 1
10001 "Functional_Manager" 2
10001 "Functional_Manager" 5
10001 "Functional_Manager" 6
10001 "Functional_Manager" 7
10001 "Functional_Manager" 8
10001 "Functional_Manager" 9
456 "Center_Manager" 9
123 "Center_Manager" 1
123 "Center_Manager" 2
123 "Center_Manager" 5
123 "Center_Manager" 6
123 "Center_Manager" 7
123 "Center_Manager" 8
15 "Team_Manager" 9
12 "Team_Manager" 8
11 "Team_Manager" 5
11 "Team_Manager" 6
11 "Team_Manager" 7
10 "Team_Manager" 1
10 "Team_Manager" 2
9 "Agent" 9
8 "Agent" 8
7 "Agent" 7
6 "Agent" 6
5 "Agent" 5
2 "Agent" 2
1 "Agent" 1

SQL : How to find number of occurrences without using HAVING or COUNT?

This is a trivial example, but I am trying to understand how to think creatively using SQL.
For example, I have the following tables below, and I want to query the names of folks who have three or more questions. How can I do this without using HAVING or COUNT? I wonder if this is possible using JOINS or something similar?
FOLKS
folkID name
---------- --------------
01 Bill
02 Joe
03 Amy
04 Mike
05 Chris
06 Elizabeth
07 James
08 Ashley
QUESTION
folkID questionRating questionDate
---------- ---------- ----------
01 2 2011-01-22
01 4 2011-01-27
02 4
03 2 2011-01-20
03 4 2011-01-12
03 2 2011-01-30
04 3 2011-01-09
05 3 2011-01-27
05 2 2011-01-22
05 4
06 3 2011-01-15
06 5 2011-01-19
07 5 2011-01-20
08 3 2011-01-02
Using SUM or CASE seems to be cheating to me!
I'm not sure if it's possible in your current formulation, but if you add a primary key to the question table (questionid) then the following seems to work:
SELECT DISTINCT Folks.folkid, Folks.name
FROM ((Folks
INNER JOIN Question AS Question_1 ON Folks.folkid = Question_1.folkid)
INNER JOIN Question AS Question_2 ON Folks.folkid = Question_2.folkid)
INNER JOIN Question AS Question_3 ON Folks.folkid = Question_3.folkid
WHERE (((Question_1.questionid) <> [Question_2].[questionid] And
(Question_1.questionid) <> [Question_3].[questionid]) AND
(Question_2.questionid) <> [Question_3].[questionid]);
Sorry, this is in MS Access SQL, but it should translate to any flavour of SQL.
Returns:
folkid name
3 Amy
5 Chris
Update: Just to explain why this works. Each join will return all the question ids asked by that person. The where clauses then leaves only unique rows of question ids. If there are less than three questions asked then there will be no unique rows.
For example, Bill:
folkid name Question_3.questionid Question_1.questionid Question_2.questionid
1 Bill 1 1 1
1 Bill 1 1 2
1 Bill 1 2 1
1 Bill 1 2 2
1 Bill 2 1 1
1 Bill 2 1 2
1 Bill 2 2 1
1 Bill 2 2 2
There are no rows where all the ids are different.
however for Amy:
folkid name Question_3.questionid Question_1.questionid Question_2.questionid
3 Amy 4 4 5
3 Amy 4 4 4
3 Amy 4 4 6
3 Amy 4 5 4
3 Amy 4 5 5
3 Amy 4 5 6
3 Amy 4 6 4
3 Amy 4 6 5
3 Amy 4 6 6
3 Amy 5 4 4
3 Amy 5 4 5
3 Amy 5 4 6
3 Amy 5 5 4
3 Amy 5 5 5
3 Amy 5 5 6
3 Amy 5 6 4
3 Amy 5 6 5
3 Amy 5 6 6
3 Amy 6 4 4
3 Amy 6 4 5
3 Amy 6 4 6
3 Amy 6 5 4
3 Amy 6 5 5
3 Amy 6 5 6
3 Amy 6 6 4
3 Amy 6 6 5
3 Amy 6 6 6
There are several rows which have different ids and hence these get returned by the above query.
you can try sum , to replace count.
SELECT SUM(CASE WHEN Field_name >=3 THEN field_name ELSE 0 END)
FROM tabel_name
SELECT f.*
FROM (
SELECT DISTINCT
COUNT(*) OVER (PARTITION BY folkID) AS [Count] --count questions for folks
,a.folkID
FROM QUESTION AS q
) AS p
INNER JOIN FOLKS as f ON f.folkID = q.folkID
WHERE p.[Count] > 3

eliminate duplicates SQL?

How do I eliminate the duplicates?
It must be 10 rows, show every student ID (1x).
Ive tried this 2 diff ways. (see below)
( the common field is class_id)
SQL> select distinct(student_id),event.class_id, event.event_id, event.event_name
2 from student_class
3 inner join event on event.class_id = student_class.class_id
4 where student_class.class_id = '10'
5 order by student_id desc;
STUDENT_ID CLASS_ID EVENT_ID EVENT_NAME
---------- ---------- ---------- --------------------------------------------------
20 10 5 PICKUP SOCCER GAME
20 10 2 Flag FOOtball Game
18 10 2 Flag FOOtball Game
18 10 5 PICKUP SOCCER GAME
16 10 2 Flag FOOtball Game
16 10 5 PICKUP SOCCER GAME
12 10 5 PICKUP SOCCER GAME
12 10 2 Flag FOOtball Game
9 10 5 PICKUP SOCCER GAME
9 10 2 Flag FOOtball Game
8 10 2 Flag FOOtball Game
8 10 5 PICKUP SOCCER GAME
6 10 2 Flag FOOtball Game
6 10 5 PICKUP SOCCER GAME
4 10 5 PICKUP SOCCER GAME
4 10 2 Flag FOOtball Game
3 10 2 Flag FOOtball Game
3 10 5 PICKUP SOCCER GAME
2 10 5 PICKUP SOCCER GAME
2 10 2 Flag FOOtball Game
SQL> select distinct(student_id), student_class.class_id,
event.event_id, event.event_date_time
from student_class
inner join event on event.class_id = student_class.class_id
order by student_id desc;
STUDENT_ID CLASS_ID EVENT_ID EVENT_DAT
---------- ---------- ---------- ---------
22 9 1 09-NOV-99
21 2 6 20-NOV-99
21 9 1 09-NOV-99
20 9 1 09-NOV-99
20 10 2 19-JAN-99
20 10 5 09-JUL-99
18 9 1 09-NOV-99
18 10 2 19-JAN-99
18 10 5 09-JUL-99
17 9 1 09-NOV-99
16 2 6 20-NOV-99
16 9 1 09-NOV-99
16 10 2 19-JAN-99
16 10 5 09-JUL-99
15 9 1 09-NOV-99
14 9 1 09-NOV-99
13 9 1 09-NOV-99
12 2 6 20-NOV-99
12 9 1 09-NOV-99
12 10 2 19-JAN-99
12 10 5 09-JUL-99
11 2 6 20-NOV-99
11 9 1 09-NOV-99
9 10 2 19-JAN-99
9 10 5 09-JUL-99
8 2 6 20-NOV-99
8 10 2 19-JAN-99
8 10 5 09-JUL-99
7 2 6 20-NOV-99
6 10 2 19-JAN-99
6 10 5 09-JUL-99
4 10 2 19-JAN-99
4 10 5 09-JUL-99
3 10 2 19-JAN-99
3 10 5 09-JUL-99
2 2 6 20-NOV-99
2 9 1 09-NOV-99
2 10 2 19-JAN-99
2 10 5 09-JUL-99
you can do this by following methods
create table table_name2 as select distinct * from table_name1;
drop table table_name1;
rename table_name2 to table_name1;
or by this query
DELETE FROM table_name A WHERE ROWID > (SELECT min(rowid) FROM table_name B WHERE A.key_values = B.key_values);

Retrieve top 48 unique records from database based on a sorted Field

I have database table that I am after some SQL for (Which is defeating me so far!)
Imagine there are 192 Athletic Clubs who all take part in 12 Track Meets per season.
So that is 2304 individual performances per season (for example in the 100Metres)
I would like to find the top 48 (unique) individual performances from the table, these 48 athletes are then going to take part in the end of season World Championships.
So imagine the 2 fastest times are both set by "John Smith", but he can only be entered once in the world champs. So i would then look for the next fastest time not set by "John Smith"... so on and so until I have 48 unique athletes..
hope that makes sense.
thanks in advance if anyone can help
PS
I did have a nice screen shot created that would explain it much better. but as a newish user i cannot post images.
I'll try a copy and paste version instead...
ID AthleteName AthleteID Time
1 Josh Lewis 3 11.99
2 Joe Dundee 4 11.31
3 Mark Danes 5 13.44
4 Josh Lewis 3 13.12
5 John Smith 1 11.12
6 John Smith 1 12.18
7 John Smith 1 11.22
8 Adam Bennett 6 11.33
9 Ronny Bower 7 12.88
10 John Smith 1 13.49
11 Adam Bennett 6 12.55
12 Mark Danes 5 12.12
13 Carl Tompkins 2 13.11
14 Joe Dundee 4 11.28
15 Ronny Bower 7 12.14
16 Carl Tompkin 2 11.88
17 Nigel Downs 8 14.14
18 Nigel Downs 8 12.19
Top 4 unique individual performances
1 John Smith 1 11.12
3 Joe Dundee 4 11.28
5 Adam Bennett 6 11.33
6 Carl Tompkins 2 11.88
Basically something like this:
select top 48 *
from (
select athleteId,min(time) as bestTime
from theRaces
where raceId = '123' -- e.g., 123=100 meters
group by athleteId
) x
order by bestTime
try this --
select x.ID, x.AthleteName , x.AthleteID , x.Time
(
select rownum tr_count,v.AthleteID AthleteID, v.AthleteName AthleteName, v.Time Time,v.id id
from
(
select
tr1.AthleteName AthleteName, tr1.Time time,min(tr1.id) id, tr1.AthleteID AthleteID
from theRaces tr1
where time =
(select min(time) from theRaces tr2 where tr2.athleteId = tr1.athleteId)
group by tr1.AthleteName, tr1.AthleteID, tr1.Time
having tr1.Time = ( select min(tr2.time) from theRaces tr2 where tr1.AthleteID =tr2.AthleteID)
order by tr1.time
) v
) x
where x.tr_count < 48