SQL - Select highest value when data across 3 tables - sql

I have 3 tables:
Person (with a column PersonKey)
Telephone (with columns Tel_NumberKey, Tel_Number, Tel_NumberType e.g. 1=home, 2=mobile)
xref_Person+Telephone (columns PersonKey, Tel_NumberKey, CreatedDate, ModifiedDate)
I'm looking to get the most recent (e.g. the highest Tel_NumberKey) from the xref_Person+Telephone for each Person and use that Tel_NumberKey to get the actual Tel_Number from the Telephone table.
The problem I am having is that I keep getting duplicates for the same Tel_NumberKey. I also need to be sure I get both the home and mobile from the Telephone table, which I've been looking to do via 2 individual joins for each Tel_NumberType - again getting duplicates.
Been trying the following but to no avail:
-- For HOME
SELECT
p.PersonKey, pn.Phone_Number, pn.Tel_NumberKey
FROM
Persons AS p
INNER JOIN
xref_Person+Telephone AS x ON p.PersonKey = x.PersonKey
INNER JOIN
Telephone AS pn ON x.Tel_NumberKey = pn.Tel_NumberKey
WHERE
pn.Tel_NumberType = 1 -- e.g. Home phone number
AND pn.Tel_NumberKey = (SELECT MAX(pn1.Tel_NumberKey) AS Tel_NumberKey
FROM Person AS p1
INNER JOIN xref_Person+Telephone AS x1 ON p1.PersonKey = x1.PersonKey
INNER JOIN Telephone AS pn1 ON x1.Tel_NumberKey = pn1.Tel_NumberKey
WHERE pn1.Tel_NumberType = 1
AND p1.PersonKey = p.PersonKey
AND pn1.Tel_Number = pn.Tel_Number)
ORDER BY
p.PersonKey
And have been looking over the following links but again keep getting duplicates.
SQL select max(date) and corresponding value
How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?
SQL Server: SELECT only the rows with MAX(DATE)
Am sure this must be possible but been at this a couple of days and can't believe its that difficult to get the most recent / highest value when referencing 3 tables. Any help greatly appreciated.

select *
from
( SELECT p.PersonKey, pn.Phone_Number, pn.Tel_NumberKey
, row_number() over (partition by p.PersonKey, pn.Phone_Number order by pn.Tel_NumberKey desc) rn
FROM
Persons AS p
INNER JOIN
xref_Person+Telephone AS x ON p.PersonKey = x.PersonKey
INNER JOIN
Telephone AS pn ON x.Tel_NumberKey = pn.Tel_NumberKey
WHERE
pn.Tel_NumberType = 1
) tt
where tt.rn = 1
ORDER BY
tt.PersonKey

you have to use max() function and then you have to order by rownum in descending order like.
select f.empno
from(select max(empno) empno from emp e
group by rownum)f
order by rownum desc
It will give you all employees having highest employee number to lowest employee number. Now implement it with your case then let me know.

Related

SQL aggregate functions, inner join

I am working on writing a sql to get the SID and SNAME. In this task, I need to count which team win the max number of League and find out the SID.
Leagues(LID, CHAMPION_TID)
LID: League ID ; CHAMPION_TID: champion team ID
SUPPORT(SID, LID)
SPONSORS(SID, SNAME)
PRIMARY KEY: LID,SID
Now, I can find out which team win the max number of League through the following SQL:
SELECT
MAX(y.cham)
FROM
(SELECT
CHAMPION_TID, COUNT(L.CHAMPION_TID) AS cham
FROM
LEAGUES L
GROUP BY
L.CHAMPION_TID) y, LEAGUES L
WHERE
y.CHAMPION_TID = L.CHAMPION_TID;
I am confusing in the following step. My idea get the LID, then use the join table to display SID and SNAME. But I suck in this step.
SELECT L.LID, MAX(y.cham)
FROM
(SELECT CHAMPION_TID, COUNT(L.CHAMPION_TID) AS cham
FROM LEAGUES L
GROUP BY L.CHAMPION_TID) y, LEAGUES L
WHERE
y.CHAMPION_TID = L.CHAMPION_TID
You can use the following to find the Sponsor ID and Sponsor Name:
SELECT DISTINCT
sp.SID,
sp.SNAME
FROM
LEAGUES l3
INNER JOIN support s ON
l3.LID = s.LID
INNER JOIN SPONSORS sp ON
s.SID = sp.SID
WHERE
l3.CHAMPION_TID IN (
SELECT
l2.CHAMPION_TID
FROM
LEAGUES l2
GROUP BY
l2.CHAMPION_TID
HAVING
count(l2.CHAMPION_TID) = (
SELECT
count(l1.CHAMPION_TID)
FROM
LEAGUES l1
GROUP BY
l1.CHAMPION_TID
ORDER BY
count(l1.CHAMPION_TID) DESC
FETCH FIRST 1 ROW ONLY
)
);
It finds the count of CHAMPION_TID in LEAGUES, orders it by desc (such that the highest count is always on top), then uses it to find the associated CHAMPION_TID. It handles ties for max(count(CHAMPION_TID)) as well :)
If fetch first 1 row only does not work, you can use select top 1 l1.CHAMPION_TID...
Here is a working demo using Postgres.

Mysql subquery with "in" problem to associate parent table

I try to create a query who select the contacts information (table invoice_contacts), and the adresses (table invoice_adresses) associate to the contact which is the most used in the (table invoice_compta)
For exemple I have two contact :
Mike
John
Mike have 2 adresses :
Paris
London
Mike have 1 invoice with Paris, and 5 invoice with London, so I want the adresse of London associate to Mike.
I have try this query with an subquery which count all adresses associate to the contact for an adresses (with NB_ADRESSES), and select only the biggest (with order by NB_ADRESSES desc and limit 1), it's seem wells but I have an error when I do where ia2.ID_CONTACT = ic.ID_CONTACT ic.ID_CONTACT is not found.. (and I need to associate the contact to the subquery).
select ic.*,
ia.*
from invoice_contacts ic
left join invoice_adresses ia on ia.ID_CONTACT = ic.ID_CONTACT
and ia.ID_ADRESSE in (
select ia3.ID_ADRESSE
from (
select ia2.ID_ADRESSE,
count(*) as NB_ADRESSES
from invoice_adresses ia2
left join invoice_comptas ico on ico.ID_ADRESSE_CONTACT = ia2.ID_ADRESSE
where ia2.ID_CONTACT = ic.ID_CONTACT
group by ia2.ID_ADRESSE
order by NB_ADRESSES desc
limit 1
) as ia3
)
group by ic.ID_CONTACT
order by CONTACT_TITRE asc
I also have try with "exist" or "inner join" instead of "in" but I doesn't find good results, so the best way seems it to be with this query for me, but I don't found the solution.
I hope you will help me :)
Thanks
UPDATE :
So finally I have found an solution with this query :
select ic.*,
ia.*
from invoice_contacts ic
left join invoice_adresses ia on ia.ID_CONTACT = ic.ID_CONTACT
and ia.ID_ADRESSE = (
select ia3.ID_ADRESSE
from (
select ia2.*,
count(*) as NB_ADRESSES
from invoice_adresses ia2
left join invoice_comptas ico on ico.ID_ADRESSE_CONTACT = ia2.ID_ADRESSE
group by ia2.ID_ADRESSE
) as ia3
where ia3.ID_CONTACT = ic.ID_CONTACT
order by NB_ADRESSES desc
limit 1
)
group by ic.ID_CONTACT
order by CONTACT_TITRE asc
Thanks
Let me rephrase the problem as finding the most common contact/address combination for a given invoice.
I find it hard to follow your query and your table naming. But this is the idea:
select contact, address
from (select contact, address, count(*) as cnt,
row_number() over (partition by contact order by count(*) desc) as seqnum
from invoices
group by contact, address
) ca
where seqnum = 1;
The subquery is counting the number of times a given address (or city if you prefer) occur for each contact. The row_number() enumerates these, so the most common one has a value of "1". The outer query then chooses the most common value.

SQL Join Multiple Tables and get the first row

I have a database where there are customers, phones, and addresses. Customers can have multiple phones and addresses and each phone and address record has a corresponding CustID. I'm trying to join these three tables and grab one phone and one address per customer, but cannot get this to work. The query returns one phone number per customer, but still return multiple addresses.
Here is my query:
SELECT customer_set.ID,
phone_set.cphone,
customer_set.Name,
address_set.Number,
address_set.Street
FROM (
SELECT ID, Name FROM CUSTOMER
) AS customer_set
LEFT JOIN PHONE AS phone_set
ON phone_set.CustID = (SELECT TOP 1 CustID
FROM PHONE
WHERE CustID = customer_set.ID
ORDER BY IsPrimary DESC)
LEFT JOIN ADDRESS AS address_set
ON address_set.CustID = (SELECT TOP 1 CustID
FROM ADDRESS
WHERE CustID = customer_set.ID
ORDER BY IsPrimary DESC)
Here you go:
SELECT
data.ID,
data.cphone,
data.Name,
data.Number,
data.Street
FROM (SELECT
customer_set.ID,
phone_set.cphone,
customer_set.Name,
address_set.Number,
address_set.Street,
ROW_NUMBER() OVER (PARTITION BY customer_set.ID ORDER BY customer_set.Name ASC) AS RowNum
FROM customer_set
LEFT OUTER JOIN PHONE
ON PHONE.CustID = customer_set.ID
LEFT OUTER JOIN ADDRESS
ON ADDRESS.CustID = customer_set.ID) AS data
WHERE data.RowNum = 1

How to display the record with the highest value in Oracle?

I have 4 tables with the following structure:
Table artist:
artistID lastname firstname nationality dateofbirth datedcease
Table work:
workId title copy medium description artist ID
Table Trans:
TransactionID Date Acquired Acquistionprice datesold askingprice salesprice customerID workID
Table Customer:
customerID lastname Firstname street city state zippostalcode country areacode phonenumber email
First question is which artist has the most works of artsold and how many of the artist works have been sold.
My SQL query is this:
SELECT * From dtoohey.artist A1
INNER JOIN
(
SELECT COUNT(W1.ArtistID) AS COUNTER, artistID FROM dtoohey.trans T1
INNER JOIN dtoohey.work W1
ON W1.workid = T1.Workid
GROUP BY W1.artistID
) TEMP1
ON TEMP1.artistID = A1.artistID
WHERE A1.artistID = TEMP1.artistId
ORDER BY COUNTER desc;
I am to get the whole table but I only want show only the first row which is the highest count how do I do that??
I have tried inserting WHERE ROWNUM <=1 but it shows artist ID with 1
qns 2 is sales of which artist's work have resulted in the highest average profit (i.e) the average of the profits made on each sale of worksby an artist), and what is that amount.
My SQL query is:
SELECT A1.artistid, A1.firstname FROM
(
SELECT
(salesPrice - AcquisitionPrice) as profit,
w1.artistid as ArtistID
FROM dtoohey.trans T1
INNER JOIN dtoohey.WORK W1
on W1.workid = T1.workid
) TEMP1
INNER JOIN dtoohey.artist A1
ON A1.artistID = TEMP1.artistID
GROUP BY A1.artistid
HAVING MAX(PROFIT) = AVG(PROFIT);
I'm not able to execute it
I have tried query below but still not able to get it keep getting the error missing right parenthesis
SELECT A1.artistid, A1.firstname, TEMP1.avgProfit
FROM
(
SELECT
AVG(salesPrice - AcquisitionPrice) as avgProfit,
W1.artistid as artistid
FROM dtoohey.trans T1
INNER JOIN dtoohey.WORK W1
ON W1.workid = T1.workid
GROUP BY artistid
ORDER BY avgProfit DESC
LIMIT 1
) TEMP1
INNER JOIN dtoohey.artist A1
ON A1.artisid = TEMP1.artistid
Sometimes ORA-00907: missing right parenthesis means exactly that: we have a left bracket without a matching right one. But it can also be thrown by a syntax error in a part of a statement bounded by parentheses.
It's that second cause here: LIMIT is a Mysql command which Oracle does not recognise. You can use an analytic function here:
SELECT A1.artistid, A1.firstname, TEMP1.avgProfit
FROM
(
select artistid
, avgProfit
, rank() over (order by avgProfit desc) as rnk
from (
SELECT
AVG(salesPrice - AcquisitionPrice) as avgProfit,
W1.artistid as artistid
FROM dtoohey.trans T1
INNER JOIN dtoohey.WORK W1
ON W1.workid = T1.workid
GROUP BY artistid
)
) TEMP1
INNER JOIN dtoohey.artist A1
ON A1.artisid = TEMP1.artistid
where TEMP1.rnk = 1
This uses the RANK() function which will return more than one row if several artists achieve the same average profit. You might want to use ROW_NUMBER() instead. Analytic functions can be very powerful. Find out more.
You can apply ROWN_NUMBER(), RANK() and DENSE_RANK() to any top-n problem. You can use one of them to solve your first problem too.
"however the avg profit is null."
That's probably a data issue. If one of the numbers in (salesPrice - AcquisitionPrice) is null the result will be null, and won't be included in the average. If all the rows for an artist are null the AVG() will be null.
As it happens the sort order will put NULL last. But as the PARTITION BY clause sorts by AvgProfit desc that puts the NULL results at rank 1. The solution is to use the NULLS LAST in the windowing clause:
, rank() over (order by avgProfit desc nulls last) as rnk
This will guarantee you a non-null result at the top (providing at least one of your artists has values in both columns).
1st question - Oracle does not guarantee the order by which rows are retrieved. Hence you must first order and then limit the ordered set.
SELECT * from (
SELECT A1.* From dtoohey.artist A1
INNER JOIN
(
SELECT COUNT(W1.ArtistID) AS COUNTER, artistID FROM dtoohey.trans T1
INNER JOIN dtoohey.work W1
ON W1.workid = T1.Workid
GROUP BY W1.artistID
) TEMP1
ON TEMP1.artistID = A1.artistID
WHERE A1.artistID = TEMP1.artistId
ORDER BY COUNTER desc
) WHERE ROWNUM = 1
2nd question: I believe (haven't tested) that you have that LIMIT 1 wrong. That keyword is for use with Bulk collecting.

SQL query for finding row with same column values that was created most recently

If I have three columns in my MySQL table people, say id, name, created where name is a string and created is a timestamp.. what's the appropriate query for a scenario where I have 10 rows and each row has a record with a name. The names could have a unique id, but a similar name none the less. So you can have three Bob's, two Mary's, one Jack and 4 Phil's.
There is also a hobbies table with the columns id, hobby, person_id.
Basically I want a query that will do the following:
Return all of the people with zero hobbies, but only check by the latest distinct person created, if that makes sense. Meaning if there is a Bob person that was created yesterday, and one created today.. I only want to know if the Bob created today has zero hobbies. The one from yesterday is no longer relevant.
select pp.id
from people pp, (select name, max(created) from people group by name) p
where pp.name = p.name
and pp.created = p.created
and id not in ( select person_id from hobbies )
SELECT latest_person.* FROM (
SELECT p1.* FROM people p1
WHERE NOT EXISTS (
SELECT * FROM people p2
WHERE p1.name = p2.name AND p1.created < p2.created
)
) AS latest_person
LEFT OUTER JOIN hobbies h ON h.person_id = latest_person.id
WHERE h.id IS NULL;
Try This:
Select *
From people p
Where timeStamp =
(Select Max(timestamp)
From people
Where name = p.Name
And not exists
(Select * From hobbies
Where person_id = p.id))