I have the following table:
ID Date FirstName Dept
1 1/2/12 James Act
1 2/5/12 Mike IT
2 5/6/12 Joe HR
2 7/6/12 Keith IT
What I need to do that for each ID, I need to get the max date.
I need to show ID, Date, FirstName, Dept for the record for each ID that has the Max Date.
So in this case for ID of 1, I would show 1 2/5/12 Mike IT
How do I do this in SQL Server T-SQL?
I know I need to do group by.
The table name is TblAct
You will use the MAX() function with a GROUP BY
select t1.id, t1.date, t1.fname, t1.dept
from tblAct t1
inner join
(
SELECT Max(Date) maxdate, ID
from TblAct
GROUP BY id
) t2
on t1.id = t2.id
and t1.date = t2.maxdate
See SQL Fiddle with Demo
You can do this with windows/ranking functions:
select ID, Date, FirstName, Dept
from (select t.*,
row_number() over (partition by id order by date desc) as seqnum
from t
) t
where seqnum = 1
This is ordering all the rows for each id by date, in reverse order. It then selects the first of them.
dont use group by :
select * from tblAct t1
where date=(select max(date) from tblAct where t1.id = id)
just enjoy.
Related
I want to keep the highest report id (Report_ID) for every type (Types) for every single date (Date)
Note: The data column has multiple dates, only 01.01.2021 is shown below.
Question: t1 is the lookup table that I need to use and my challenge is that it does not contain a date column for reference.
select t2.*
from t2
where t1.Report_ID = (select max(t1.Report_ID)
from t1
where t2.Date = ??? and t2.Types = ???
);
t1
Report_ID
Name
Value
1
Name 1
Value 1
2
Name 2
Value 2
3
Name 3
Value 3
t2
Date
Types
Report_ID
Name
01.01.2020
Type 1
1
Name 1
01.01.2020
Type 1
2
Name 2
01.01.2020
Type 3
3
Name 3
view
Date
Types
Name
Value
Report_ID
01.01.2020
Type 1
Name 2
Value 2
2
01.01.2020
Type 3
Name 3
Value 3
3
With this query:
SELECT Date, Types, MAX(Report_ID) Report_ID
FROM t2
GROUP BY Date, Types
you get the max Report_ID for each Date and Types
Join it to t1:
SELECT t2.Date, t2.Types, t1.Name, t1.Value, t1.Report_ID
FROM t1
INNER JOIN (
SELECT Date, Types, MAX(Report_ID) Report_ID
FROM t2
GROUP BY Date, Types
) t2 ON t2.Report_ID = t1.Report_ID
See the demo.
Results:
Date
Types
Name
Value
Report_ID
2020-01-01
Type 1
Name 2
Value 2
2
2020-01-01
Type 3
Name 3
Value 3
3
Using ROW_NUMBER():
WITH cte AS (
SELECT t2.*, t1.Value,
ROW_NUMBER() OVER(PARTITION BY `Date`, Types ORDER BY Report_ID DESC) AS rn
FROM t2
JOIN t1 ON t1.Report_ID = t2.Report_ID
)
SELECT * FROM cte WHERE rn = 1;
db<>fiddle demo
You can use NOT EXISTS as follows:
select t2.*
from t2
--join t1 on t1.Report_ID = t2.Report_ID -- use it if you want data from t1 in SELECT
where not exists
(select 1 from t2 t22
where t22.date = t2.date and t22.type = t2.type
and t22.Report_ID > t2.Report_ID)
This answers the original version of the question.
I want to keep the highest report id (Report_ID) for every type (Types) for every single date (Date)
The reference table is not needed for this. Your logic should do what you want with t2 in the subquery:
select t2.*
from t2
where t2.Report_ID = (select max(tt2.Report_ID)
from t2 tt2
where tt2.Date = t2.date and tt2.Type = t2.Type
);
You can easily achieve that through row_number() and CTE. First we need to join t1 and t2 to get the value column from t1. We used row_number() to put a sequence number in every row starting from highest Report_ID to lowest for a particular type in a given date.
Then we only consider the rows with lowest sequence number which represents highest report_id for any particular type of a given da.
With cte as
(
select t2.date,t2.types,t2.report_id,t2.name ,t1.value ,row_number () over (partition by date,types order by t2.report_id desc) RowNumber
from t2 inner join t1 on t2.report_id=t1.report_id
)
select date_format(date,"%Y.%m.%d") date,types,name,value,report_id from cte where RowNumber=1
Output:
I have a table as follows and what I want is to use get the initial row with least id of each uid group.
The table is as follows
_id uid type
1 a a
2 b bbb #satisfied
3 b ccc
4 b aaa #satisfied
5 a aaa #satisfied
6 b eee
I can already get the initial row using the following correlated subquery
SELECT *
FROM table
WHERE _id IN (
SELECT MIN(_id)
FROM table
WHERE type IN ('aaa','bbb')
GROUP BY uid
);
However, I want the 4th column shown the count of rows satisfied the condition (type IN ('aaa','bbb')), as cnt shown below:
_id uid type cnt
5 a aaa 1
2 b bbb 2
I think I can count this use several joins and then join the result to my code...But this is ugly...Is there any elegant way to achieve this...
You can try this:
SELECT t1.*, t2.cnt
FROM table t1 INNER JOIN (
SELECT MIN(_id) AS id, COUNT(_id) AS cnt
FROM table
WHERE type IN ('aaa','bbb')
GROUP BY uid
) t2 ON t1._id = t2.id
ORDER BY t1.uid
If you are running MySQL 8.0, you can just use window functions for this:
select _id, uid, type, cnt
from (
select
t.*,
count(*) over(partition by uid) cnt,
row_number() over(partition by uid order by _id) rn
from mytable t
where type in ('aaa', 'bbb')
) t
where rn = 1
You can do this without a subquery. In MySQL 8+, you can use this logic:
SELECT DISTINCT MIN(_id) OVER (PARTITION BY uid) as _id,
uid,
FIRST_VALUE(type) OVER (PARTITION BY uid ORDER BY _id) as type,
COUNT(*) OVER (PARTITION BY uid) as cnt
FROM table
WHERE type IN ('aaa', 'bbb');
Unfortunately, MySQL doesn't have a "first" aggregation function, but there is a trick if you like:
SELECT MIN(_id) as _id, uid,
SUBSTRING_INDEX(GROUP_CONCAT(type ORDER BY _id), ',', 1) as type,
COUNT(*) as cnt
FROM table
WHERE type IN ('aaa', 'bbb')
GROUP BY uid;
Sorry for the poor formatting but as part of a larger problem, I have created a query that produces this table:
id id2
4 7
4 6
1 3
1 2
1 1
How would I extract the rows that don't have the highest id2 for each id1.
What I want:
id id2
4 6
1 2
1 1
I can only seem to figure out how to get rid of the max id2 overall but not for each distinct id1. Any help on actually differentiating the max id2 for each id1 would be appreciated.
You can try below way -
select a.id, a.id2
from tablename a
where a.id2 <> (select max(a1.id2) from tablename a1 where a.id=a1.id)
If you are using MySQL 8+, then RANK() provides one option:
WITH cte AS (
SELECT id, id2, RANK() OVER (PARTITION BY id ORDER BY id2 DESC) rnk
FROM yourTable
)
SELECT id, id2
FROM cte
WHERE rnk > 1
ORDER BY id DESC, id2 DESC;
Demo
instead of a correlated subquery in the where, you can LEFT JOIN and apply not in...
select id, id2
from yourTable YT
LEFT JOIN
( select id, max( id2 ) highestID2
from YourTable
group by id ) TopPerID
on YT.ID = TopPerID.ID
AND YT.ID2 != TopPerID.highestID2
where TopPerID.id IS NULL
Since you can have id values with only one id2 value, you need to check for that situation as well, which you can do by comparing the MAX(id2) value with the MIN(id2) value in a JOIN:
SELECT t1.*
FROM Table1 t1
JOIN (SELECT id, MAX(id2) AS max_id2, MIN(id2) AS min_id2
FROM Table1
GROUP BY id) t2 ON t2.id = t1.id
AND (t1.id2 < t2.max_id2 OR t2.min_id2 = t2.max_id2)
If we add a row 2, 5 to your sample data this correctly gives the result as
id id2
4 6
1 2
1 1
2 5
Demo on SQLFiddle
having a list of people like:
name date_of_birth
john 1987-09-08
maria 1987-09-08
samuel 1987-09-09
claire 1987-09-10
jane 1987-09-10
rose 1987-09-12
...
How can I get a result view using SQL of how many people are born up to that date, like the output for that table should be:
date count
1987-09-08 2
1987-09-09 3
1987-09-10 5
1987-09-11 5
1987-09-12 6
...
Thanks!
Here is another way, in addition to Gordon's answer. It uses joins:
SELECT
t1.date_of_birth,
COUNT(*) AS count
FROM (SELECT DISTINCT date_of_birth FROM yourTable) t1
INNER JOIN yourTable t2
ON t1.date_of_birth >= t2.date_of_birth
GROUP BY
t1.date_of_birth;
Note: I left out a step. Apparently you also want to report missing dates. If so, then you may replace what I aliased as t1 with a calendar table. For the sake of demonstration, you can inline all the dates:
SELECT
t1.date_of_birth,
COUNT(*) AS count
FROM
(
SELECT '1987-09-08' AS date_of_birth UNION ALL
SELECT '1987-09-09' UNION ALL
SELECT '1987-09-10' UNION ALL
SELECT '1987-09-11' UNION ALL
SELECT '1987-09-12'
) t1
LEFT JOIN yourTable t2
ON t1.date_of_birth >= t2.date_of_birth
GROUP BY
t1.date_of_birth;
Demo
In practice, your calendar table would be a bona fide table which just contains all the dates you want to appear in your result set.
One method is a correlated subquery:
select dob.date_of_birth,
(select count(*) from t where t.date_of_birth <= dob.date_of_birth) as running_count
from (select distinct date_of_birth from t) dob;
This is not particularly efficient. If your data has any size, variables are better (or window functions if you are using MySQL 8.0):
select date_of_birth,
(#x := #x + cnt) as running_count
from (select date_of_birth, count(*) as cnt
from t
group by date_of_birth
order by date_of_birth
) dob cross join
(select #x := 0) params;
Use subquery with correlation approach :
select date_of_birth, (select count(*)
from table
where date_of_birth <= t.date_of_birth
) as count
from table t
group by date_of_birth;
SELECT id, count(*) as Number
FROM (SELECT id FROM t1
UNION ALL
SELECT id FROM t2
UNION ALL
SELECT id FROM t3
) t
GROUP BY id
ORDER BY Number DESC
This is the query giving me the correct result. But When I want to add where call it is throwing error.
SELECT id, count(*) as Number
FROM (SELECT id from t1
UNION ALL
select id from t2
UNION ALL
select id from t3
) t
WHERE Number > 10
GROUP BY id
ORDER BY Number DESC
You want to test conditions on an aggregate function with a HAVING clause rather than a WHERE.
select id, count(*) as Number
from (select id
from t1
UNION ALL
select id
from t2
UNION ALL
select id
from t3) t
group by id
having Number > 10
order by Number desc;
select
id,
count(*) as Number
from
(
select
id
from
t1
UNION ALL
select
id
from
t2
UNION ALL
select
id
from
t3
)t
group by
id
HAVING
Number > 10
order by
Number desc;
Try that - I think its easier to debug if you make your query easy to read...