Select all Persons that NOT continuously present in SQL - 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;

Related

SELECT salesman who had sales in particular years POSTGRES

I want to find a salesman who had sales in 1998, 1999 and 2001
For example:
**year salesmen**
1998 a
1998 a
1998 b
1999 a
1999 b
1999 c
2001 a
2001 b
2001 c
result should be
"a" and "b" because only those salesman had sales in all years
I don't think it requires complex query but I could not come up with solution
Aggregation is one option:
SELECT salesmen
FROM yourTable
WHERE year IN (1998, 1999, 2001)
GROUP BY salesmen
HAVING COUNT(DISTINCT year) = 3;
This query shows salesmen that had sales in all 3 years(1998,1999,2001) at least once with only 1 read on the table:
select salesmen
from your_table
group by salesmen having count(case when year = 1998 then 1 end)>0 and
count(case when year = 1999 then 1 end)>0 and
count(case when year = 2001 then 1 end)>0
DB<>FIDDLE LINK
There are many options to get the desired result. One is using HAVING and COUNT or GROUP BY (there are already answers that show this way). I want to show the option to do not query harcoded the 3 years, but to do this instead:
SELECT salesmen
FROM yourTable
WHERE year IN (SELECT year FROM yourtable)
GROUP BY salesmen
HAVING COUNT(DISTINCT year) = (SELECT COUNT(DISTINCT year) FROM yourtable);
If this is intended, this will be the way to select those salesmen that occur in every year that appears in your table.
To come back to your question, another possibility is to use IN:
SELECT DISTINCT salesmen FROM yourtable
WHERE salesmen IN
(SELECT salesmen FROM yourtable WHERE year = 1998) AND salesmen IN
(SELECT salesmen FROM yourtable WHERE year = 1999) AND salesmen IN
(SELECT salesmen FROM yourtable WHERE year = 2001);
You can also use EXISTS:
SELECT DISTINCT salesmen FROM yourtable y
WHERE
EXISTS (SELECT 1 FROM yourtable WHERE year = 1998 AND salesmen = y.salesmen) AND
EXISTS (SELECT 1 FROM yourtable WHERE year = 1999 AND salesmen = y.salesmen) AND
EXISTS (SELECT 1 FROM yourtable WHERE year = 2001 AND salesmen = y.salesmen);
You can see the difference here and try out: db<>fiddle

Query to compare multiple records for a person

I need to write a query to find all records that have the value C in the Type column AND it is not the latest date. For example, in Table1 the value C should be the latest date, but John Smith also had type B and A that come after 2014-02-01. Joana and Brian would both not appear in the results.
Person:
FirstName LastName DOB Type Date
John Smith 01/01/1992 C 2014-02-01
John Smith 01/01/1992 B 2014-05-01
John Smith 01/01/1992 A 2014-04-01
Joana Doe 05/14/1971 A 2014-07-01
Joana Doe 05/14/1971 C 2014-09-01
Brian Holden 12/01/1992 A 2014-08-01
I am at a total loss on what to even do. Here is what I was thinking:
SELECT FirstName, LastName, DOB
FROM Table1
GROUP BY FirstName, LastName, DOB
HAVING COUNT(LastName) > 1 AND Type='C'
Any tips on what I can try would be helpful!
select firstname, lastname, dob from table1 left join
(select max(date) as maxdate from table1) as m on table1.date=m.maxdate
where table1.Type='C' and m.maxdate is null;
The left join with where clause requiring null in second table's join field is a pattern that finds all in first table that do not have, in this case, the maximum date.
You can use row_number() for this:
select p.*
from (select p.*, row_number() over (partition by firstname, lastname, dob order by date desc) as seqnum
from person
where type = 'C'
) p
where seqnum > 1;
EDIT:
I misread the question, I think. You want records that come after the latest C date. The idea is similar, using max() instead of row_number():
select p.*
from (select p.*,
max(case when type = 'C' then date end) over (partition by firstname, lastname, dob) as maxcdate
from person
where type = 'C'
) p
where date > maxcdate and type <> 'C';
You can use exists to check for another row with a greater date
select FirstName, LastName, DOB
from Table1 t1
where Type = 'C'
and exists (
select 1 from Table1 t2
where t2.FirstName = t1.FirstName
and t2.LastName = t1.LastName
and t2.DOB = t1.DOB
and t2.Date > t1.Date
)
Or you could modify your original group by query
SELECT FirstName, LastName, DOB
FROM Table1
GROUP BY FirstName, LastName, DOB
HAVING MAX(Date) > MAX(CASE WHEN Type = 'C' THEN Date END)

How to perform SQL Query to get last entry

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

Selecting records with most recent date for each 'order'

I have information in the format of the sample table below. Each file can have multiple grades, I need to select the most recent grade (based on completion date) for each file. If there is a file w/ the same completion dates, I would select the best grade (a being best and subsequent letters being a lesser grade). This seems easy, but for some reason having a brain fart
Sample Table:
ID_PK File_No Grade Completion_Date
1 Smith A 10/1/2010
2 Smith C 9/25/2010
3 Davis B 11/1/2010
4 Johnson D 12/5/2010
5 Johnson A 11/1/2010
6 Johnson C 10/1/2010
7 Miller X 9/1/2010
8 Miller F 12/1/2010
9 Miller D 10/1/2010
Ideal Results:
1 Smith A 10/1/2010
3 Davis B 11/1/2010
4 Johnson D 12/5/2010
8 Miller F 12/1/2010
uSING WINDOWING FUNCTION IS MORE EFFICIENT and also simpler as
with cte AS(
select '1' AS ID_no,'Smith' AS FILE_NO,'A' AS GRADE,
CAST('10/1/2010' AS DATE) AS CREATION_DATE
union all
select '2','Smith','C','9/25/2010'
union all
select '3','Davis','B','11/1/2010'
union all
select '4','Johnson','D','12/5/2010'
union all
select '5','Johnson','A','11/1/2010'
union all
select '6','Johnson','C','10/1/2010'
union all
select '7','Miller','X','9/1/2010'
union all
select '8','Miller','F','12/1/2010'
union all
select '9','Miller','D','10/1/2010')
SELECT X.ID_NO,X.FILE_NO,X.GRADE,X.CREATION_DATE FROM(
SELECT ID_NO,FILE_NO,GRADE,CREATION_DATE ,
ROW_NUMBER() OVER(PARTITION BY FILE_NO ORDER BY CREATION_DATE DESC,GRADE ASC ) AS RN
FROM CTE)AS X
WHERE X.RN=1
ORDER BY ID_NO
try this (untested):
select max_grade.*
from `Sample Table` st
inner join (
select File_No, max(Completion_Date) as Completion_Date
from `Sample Table`
group by File_No
) max_date on st.Completion_Date = max_date.CompletionDate
inner join (
select File_No, Completion_Date, max(Grade) as Grade
from `Sample Table`
group by File_No, Completion_Date
) max_grade on st.File_No = max_grade.File_No and st.Completion_Date = max_grade.Completion_Date
Note that you may need to modify the syntax and table name for your particular DB.
I created a table with your example data. I tested the following query against the table and everything seem to work correctly and matched the example results.
SELECT
ID_PK,
StudentGrade.File_No,
MIN(StudentGrade.Grade),
StudentGrade.Completion_Date
FROM
(
SELECT File_No, MAX(Completion_Date) Completion_Date
FROM StudentGrade
GROUP BY File_No
) Student
INNER JOIN StudentGrade ON
Student.File_No = StudentGrade.File_No
AND StudentGrade.Completion_Date = Student.Completion_Date
GROUP BY ID_PK, StudentGrade.File_No, StudentGrade.Completion_Date
ORDER BY ID_PK

Group by SQL with count

Lets say we have got rows like that:
MyTable
ID Name Product
------------------
1 Adam x
2 Adam y
3 Adam z
4 Peter a
5 Peter b
Using query like:
Select Name, Count(Product) from MyTable
group by Name
results will be:
Adam 3
Peter 2
But I would like results like:
1 Adam x 3
2 Adam y 3
3 Adam z 3
4 Peter a 2
5 Peter b 2
I hope Ypu know what I mean
Could You help me with that query,
thanks for help,
Bye
You can join the table with a subquery run on the table to select the counts:
SELECT a.ID as ID, a.Name as Name, a.Product as Product, ISNULL(b.cnt,0) as Cnt
FROM MyTable a
LEFT JOIN (SELECT Name, COUNT(*) as Cnt FROM MyTable GROUP BY Name) b
ON a.Name = b.Name
How about?
Select *, Count() OVER(PARTITION BY Name) As C
from MyTable
Select a.Id,
a.Name,
a.Product,
IsNull(b.CountOfUsers,0) as CountOfUsers
From MyTable a
Left Join (Select Name, Count(Product) as CountOfUsers from MyTable
group by Name)b on a.Name = b.Name
;WITH c AS (SELECT Name, COUNT(Product) CountOfProduct
FROM MyTable
GROUP BY Name)
SELECT t.Id, t.Name, t.Product, c.CountOfProduct
FROM MyTable t
INNER JOIN c ON c.Name = t.Name
Use:
SELECT x.id,
x.name,
x.product,
COALESCE(y.name_count, 0) AS num_instances
FROM MyTable x
LEFT JOIN (SELECT t.name,
COUNT(*) AS name_count
FROM MyTable t
GROUP BY t.name) y ON y.name = x.name
COALESCE is the ANSI standard means of handling NULL values, and is supported by MySQL, SQL Server, Oracle, Postgre, etc.