How to perform SQL Query to get last entry - sql

I am working on creating a SQL query where the result will return a student test score
from the last test that they took. I think that this should be fairly simple but I am just not seeing it.
Here is my test data
Name Date Score
John 2/3/2012 94
John 2/14/2012 82
John 2/28/2012 72
Mary 2/3/2012 80
Mary 2/28/2012 71
Ken 2/14/2012 68
Ken 2/14/2012 66
I want the returned result to be
John 2/28/2012 72
Mary 2/28/2012 80
Ken 2/14/2012 66
I appreciate any assistance.

select date, name, score
from temp t1
where date = (select max(date) from temp where t1.name = temp.name)
OR
SELECT a.*
FROM temp a
INNER JOIN
(
SELECT name,MAX(date) as max_date
FROM temp a
GROUP BY name
)b ON (b.name = a.name AND a.date=b.max_date)
Here is a sql fiddle with an example
or even this if you have more than one record for each person on a date like you show in your sample data.
SELECT c.name,c.date, MAX(c.score) as max_score
FROM
(
SELECT a.*
FROM temp a
INNER JOIN
(
SELECT name,MAX(date) as max_date
FROM temp a
GROUP BY name
)b ON (b.name = a.name AND a.date=b.max_date)
)c
group by c.name,c.date
Sql fiddle with this example

SELECT Name, Date, Score
FROM tablename t1
WHERE Date = (SELECT MAX(Date)
FROM tablename
WHERE Name = t1.Name
GROUP BY Name)

Which database are you using? Most support row_number() which is the right way to answer this:
select *
from
(
select t.*, row_number() over (partition by name order by date desc) as seqnum
from table t
)
where rownum = 1

Related

Select the duplicate rows with specific values

How can I only get the data with the same ID, but not the same Name?
The following is the example to explain my thought. Thanks.
ID Name Date
123 Amy 08/03/2022
123 Amy 12/03/2022
456 Billy 08/03/2022
456 Cat 09/03/2022
789 Peter 10/03/2022
Expected Output:
ID Name Date
456 Billy 08/03/2022
456 Cat 09/03/2022
How I have done.
select ID, Name, count(*)
from table
groupby ID, Name
having count(*) > 1
But the result included the following parts that I do not want it.
ID Name Date
123 Amy 08/03/2022
123 Amy 12/03/2022
One approach would be to use a subquery to identify IDs that have multiple names.
SELECT *
FROM YourTable
WHERE ID IN (SELECT ID FROM YourTable GROUP BY ID HAVING COUNT(DISTINCT Name) > 1)
I'd join the table to its self like this:
SELECT DISTINCT
a.Id as ID_A,
b.Id as ID_B,
a.[Name] as Name_A
FROM
Test as a
INNER JOIN Test as b
ON A.Id = B.Id
WHERE
A.[Name] <> B.[Name]
Do you want
SELECT * FROM table_name
WHERE ID = 456;
or
SELECT * FROM table_name
WHERE ID IN
(SELECT
ID
FROM table_name
GROUP BY ID
HAVING COUNT(DISTINCT name) > 1
);
?
Window functions are likely to be the most efficient here. They do not require self-joining of the source table.
Unfortunately, SQL Server does not support COUNT(DISTINCT as a window function. But we can simulate it by using DENSE_RANK and MAX
WITH DistinctRanks AS (
SELECT *,
rnk = DENSE_RANK(*) OVER (PARTITION BY ID ORDER BY Name)
FROM YourTable
),
MaxRanks AS (
SELECT *,
mr = MAX(rnk) OVER (PARTITION BY ID)
FROM DistinctRanks
)
SELECT
ID,
Name,
Count
FROM MaxRanks t
WHERE t.mr > 1;

Select all Persons that NOT continuously present in SQL

I have the table Persons with the following contents:
year name
2015 John
2016 John
2017 John
2015 Mary
2015 Jennifer
2016 Jennifer
2015 Lisa
2016 Lisa
2017 Lisa
How can I get all Persons that not continuously present in all the years?
The Answer should be:
2015 Mary
2015 Jennifer
2016 Jennifer
One approach would be to aggregate by name and then assert that a given name's distinct count of years does not equal the total distinct number of years in the entire table.
SELECT t1.name
FROM yourTable t1
INNER JOIN
(
SELECT name
FROM yourTable
GROUP BY name
HAVING COUNT(DISTINCT year) < (SELECT COUNT(DISTINCT year) FROM yourTable)
) t2
ON t1.name = t2.name
ORDER BY
t1.name,
t1.year;
Demo
You can use below query,
select * from persons p1
where exists
(select name, count(1) from persons p2 where p1.name=p2.name group by name having count(1) <
(select count(distinct year) from persons));
You can use below query:
SQLfiddle
select a.name, a.year1
from name_test a
inner join
(select name, count(year1)
from name_test
group by name
having count(year1) < (select count(distinct year1) from name_test))
b on (a.name = b.name);
I would suggest window functions. Assuming no duplicates per name:
select p.*
from (select p.*,
count(*) over (partition by name) as cnt_name,
count(distinct year) over () as cnt_years
from persons p
) p
where cnt_name <> cnt_years;

sql get duplicate column values grouped by another column

I've got the following temp table as an output from a query:
FacilityID UserID User_Name
1046 105 John Smith
1046 106 John Smith
1046 110 Jack Welsh
1091 107 Ana Romero
1091 248 Rebecca Cruz
1095 418 Alex Sterling
I need to display only these facilities that have users with the same name, and only these names should pass the query filter. This is to find out if any facility has users with exactly same name (even though these are different people). So, considering table above, I need to display only the following:
FacilityID UserID User_Name
1046 105 John Smith
1046 106 John Smith
I would use exists :
select t.*
from table t
where exists (select 1
from table t1
where t1.FacilityID = t.FacilityID and
t1.User_Name = t.User_Name and t.userid <> t1.userid
);
You can use exists:
select t.*
from t
where exists (select 1
from t t2
where t2.FacilityID = t.FacilityID and t2.user_name = t.user_name and
t2.UserId <> t.userId and
);
If you have a query returning results, then window functions are also a good choice:
with t as (<your query here>)
select t.*
from (select t.*, min(userid) over (partition by FacilityID, user_name) as min_ui,
max(userid) over (partition by FacilityID, user_name) as max_ui
from t
) t
where min_ui <> max_ui;
I would use the EXISTS clause:
(Example uses a CTE [TEMP] as a test)
;WITH TEMP (FacilityID, UserID, User_Name) AS (
SELECT * FROM (
VALUES
('1046','105','John Smith'),
('1046','106','John Smith'),
('1046','110','Jack Welsh'),
('1091','107','Ana Romero'),
('1091','248','Rebecca Cruz'),
('1095','418','Alex Sterling')
) AS A (Column1, Column2, Column3)
)
SELECT TEMP.*
FROM TEMP
WHERE EXISTS (SELECT 1
FROM TEMP SubT
WHERE SubT.FACILITYID = TEMP.FACILITYID
AND SubT.USER_NAME = TEMP.USER_NAME
AND TEMP.USERID <> SubT.USERID
)
I'll chip in my solution:
select FacilityID, UserID, User_Name from (
select FacilityID, UserID, User_Name
count(*) over (partition by User_Name) cnt
from MY_TABLE
) a where cnt > 1

SQL statement for average in specific row values group columns

I've my table like the format below, I want to write sql statement for display column average for test1 and test2, column for main and column for position.
I've tried this sql:
select name, average as final,AVG(case when exam in('test1','test2') ) as testa
from table
where exam='main'
order by main final
group by name;
But It Does not working....help please
I want results to appear like this:
You could do something like the following.
Select t1.name, average12, main, rownum from (
select t1.name as name, t1.avg(average) as average12, t2.mainexam as main
from <table> t1 left join <table> t2
on t1.id = t2.id
where t2.exam = 'Main'
group by t1.name, t2.mainexam
) order by main desc;
Try this query for SQL Server:
SELECT
e.name AS 'Student Name'
, CAST( AVG( CASE WHEN e.Exam IN ( 'Test1', 'Test2' ) THEN e.average END ) AS FLOAT ) AS 'Test Average'
, e2.average AS 'Main Exam'
, ROW_NUMBER() OVER ( ORDER BY e2.average DESC ) AS 'Position'
FROM
exams AS e
INNER JOIN exams AS e2
ON e2.name = e.name
AND e2.exam = 'Main'
GROUP BY
e.name
, e2.average
ORDER BY
e2.average DESC
Output is:
+--------------+--------------+-----------+----------+
| Student Name | Test Average | Main Exam | Position |
+--------------+--------------+-----------+----------+
| Name1 | 60.5 | 89 | 1 |
| Name2 | 49.5 | 63 | 2 |
| Name3 | 73 | 38 | 3 |
+--------------+--------------+-----------+----------+
This simple query averages Test1 and Test2. Depending on your dbms, you might have trouble with reserved words like name.
select name, cast(avg(average) as integer)
from test
where exam = 'Test1'
or exam = 'Test2'
group by name;
This simple query gives you the values for "main".
select name, average as main
from test
where exam = 'Main'
Put them together in a common table expression, and join on "name".
with test_avg as (
select name, cast(avg(average) as integer)
from test
where exam = 'Test1'
or exam = 'Test2'
group by name
), main_avg as (
select name, average as main
from test
where exam = 'Main'
)
select t1.name, t1.avg, t2.main
from test_avg t1
inner join main_avg t2
on t1.name = t2.name;
name avg main
--
Name1 61 89
Name2 50 63
Name3 73 38
I don't know what you mean by position. If you're trying to rank the rows from highest average to lowest average, you might do it like this.
with test_avg as (
select name, cast(avg(average) as integer)
from test
where exam = 'Test1'
or exam = 'Test2'
group by name
), main_avg as (
select name, average as main
from test
where exam = 'Main'
)
select t1.name, t1.avg, t2.main, row_number() over (order by avg desc) as position
from test_avg t1
inner join main_avg t2
on t1.name = t2.name
order by position;
name avg main position
--
Name3 73 38 1
Name1 61 89 2
Name2 50 63 3
You can play with the max here.... Its very simple
select a.*,row_number() over (order by main desc) as position from
(
select name,
AVG(case when exam in ('Test1','Test2') then Average else null end ) as testa,
max(case when exam in ('Main') then Average else null end) as main
from abc
group by name
) a
order by name;
Output :
name testa main position1
Name1 60.50 89 1
Name2 49.50 63 2
Name3 73.00 38 3

Query SQL distinct field with max date, returning all fields from table

I'm having a hard time trying to get a query that sounds simple. I'm using SQL Server 2008.
I have this table:
ID Name Date Zone
----------------------------
01 AAA 01/01/2010 North
02 BBB 02/02/2010 South
03 AAA 05/01/2010 West
So, I need to filter the table to get distinct items (by Name), but, in case the item is repeated, get the one with the max date. Here is the result that I wanted to get from the example above:
ID Name Date Zone
----------------------------
02 BBB 02/02/2010 South
03 AAA 05/01/2010 West
The problem is that I need to get all the fields from the table (I mean: Id, Name, Date and Zone should be returned from the query) once applied the filter.
Try this:
;WITH CTETable AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY Name ORDER BY [Date] DESC) ID
FROM YourTable
)
SELECT *
FROM CTETable
WHERE ID = 1
select
t.ID,
t.Name,
t.Date,
t.Zone
from
ThisTable t
inner join
(select
Name,
max(Date) as Date
from
ThisTable
group by
Name) x on x.Name = t.Name and x.Date = t.Date
SELECT a.columns
FROM table a
WHERE a.ID IN
(SELECT TOP 1 b.id FROM table b WHERE b.Name = a.Name ORDER BY b.Date DESC)
This will return the most recent row for each unique name.