Get highest value from every group - sql

I have table:
Id, Name, Account, Date, ItemsToSend
I want to group rows by Name and Account
From each group I want to get elements with latest Date
And display element's Name, Account and ItemsToSend
I managed something like this:
select
Name,
Account,
max(Date),
max(CountItemsSend)
from History
where
Date = (
select max(Date)
from History as p
where
p.Account = History.Account
and p.Name = History.Name
)
group by
Name,
Account
I am afraid of max(Date), max(CountItemsSend). I dont think it is ok. After where there is only 1 result for each group, so what is the point of max use there?

A CTE can make this neater.
WITH maxDates as
(
SELECT Name, Account, MAX(Date) as LatestDate
FROM History
GROUP BY Name, Account
)
SELECT h.Name, h.Account, h.Date, h.CountItemsSend
FROM History h
INNER JOIN maxDates m
on m.Name = h.Name and m.Account = h.Account and m.LatestDate = h.Date

Another possible approach is to use ROW_NUMBER() to number rows grouped by name and account and ordered by date descending and then select the rows with number equal to 1. These rows are with max Date per group and CountItemsSend from the same row.
SELECT
t.[Name],
t.[Account],
t.[Date],
t.[CountItemsSend]
FROM (
SELECT
[Name],
[Account],
[Date],
[CountItemsSend],
ROW_NUMBER() OVER (PARTITION BY [Name], [Acount] ORDER BY [Date] DESC) AS Rn
FROM History
) t
WHERE t.Rn = 1

You don't need aggregation. Just:
select h.*
from History h
where h.Date = (select max(h2.Date)
from History h2
where h2.Account = h.Account and
h2.Name = h.Name
);

Related

How to get last date on max count transaction_id by branch in sql

I would like to get last date on max count txn_id base on branch_name.
This is the data
I want the result like this
here is my script. but I get only one row.
select max(date),
account,
branch_name,
province,
district
from
(select date,
account,
branch_name,
province,
district,
RANK() OVER (ODER BY txn_no desc) rnk
from
(select count(tr.txn_id) txn_no,
tr.date,
u.account,
b.branch_name,
b.province,
b.district
from transaction tr
inner join users u
on u.user_id = tr.user_id
inner join branch b
on b.user_id = u.user_id
where 1=1
and tr.date >= to_date('01/04/2021','dd/mm/yyyy') and tr.date < to_date('30/04/2021','dd/mm/yyyy')
group by tr.date,
u.account,
b.branch_name,
b.province,
b.district
))
where rnk = 1
group by tr.date,
u.account,
b.branch_name,
b.province,
b.district
WITH
cte1 AS ( SELECT *, COUNT(*) OVER (PARTITION user, branch) cnt
FROM source_table ),
cte2 AS ( SELECT *, RANK() OVER (PARTITION BY user ORDER BY cnt DESC) rnk
FROM cte1 )
SELECT *
FROM cte2
WHERE rnk = 1
If more than one branch have the same amount of rows then all of them will be returned. If only one of them must be returned in this case then according additional criteria must be used and ORDER BY expression clause in cte2 must be accordingly expanded. If any (random) must be returned in this case then RANK() in cte2 must be replaced with ROW_NUMBER().
I would do it with SELECT TOP 1 ... instead of RANK(), something like:
SELECT date,
txn_id,
account,
branch_name,
province,
district
FROM transaction t
WHERE branch_name = (
SELECT TOP 1 branch_name
FROM (
SELECT branch_name, count(*) as cnt
FROM transaction
WHERE account = t.account
GROUP BY branch_name
ORDER BY 2 DESC
) s
AND date = (
SELECT TOP 1 date
FROM (
SELECT date, count(*) as cnt
FROM transaction
WHERE account = t.account
AND branch_name = (
SELECT TOP 1 branch_name
FROM (
SELECT branch_name, count(*) as cnt
FROM transaction
WHERE account = t.account
GROUP BY branch_name
ORDER BY 2 DESC
) s2
GROUP BY branch_name
ORDER BY 2 DESC
) s2
)
If there are multiple transactions on the last date, they all are going to be returned though.
You want the most recent transaction from the account/branch with the most transactions. If so, you can do this as:
select t.*
from (select t.*,
max(cnt) over (partition by account) as max_cnt
from (select t.*,
count(*) over (partition by account, branch_name) as cnt,
row_number() over (partition by account, branch_name order by date desc) as seqnum
from t
) t
) t
where max_cnt = cnt and seqnum = 1;
Note: If there are multiple branches that have the same count, then they are all returned. Your question does not specify how to deal with such duplicates.

How to select multiple max values from a sql table

I am trying to get the top performers from a table, grouped by the company but can't seem to get the grouping right.
I have tried to use subqueries but this goes beyond my knowledge
I am trying to make a query that selects the rows in green. In other words I want to include the name, the company, and what they paid but only the top performers of each company.
Here is the raw data
create table test (person varchar(50),company varchar(50),paid numeric);
insert into
test
values
('bob','a',200),
('jane','a',100),
('mark','a',350),
('susan','b',650),
('thabo','b',100),
('thembi','b',210),
('lucas','b',110),
('oscar','c',10),
('janet','c',20),
('nancy','c',30)
You can use MAX() in a subquery as
CREATE TABLE T(
Person VARCHAR(45),
Company CHAR(1),
Paid INT
);
INSERT INTO T
VALUES ('Person1', 'A', 10),
('Person2', 'A', 20),
('Person3', 'B', 10);
SELECT T.*
FROM T INNER JOIN
(
SELECT Company, MAX(Paid) Paid
FROM T
GROUP BY Company
) TT ON T.Company = TT.Company AND T.Paid = TT.Paid;
Demo
Or using a window function as
SELECT Person,
Company,
Paid
FROM
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY Company ORDER BY Paid DESC) RN
FROM T
) TT
WHERE RN = 1;
Demo
Here's your query.
select a.person, a.company, a.paid from tableA a
inner join
(select person, company, row_number() over (partition by company order by paid desc) as rn from tableA) as t1
on t1.person = a.person and t1.company = a.company
where t1.rn = 1
Maybe something like
WITH ranked AS (SELECT person, company, paid
, rank() OVER (PARTITION BY company ORDER BY paid DESC) AS rnk
FROM yourtable)
SELECT person, company, paid
FROM ranked
WHERE rnk = 1
ORDER BY company;
You can use rank() function with partition by clause.
DENSE_RANK gives you the ranking within your ordered partition, but the ranks are consecutive. No ranks are skipped if there are ranks with multiple items.
WITH cte AS (
SELECT person, company, paid
rank() OVER (PARTITION BY company ORDER BY paid desc) rn
FROM yourtable
)
SELECT
*
FROM cte

SQL Server, how to get younger users?

I'm trying to get users from a younger country for example I have the following tables.
If there is more than one user of the youngest who have the same age, they should also be shown
Thanks
You can try this query, get MIN birthday on subquery then self join on users table.
select u.idcountry,t.name,u.username, (DATEPART(year, getdate()) - t.years) 'age'
from
(
SELECT u.idcountry,c.name,DATEPART(year, u.birthday) as 'years',count(*) as 'cnt'
FROM users u inner join country c on u.idcountry = c.idcountry
group by u.idcountry,c.name,DATEPART(year, u.birthday)
) t inner join users u on t.idcountry = u.idcountry and t.years = DATEPART(year, u.birthday)
where t.cnt > 1
sqlfiddle:https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=9baab959f79b1fa8c28ed87a8640e85d
Use the rank() window function:
select ...
from ...
where rank() over (partition by idcountry order by birthday) = 1
Rows with the same birthday in a country are ranked the same, so this returns all youngest people with if there’s more than one.
This is a little tricky. I would use window functions -- count the people of a particular age and choose the ones where there are duplicates for the youngest.
You don't specify how to define age, so I'll just use the earliest calendar year:
select u.*
from (select u.*,
count(*) over (partition by idcountry, year(birthday)) as cnt_cb,
rank() over (partition by idcountry order by year(birthday)) as rnk
from users u
) u
where cnt_cb > 1 and rnk = 1;
I'll let you handle the joins to bring in the country name.
Your sample data and desired results show the oldest users within each country when more than one of the oldest have the same age. The query below will do that, assuming age is calculated using full birth date.
WITH
users AS (
SELECT
username
, birthday
, idcountry
, (CAST(CONVERT(char(8),GETDATE(),112) AS int) - CAST(CONVERT(char(8),birthday,112) AS int)) / 10000 AS age
, RANK() OVER(PARTITION BY idcountry ORDER BY (CAST(CONVERT(char(8),GETDATE(),112) AS int) - CAST(CONVERT(char(8),birthday,112) AS int)) / 10000 DESC) AS age_rank
FROM dbo.Users
)
, oldest_users AS (
SELECT
username
, birthday
, idcountry
, age
, COUNT(*) OVER(PARTITION BY idcountry, age_rank ORDER BY age_rank) AS age_count
FROM users
WHERE age_rank = 1
)
SELECT
c.idcountry
, c.name
, oldest_users.age
, oldest_users.username
FROM oldest_users
JOIN dbo.Country AS c ON c.idcountry = oldest_users.idcountry
WHERE
oldest_users.age_count > 1;

SQL Return MAX Values from Multiple Rows

I have tried many solutions and nothing seems to work. I am trying to return the MAX status date for a project. If that project has multiple items on the same date, then I need to return the MAX ID. So far I have tried this:
SELECT PRJSTAT_ID, PRJSTAT_PRJA_ID, PRJSTAT_STATUS, PRJSTAT_DATE
From Project_Status
JOIN
(SELECT MAX(PRJSTAT_PRJA_ID) as MaxID, MAX(PRJSTAT_DATE) as MaxDate
FROM Project_Status
Group by PRJSTAT_PRJA_ID)
On
PRJSTAT_PRJA_ID = MaxID and PRJSTAT_DATE = MaxDate
Order by PRJSTAT_PRJA_ID
It returns the following:
I am getting multiple records for PRJSTAT_PRJA_ID, but I only want to return the row with the MAX PRJSTAT_ID. Any thoughts?
Take out the MAX on the ID on the subquery:
SELECT PRJSTAT_ID, PRJSTAT_PRJA_ID, PRJSTAT_STATUS, PRJSTAT_DATE
From Project_Status
JOIN
(SELECT PRJSTAT_PRJA_ID as ID, MAX(PRJSTAT_DATE) as MaxDate
FROM Project_Status
Group by PRJSTAT_PRJA_ID)
On
PRJSTAT_PRJA_ID = ID and PRJSTAT_DATE = MaxDate
Order by PRJSTAT_PRJA_ID
Or remove the need to join:
SELECT * FROM
(SELECT PRJSTAT_ID, PRJSTAT_PRJA_ID, PRJSTAT_STATUS, PRJSTAT_DATE,
ROW_NUMBER() OVER (PARTITION BY PRJSTAT_PRJA_ID ORDER BY PRJSTAT_DATE DESC)
AS SEQ,
ROW_NUMBER() OVER (PARTITION BY PRJSTAT_PRJA_ID ORDER BY PRJSTAT_PRJA_ID
DESC) AS IDSEQ
From Project_Status
)PR
WHERE SEQ = 1
AND IDSEQ = 1
Your problem is ties. You want the record with the maximum date per PRJSTAT_PRJA_ID and in case of a tie the record with the highest ID. The easiest way to rank records per group and only keep the best record is ROW_NUMBER:
select prjstat_id, prjstat_prja_id, prjstat_status, prjstat_date
from
(
select
project_status.*,
row_number() over (partition by prjstat_prja_id
order by prjstat_date desc, prjstat_id desc) as rn
from project_status
)
where rn = 1
order by prjstat_prja_id;

SQL return all fields from max of count

I have tried but just cannot get the results so I'm asking for help here. I have a table of logins for users on computers. I want to count the number of times a user has logged into a computer and then get the max so that max user is considered the primary user of the computer.
Depending on what dbms you are using, you can use a window function ROW_NUMBER() to rank the counts per comp. Then pull the highest ranking.
SELECT
Comp,
[User],
Cnt
FROM
(SELECT
Comp,
[User],
COUNT(1) AS Cnt,
ROW_NUMBER() OVER (PARTITION BY Comp ORDER BY Count(1) DESC) AS Rnk
FROM UserHist
GROUP BY Comp, [User]) BaseTable
WHERE Rnk = 1
You can RANK the counts and then select the largest. This uses a CTE to get the data with the count and the rank.
WITH CTECOMP AS
(
SELECT COMP, [user], COUNT([USER]) [USERCOUNT],
RANK() OVER (PARTITION BY COMP ORDER BY COUNT([USER]) DESC ) [MYRANK]
FROM #TableData
GROUP BY comp, [user]
)
SELECT *
FROM CTECOMP
WHERE MYRANK = 1
select Comp, [User], count([User]) as MaxCnt
from TableHere
group by Comp, [User]