Oracle SQL: show max when record in table exists - sql

If record exists in the table i need to bring the data from the highest address record linked to the person.
Example:
John Doe have no address at all. Report need to still bring John Doe name but nothing as an address.
John Doe have 3 addresses with address number increasing. Report need to bring John Doe name and only the address with the highest address number.
Code I tried:
Select *
from person p left join address a on p.id = a.person and a.addressnumber = (select max(a2.addressnumber) from address a2 where a2.a_peron = p.id)
Oracle returns error: ORA-01799: a column may not be outer-joined to a
subquery
01799. 00000 - "a column may not be outer-joined to a subquery"
I also tried
Select *
from person p left join address a1 on p.id = a1.person
inner join (select a.person, max(a.addressnumber) MaxAdd, a.postcode, a.country from address a group by a.person, a.postcode, a.country) main on main.person = p.id and main.MaxAdd = a1.addressnumber
This doesnt work neither due to the grouping.
I can probably get this done by using subqueries in the select itself together with the case statement but i would like to avoid that because I will be pulling a lot of data from the address so this would mean case statement with subquery for every single column.
Oracle 11g - 11.2
Any idea? :D

You can use ROW_NUMBER to rank your rows and only keep the last one:
select *
from person p
left join
(
select
ad.*,
row_number() over (partition by person order by addressnumber desc) as rn
from address ad
) a on a.person = p.id and a.rn = 1;

Please try this:
select * from
person p left join
(select a.person, max(a.addressnumber) MaxAdd, a.postcode, a.country from address a
group by a.person, a.postcode, a.country) A
on p.id=A.person

Use row_number():
Select *
from person p left join
(select a.*,
row_number() over (partition by a.person order by a.addressnumber desc) as seqnum
from address a
) a
on p.id = a.person and seqnum = 1;

Related

SQL Server: how to limit results based on query

I have a customer table, and a customer address table but I want to limit it by just one address. so even though a customer has two postal addresses I want to show only one for each customer. Forgive me if this is a silly question, I am new to programming
custid forename surname
---------------------------
1 Sam Supra
2 Kelly Kenwood
addid custid address addresstype
-------------------------------------------
1 1 Main street POSTAL
2 1 2nd Main street POSTAL
3 1 0712456254 Mobile
4 1 0526545686 LANDLINE
5 2 Second Street POSTAL
6 2 04756325654 Mobile
7 2 058654236545 LANDLINE
Query:
SELECT a.*
FROM dbo.customers a
LEFT OUTER JOIN dbo.addresses b ON a.custid = b.custid
WHERE b.addresstype = 'POSTAL'
You can use ROW_NUMBER():
SELECT c.*, a.*
FROM dbo.customers c LEFT JOIN
(SELECT a.*,
ROW_NUMBER() OVER (PARTITION BY a.custid ORDER BY a.addid DESC) as seqnum
FROM dbo.addresses a
WHERE a.addresstype = 'POSTAL'
) a
ON a.custid = c.custid AND seqnum = 1;
Notes:
Don't use arbitrary table aliases. Use abbreviations for the table names.
Presumably, you want to select the addresses, so I changed the SELECT clause.
This returns all customers, even those with no address. I am guessing that is your intention, although your query would return only customers with a postal address.
I assume you are interested in the last address entered:
SELECT
*
FROM
dbo.customers a
LEFT JOIN
dbo.addresses b on b.id =
(
SELECT
x.id
FROM
dbo.addresses x
WHERE
x.custid = a.custid
AND
x.addresstype = 'POSTAL'
ORDER BY
x.id DESC
)
SELECT c.*,
a.*
FROM dbo.customers c
LEFT JOIN (
SELECT a.*,
ROW_NUMBER() OVER (
PARTITION BY a.custid
ORDER BY a.addid DESC
) as seqnum
FROM dbo.addresses a
WHERE a.addresstype = 'POSTAL'
) a
ON a.custid = c.custid AND seqnum = 1;

How can I show the maximum score of each department with their names

I can select maximum the score of each department but I can't show the name of each person associated with the max score.
I tried to select the name and the maximum grade (with maximum function) but it doesn't work:
select max(stgrade)as highscore,StName,DepName --department
from TBL_DEPARTMANTS d
inner join TBL_LESSONS l on d.DepID=l.LessonID
inner join TBL_GRADES g on g.lessonid=l.LessonID
inner join TBL_STUDENT s on s.STID=g.stid
group by DepName,StName
order by DepName,highscore desc
You may try this...
select * from ( select rank() over (partition by DepName order by stgrade desc) as Slno, stgrade, stname, DepName
from TBL_DEPARTMANTS d
inner join TBL_LESSONS l on d.DepID=l.LessonID
inner join TBL_GRADES g on g.lessonid=l.LessonID
inner join TBL_STUDENT s on s.STID=g.stid ) as dep where dep.slno=1
First create rank() in decreasing order of grade for individual department. then select top record for same.
Note: Use RANK() or DENSE_RANK(), both will work fine for top 1 record, while if you want to select n highest grade then use DENSE_RANK(), at the last for slno pass n'th record you want to select.
Always hard to do theory selects, but while DarkRob's solution is good, it will remove students if for instance two people are best. This is why I love using cross apply:
select
d.Depname
, s.StName
, g.stgrade
from TBL_DEPARTMANTS d
inner join TBL_LESSONS l on
d.DepID=l.LessonID
inner join TBL_GRADES g on
g.lessonid=l.LessonID
inner join TBL_STUDENT s on
s.STID=g.stid
cross apply (
select
sub_d.DepID
, max(sub_g.stgrade) as maxgrade
from TBL_DEPARTMANTS sub_d
inner join TBL_LESSONS sub_l on
sub_d.DepID=sub_l.LessonID
inner join TBL_GRADES sub_g on
sub_g.lessonid=sub_l.LessonID
where sub_d.Dep_ID = d.Dep_ID
group by sub_d.DepID
) as sub
where g.stgrade = sub.maxgrade
This way you will get all the people with max grade per department and not just one.

Display the city name which has most number of branches

I have tried to get city name which has most number of branches .
select C.City_name ,count(B.B_Name)
from tblcity C
inner join
tblBranch B
on c.city_id=B.City_id
group by C.City_name
order by count(B.B_Name) desc
Above code will give me the count of branches for particular city .
Please help me solve to get city name which has most number of branches
you can add TOP 1 to your query
select TOP 1 C.City_name ,count(B.B_Name)
from tblcity C
inner join
tblBranch B
on c.city_id=B.City_id
group by C.City_name
order by count(B.B_Name) desc
Use DENSE_RANK():
SELECT
City_Name, cnt
FROM
(
SELECT
c.City_name,
COUNT(b.B_Name) cnt,
DENSE_RANK() OVER (ORDER BY COUNT(b.B_Name) DESC) dr
FROM tblcity c
INNER JOIN tblBranch b
ON b.city_id = c.City_id
GROUP BY c.City_name
) t
WHERE dr = 1;
Using TOP 1 WITH TIES would be another option here, but that is specific to SQL Server.

Count and group the number of times each town is listed in the table

SELECT PEOPLE.TOWNKEY, TOWN_LOOKUP.TOWN FROM PEOPLE
INNER JOIN TOWN_LOOKUP
ON PEOPLE.TOWNKEY = TOWN_LOOKUP.PK
ORDER BY TOWN
Current Table Output:
You are missing the group by clause entirely:
SELECT tl.town, COUNT(*)
FROM people p
INNER JOIN town_lookup ON p.townkey = tl.pk
GROUP BY tl.town
ORDER BY tl.town

Select Most Recent Date with Inner Join

Running into a wall when trying to pull info from tables similar to those below. Not sure how to approach this.
The results should have the most recent TRANSAMT for each ACCNUM along with NAME and address.
Select A.ACCNUM, MAX(B.TRANSAMT) as BAMT, B.ADDRESS from
From TableA A inner join TableB on A.ACCNUM = B.ACCNUM
This is what i have so far. Any help would be appreciated.
TableA
ACCNUM NAME ADDRESS
00001 R. GRANT Miami, FL
00002 B. PAUL Dallas, TX
TableB
ACCNUM TRANSAMT TRANSDATE
00001 150 1/1/2015
00001 200 13/2/2015
00002 100 2/1/205
00003 50 18/2/2015
You can use the ANSI standard row_number() function in most databases. This allows you to do conditional aggregation:
select a.accnum, a.name, b.amount, a.address
from tableA a left join
(select b.*, row_number() over (partition by accnum order by transdate desc) as seqnum
from tableB b
) b
on a.accnum = b.accnum and b.seqnum = 1;
Note: I changed the join to a left join. This will keep all records in tableA, even those with no matches. I am not sure if that is the intention of your query.
You can use row_number to order rows per each account number by the most recent first.
select accnum, amt, name, address
from (
select A.ACCNUM, B.TRANSAMT as BAMT, B.ADDRESS,A.Name,
row_number() over(partition by a.accnum order by b.transdate desc) as rn
From TableA A
inner join TableB on A.ACCNUM = B.ACCNUM
) t
where rn = 1;
Please note this will not work if you are using MySQL.
This one with no ROW_NUMBER():
with find_max as(
select acc_name,max(TRANSDATE) as TRANSDATE from talbeB group by acc_name)
select find_max.ACCNUM , A.TRANSAMT ,
find_max.TRANSDATE , B.ADDRESS,B.Name
from tableA as A
join find_max on find_max.ACCNUM=A.ACCNUM and find_max.ACCNUM=A.ACCNUM
join TableB B on A.ACCNUM = B.ACCNUM
First find the max date for each acc_name, the join both of tables to it.
Will work on most data bases.