SQL Join Multiple Tables and get the first row - sql

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

Related

Need SQL MAX please

I have a table with a list of Client No's, ID etc & there are many Clients with different ID's. I need to pull out the MAX ID No. for each Client, e.g.
ClientNo: 1500 has 3 ID's - the maximum in the ID field is the one I need!
UPDATE: This works:
SELECT MP.ClientID, MP.SequenceID
FROM TABLENAME MP
INNER JOIN (
SELECT ClientID, MAX(SequenceID) SequenceID
FROM TABLENAME
GROUP BY ClientID
) b on MP.ClientID = b.ClientID AND MP.SequenceID = b.SequenceID
BUT....
I need to link the table to many others to pull in other data, where do I insert the left joins to these tables please?
I am assuming you have multiple same client (numbers) with different IDs and for each client (number) you have to get the maximum ID. You may do the following:
select client_number, max(ID) from client group by client_number;
Depending on your need tweak this query.
You may want to do this:
SELECT MP.ClientID, b.DesiredValue
FROM TABLENAME MP
INNER JOIN (
SELECT ClientID, DesiredValue, ROW_NUMBER () OVER (PARTITION BY ClientID order by SequenceID desc) As RecentRN
FROM TABLENAME
) b
on MP.ClientID = b.ClientID AND b.RecentRN = 1
or
SELECT MP.ClientID, b.DesiredValue
FROM TABLENAME MP
INNER JOIN (
SELECT ClientID, DesiredValue,ROW_NUMBER () OVER (PARTITION BY ClientID order by SequenceID desc) As RecentRN
FROM TABLENAME
) b
on MP.ClientID = b.ClientID
Where b.RecentRN = 1

Getting duplicate rows after two JOINS and a UNION command

I have 3 tables in the following way:
Client with a client id
Cards with a card id and a FK with client id
Transactions with a transaction id and a FK with Card id
Every client can have more than a card, and a card can make more than a transaction.
I want to do the following: Obtain the 3 clients with the minor number of transactions.
My problem: my query returns a client 2 times.
SELECT TOP 3
ClientId, Name, LastName, QtyTrans
FROM
(SELECT
ClientId, Name, LastName, 0 AS QtyTrans
FROM
Client
JOIN
Card ON ClientId = IdClient
WHERE
CardId NOT IN (SELECT CardId FROM Transaction)
GROUP BY
ClientId, Name, LastName
UNION
SELECT
ClientId, Name, LastName, COUNT(TransId) AS QtyTrans
FROM
Client
JOIN
Card c ON ClientId = IdClient
JOIN
Transaction t ON c.CardId = t.CardId
GROUP BY
ClientId, Name, LastName
) TotalTrans
ORDER BY
QtyTrans
When the client has transactions with every card, just returns one row, but if one of the cards has no transactions returns one row with the count 0 and another one with the sum of the other cards transactions.
Result:
11 Martin Camejo 0,
7 Matias Barrenechea 0,
7 Matias Barrenechea 2
Any help?
I want to do the following: Obtain the 3 clients with the minor number of transactions.
I don't see what UNION has to do with this question. You just want LEFT JOIN and GROUP BY:
SELECT TOP (3) cl.ClientId, cl.Name, cl.LastName,
COUNT(t.CardId) AS QtyTrans
FROM Client cl LEFT JOIN
Card c
ON cl.ClientId = c.IdClient LEFT JOIN -- don't know which table has which column
Transaction t
ON t.CardId = c.CardId
GROUP BY cl.ClientId, cl.Name, cl.LastName
ORDER BY QtyTrans ASC;

How to filter out duplicate records caused by a JOIN in SQL?

I have a simple query that returns a list of phone numbers for a given customer. The users are able to search for a specific customer by entering any part of their address. The customer can have multiple phone numbers and addresses.
Here is an example of my query:
SELECT
ROW_NUMBER() OVER(PARTITION BY Customer.CustomerNumber, ORDER BY PhoneNumber.PhoneNumber) RowNumber,
Customer.CustomerNumber,
PhoneNumber.PhoneNumber
FROM Customer
JOIN PhoneNumber ON PhoneNumber.CustomerId = Customer.Id
JOIN CustomerAddress on CustomerAddress.CustomerId = Customer.Id
Here is what this query is producing when I have a customer that has two phone numbers and two addresses:
RowNumber CustomerNumber PhoneNumber
1 1 111-111-1111
2 1 222-222-2222
3 1 111-111-1111
4 1 222-222-2222
My desired result would be something along these lines:
RowNumber CustomerNumber PhoneNumber
1 1 111-111-1111
2 1 222-222-2222
I can only produce the desired result above when I remove the join on the address table.
A user should be able to look up a customer by entering in any part of the address (ex: I want to show any user that has an address in Phoenix). While it isn't displayed in the result, it still should be filterable.
I think I am able to do something like this:
SELECT *, ROW_NUMBER() OVER(PARTITION BY Test.CustomerNumber ORDER BY Test.PhoneNumber) RowNumber
FROM
(
SELECT
DISTINCT
CustomerNumber,
PhoneNumber.PhoneNumber
FROM Customer
JOIN PhoneNumber ON PhoneNumber.CustomerId = Customer.Id
JOIN CustomerAddress ON CustomerAddress.CustomerId = Customer.Id
) Test
You're not selecting any columns from the CustomerAddress table, so joining that table has only the effects of suppressing results for customers with no address and duplicating results for those with multiple addresses.
If you don't want either of those effects then don't join CustomerAddress. If you want only the former then you'd be better off with a different approach, such as
SELECT
ROW_NUMBER() OVER(PARTITION BY Customer.CustomerNumber,
ORDER BY PhoneNumber.PhoneNumber) RowNumber,
Customer.CustomerNumber,
PhoneNumber.PhoneNumber
FROM
Customer
JOIN PhoneNumber ON PhoneNumber.CustomerId = Customer.Id
WHERE
Customer.Id IN (SELECT CustomerId from CustomerAddress)
Presuming that Customer.Id is a primary key, that should produce duplicates only if duplicate phone numbers for a given customer are recorded in the PhoneNumber table.
Removing the join on address might affect the ability of users to search by address. If that is the problem, change the query to use exists:
SELECT ROW_NUMBER() OVER (PARTITION BY c.CustomerNumber ORDER BY pn.PhoneNumber) as RowNumber,
c.CustomerNumber,
pn.PhoneNumber
FROM Customer c JOIN
PhoneNumber pn
ON pn.CustomerId = c.Id
WHERE EXISTS (SELECT 1
FROM CustomerAddress ca
WHERE ca.CustomerId = c.Id AND
ca.address like '%#ADDRESS%' -- this is just an example of searching logic
);
Change your window function to partition by customer and phone number in a sub query. I use this all the time to filter dupes. Just set the partition by to your unique key.
Select * from (
SELECT
ROW_NUMBER() OVER(PARTITION BY Customer.CustomerNumber, PhoneNumber PhoneNumber, ORDER BY PhoneNumber.PhoneNumber) RowNumber,
Customer.CustomerNumber,
PhoneNumber.PhoneNumber
FROM Customer
JOIN PhoneNumber ON PhoneNumber.CustomerId = Customer.Id
JOIN CustomerAddress on CustomerAddress.CustomerId = Customer.Id)
Where RowNumber = 1

SQL - Select highest value when data across 3 tables

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.

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.