Select Most Recent Date with Inner Join - sql

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.

Related

Oracle SQL: show max when record in table exists

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;

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;

Making a MAX() query from a subquery with COUNT()

Need to Show the Name of the boat which made the most trips, so i made a query that counts the trips:
SELECT B.IdBoat, COUNT(T.IdTrip)
FROM Trip T INNER JOIN Boat B ON T.IdBoat=B.IdBoat
GROUP BY B.IdBoat
Now I need to show the name of the one with the MAX trips, how do I use that query as a subquery, without using the ORDER BY DESC and TOP 1 but using MAX?
Currently got:
SELECT B.Name
FROM Trip T INNER JOIN Boat B ON T.IdBoat=B.IdBoat
WHERE B.IdBoat = MAX( the sub query above)
also tried
SELECT B.Name, T.IdTrip
FROM Boat B INNER JOIN Trip T ON B.IdBoat=T.IdBoat
WHERE B.IdBoat IN (
SELECT MAX(T.NTrips) FROM
(SELECT B.IdBoat AS [IdBoat], COUNT(T.IdTrip) AS [NTrips]
FROM Trip T INNER JOIN Boat B ON B.IdBoat=T.IdBoat
GROUP BY B.Boat) T
GROUP BY T.IdBoat)
The above returned the full count of 3 on the name of the boat instead of the correct 2.
I've tried googling and searching about said problem on stackoverflow and others but can't adapt their solution to my query, any help is good help.
Thank you.
edit 1. As asked, I'll provide some data as to help understand the problem better
Table Boat:
IdBoat | Name
1 | 'SS Sparrow'
2 | 'SS AndaNoMar'
Table Trip
IdTrip | IdBoat
1 | 1
2 | 1
3 | 2
Subquery 1 (COUNT)
IdBoat | NTrips
2 | 1
1 | 2
You can do:
with
x as (
select
b.idBoat,
b.Name,
count(*) as cnt
from trip t
join boat b on b.idBoat = t.idBoat
group by b.idBoat, b.Name
),
m as (
select max(cnt) as max_cnt from x
)
select
x.*
from x
join m on m.max_cnt = x.cnt
SELECT
B.IdBoat,
B.Name,
T.Trips
FROM
Boat AS B
INNER JOIN
(
SELECT
IdBoat,
COUNT(*) AS Trips,
RANK() OVER (PARTITION BY IdBoat
ORDER BY COUNT(*) DESC
)
AS TripsRank
FROM
Trip
GROUP BY
IdBoat
)
AS T
ON T.IdBoat = B.IdBoat
WHERE
T.TripsRank = 1
A better method than either of the other two answers is to use ORDER BY:
SELECT TOP (1) B.IdBoat, B.Name, COUNT(T.IdTrip) as cnt
FROM Trip T INNER JOIN
Boat B
ON T.IdBoat = B.IdBoat
GROUP BY B.IdBoat, B.Name
ORDER BY cnt DESC;
There is no need for subqueries or CTEs or window functions.
If you want ties, then you can use TOP (1) WITH TIES.

Row value from another table

I have a table that is having 2 duplicate rows (total of 3 rows), so I used the code below to get the duplicate value in the column
SELECT CustNo, COUNT(*) TotalCount
FROM Rental
GROUP BY CustNo
HAVING COUNT(*) > 1
ORDER BY COUNT(*) DESC
So once I get the repeated value, I need to get the CustNo derived as duplicate from the customer table. How do I go about taking this value and using it in the select statment all in the same query.
I also have the select statement prepared like this.
Select * from Customer where CustNo = 'T0002';
Thanks.
Select * from Customer
where CustNo IN
(
SELECT CustNo
FROM Rental
GROUP BY CustNo
HAVING COUNT(*) > 1
)
You can use join:
SELECT c.*
FROM (SELECT CustNo, COUNT(*) TotalCount
FROM Rental
GROUP BY CustNo
HAVING COUNT(*) > 1
) cc JOIN
Customer c
on cc.CustNo = c.CustNo;
Select C.* from Customer C RIGHT JOIN (
SELECT CustNo
FROM Rental
GROUP BY CustNo
HAVING COUNT(*) > 1) D
ON C.CustNo = D.CustNo
You can also try this,
With tblDups as(
select CustNo,count(CustNo) as TotalCount from a_rental
Group by CustNo
Having count(CustNo) >1)
select b.* from a_rental b
inner join tblDups a on a.CustNo = b.Custno

Join table on null where condition

I have tables Member and Transaction. Table Member has 2 columns MemberID and MemberName. Table Transaction has 3 columns, MemberID, TransactionDate, and MemberBalance.
The rows in the tables are as shown below:
Table Member:
MemberID MemberName
=============================
1 John
2 Betty
3 Lisa
Table Transaction:
MemberID TransactionDate MemberBalance
=====================================================
1 13-12-2012 200
2 12-12-2012 90
1 10-09-2012 300
I would like to query for MemberID, MemberName and MemberBalance where the TransactionDate is the latest (max) for each MemberID.
My query is like this:
SELECT
t.MemberID, m.MemberName , t.MemberBalance
FROM
Member AS m
INNER JOIN
Transaction AS t ON m.MemberID = t.MemberID
WHERE
t.TransactionDate IN (SELECT MAX(TransactionDate)
FROM Transaction
GROUP BY MemberID)
This query returns:
MemberID MemberName MemberBalance
===================================================
1 John 200
2 Betty 90
My problem is, I want the query to return:
MemberID MemberName MemberBalance
===================================================
1 John 200
2 Betty 90
3 Lisa NULL
I want the member to be displayed even if its MemberID does not exist in the Transaction table.
How do I do this?
Thank you.
You can also use something like this:
SELECT m.MemberID, m.MemberName, t1.MemberBalance
FROM Member AS m
LEFT JOIN
(
select max(transactionDate) transactionDate,
MemberID
from Transactions
group by MemberID
) AS t
ON m.MemberID = t.MemberID
left join transactions t1
on t.transactionDate = t1.transactionDate
and t.memberid = t1.memberid
See SQL Fiddle with Demo
member to be displayed even if its MemberID does not exist in Transaction table
You can preserve rows using LEFT JOIN on the Member table to Transaction table.
where the TransactionDate is the latest (max) for each MemberID.
From SQL Server 2005 onwards, the preferred and better performing method is to use ROW_NUMBER()
SELECT MemberID, MemberName, MemberBalance
FROM (
SELECT m.MemberID, m.MemberName , t.MemberBalance,
row_number() over (partition by m.MemberID order by t.TransactionDate desc) rn
FROM Member AS m
LEFT JOIN [Transaction] AS t ON m.MemberID = t.MemberID
) X
WHERE rn=1;
To keep member in the result set, you need an outer join.
Also, please don't forget to add a condition on memberid for inner select query, as you might get issues when a maximum date for one user would match a non-maximum date of another (your where condition would pass twice for the second user as his transaction dates would appear on the select's results twice, one would be his actual maximum date and another - the max date of some user matching a non-max date)
You need to use LEFT JOIN. Also you had an error in your query because if two members had transactions at the same time you can get two rows for both the users.
Try this
SELECT t.MemberID, m.MemberName , t.MemberBalance
FROM Member AS m
LEFT JOIN Transaction AS t ON m.MemberID = t.MemberID AND t.TransactionDate=
(
SELECT MAX(TransactionDate)
FROM Transaction T2
WHERE T2.MemberID=t.MemberID
)
SELECT a.MemberId,a.MemberName,a.MemberBalance
FROM
(
SELECT m.MemberId,m.MemberName,t1.MemberBalance
,ROW_NUMBER() OVER(PARTITION BY m.MemberId ORDER BY t1.TransactionDate DESC) AS RN
FROM
#Member m OUTER APPLY (SELECT t.MemberId,t.MemberBalance,t.TransactionDate
FROM #Transaction t WHERE m.MemberId=t.MemberId) t1
)a
WHERE a.RN=1