In SQL server 2008 R2, how do you get the MAX result if you have two rows that have the same value? I have a table that I use to store the data about the number of times a client is seen at a location.
create table #templocation
(
ClientID Int,
Location Varchar(100),
LocationCount Int,
MaxCount Int
)
I only fill the first three columns with an insert statement then I get the max for each client like this:
update t
set t.MaxCount = t2.locationcount
from #templocation t
join (select tl.ClientID, MAX(tl.LocationCount) as 'locationcount'
from #templocation tl
group by tl.ClientID) t2
on t2.ClientID = t.ClientID
update t
set t.PrimaryLocation = tl.Location
from #temp t
join #templocation tl
on t.ClientId = tl.ClientID
where tl.LocationCount = tl.MaxCount
I then join this to the main table to get the final results. My problem is if a client is has two or more max counts. Then it wants to display all of the max locations like this:
ClientId Location LocationCount
12502 Main St. 4
12502 Lake Ave 4
12502 Tracy Rd 2
The results I get are:
ClientId ClientName Location
12502 John Smith Main St.
12502 John Smith Lake Ave
I only want the first or top one displayed though preferably by alphabetical order.
I do things like this through a couple of CTEs. One will give us the max(locationcount) for each client, and one just adds a row_number() for every clientid so we can pick the "first" location".
SQL Fiddle
It's a bit of a hack, but it works.
;with maxrows as
(select
ClientID as ClientID,
max(LocationCount) as MaxValue
--row_number() OVER (partition by ClientID order by ClientID)
from
table1
group by ClientID)
,
rowcounts as
(
select
t1.ClientID,
t1.Location,
row_number() over (PARTITION BY t1.ClientID order by t1.Location) as RowNum
from
table1 t1
)
select
t2.ClientID,
t2.Location
from
maxrows t1
inner join rowcounts t2
on t1.ClientID = t2.ClientID
and t2.rownum = 1
From how I understood the issue, you could use MAX() OVER () to achieve what you want like this:
WITH maxvalues AS (
SELECT
ClientId,
Location,
LocationCount,
MaxCount = MAX(LocationCount) OVER (PARTITION BY ClientId)
FROM atable
)
SELECT
ClientId,
Location
FROM maxvalues
WHERE LocationCount = MaxCount
;
Join maxvalues to whatever table holds the client names to include those into the output too.
Learn more about the OVER clause in this manual:
OVER clause (Transact-SQL)
Related
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
How is the given query correct as it is using T1 inside the with clause and T1 is declared after the clause within WITH is completed.
WITH T1(Emp,Manager,Salary) AS
(
SELECT tt2.[Emp],tt2.[Manager],tt2.[Salary]
FROM [YourTable] AS tt1
RIGHT OUTER JOIN [YourTable] AS tt2 ON tt1.[Emp]=tt2.[Manager]
WHERE tt1.[Emp] is NULL
UNION ALL
SELECT r.[Emp],T1.[Manager],r.[Salary]
FROM [YourTable] AS r
INNER JOIN T1 ON r.[Manager]=T1.[Emp]
)
SELECT [Manager],SUM([Salary]) AS Salary
FROM T1
GROUP BY [Manager]
ORDER BY SUM([Salary]) DESC
Above query is answer to following question -
I have table with columns (Employee, Manager, Salary). Need to calculate aggregate salary for all employees corresponding to top-level managers in one SQL. For example
Input table is :
Emp Manager Salary
A T 10
B A 11
C F 13
D B 5
Result should be :
Top-Lvl Manager Salary(agg)
T 26
F 13
Manager-Employee layering can go multiple levels.
This is a recursive query. The part before UNION ALL gets the base records. The part after it recursively gets more rows attatched to the former.
The first part is confusingly written. It is an anti-join pattern inplemented even with a right outer join which many consider hard to read. It merely means this:
select emp, manager, salary
from yourtable
where manager not in (select emp from yourtable);
So you get all employees that have no manager (i.e. the super managers).
With the part after UNION ALL you get their subordinates and the subordinates of these etc. A hierarchical query.
At last in
SELECT [Manager],SUM([Salary]) AS Salary
FROM T1
GROUP BY [Manager]
ORDER BY SUM([Salary]) DESC
you use those rows to get a cumulated salary per manager.
You can read up on recursive queries in SQL Server here: https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx.
EDIT - Less over-kill
Declare #YourTable table (Emp varchar(25),Manager varchar(25),Salary int)
Insert into #YourTable values
('A','T',10),
('B','A',11),
('C','F',13),
('D','B',5)
;with cteP as (
Select Seq = cast(1000+Row_Number() over (Order by Emp) as varchar(500))
,Emp=Manager
,Manager=cast(null as varchar(25))
,Lvl=1
,Salary = 0
From #YourTable
Where Manager Not In (Select Distinct Emp From #YourTable)
Union All
Select Seq = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.Emp)) as varchar(500))
,r.Emp
,r.Manager
,p.Lvl+1
,r.Salary
From #YourTable r
Join cteP p on r.Manager = p.Emp)
Select TopLvl = A.Emp
,Salary = sum(B.Salary)
from cteP A
Join cteP B on (B.Seq Like A.Seq+'%')
Where A.Lvl=1
Group By A.Emp
Returns
TopLvl Salary
F 13
T 26
Inside with T1 references to INNER JOIN T1 ON r.[Manager]=T1.[Emp] which is probably table in your DB. Outside with T1 references to result of with statement.
I have 2 tables: Users and Results.
The usertable contains duplicate data which is reflected in the results table. The user below is created 3 times. I need to update the results table where UserId 2 and 3 to 1 so that all the results can be viewed on this user only.
This is easy if I have only have a few users and a few results for them, but in my case I have 500 duplicated users and 30000 results.
I am using SQL Server Express 2014
I will really appreciate any help with this!
Edit: misstyped column names in resultTable. Im sorry if you got confused by it.
UserTable
UserId---Fname---LName
1-----Georg-----Smith
2-----Georg-----Smith
3-----Georg-----Smith
ResultsTable
ResultId---UserRefId
1-----1
2-----2
3-----3
4-----1
I have manage to select duplicates from usertable, but i don't know how to proceed further.
;WITH T AS
(
SELECT *, COUNT(*) OVER (PARTITION BY Fname + Lname) as Cnt
FROM TestDatabase.Users
)
SELECT Id, Fname, Lname
FROM T
WHERE Cnt > 2
Your ResultTable has 2 columns with the same UserId name. I changed the second to UserId2 for the query below:
;WITH cte As
(
SELECT R.UserId, R.UserId2,
MIN(U.UserId) OVER (PARTITION BY U.FName, U.LName) As OriginalUserId
FROM ResultTable R
INNER JOIN UserTable U ON R.UserId = U.UserId
)
UPDATE cte
SET UserId2 = OriginalUserId
You are on the right track with the cte. The ROW_NUMBER() function can be used to flag duplicate UserIds, then you can join the cte into the from clause of your update statement to find the UserIds you want to replace, and join again to find the UserIds you want to replace them with.
;WITH cteDedup AS(
SELECT
UserId
,FName
,LName
,ROW_NUMBER() OVER(PARTITION BY FName, LName ORDER BY UserID ASC) AS row_num
FROM UserTable
)
UPDATE rt
SET UserId = original.UserId
FROM ResultsTable rt
JOIN cteDedup dupe
ON rt.UserId = dupe.UserId
JOIN cteDedup original
ON dupe.FName = original.FName
AND dupe.LName = original.LName
WHERE dupe.row_num <> 1
AND original.row_num = 1
See the SQLFiddle
A little tricky query looks like this:
;with t as (
select fname+lname name,id,
ROW_NUMBER() over(partition by fname+lname order by id) rn
from #users
)
--for test purpose comment next 2 lines
update #results
set userid=t1.id
--and uncomment the next one
--select t.name,t.id,userid,res,t1.id id1--,(select top 1 id from t t1 where t1.name=t.name and t.rn=1) id1
from t
inner join #results r on t.id=r.userid
inner join t t1 on t.name=t1.name and t1.rn=1
And then you can delete duplicate users
;with t as (
select name,id,
ROW_NUMBER() over(partition by name order by id) rn
from #users
)
delete t where rn>1
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.
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.