Query to compare multiple records for a person - sql

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)

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;

SQL Select a value from the history table based on a date

I have two tables, which one table (table A) contains the users' payment data, and the other (Table B) contains the users' rank history.
Table A
IDNO LName FName Start_Date PayType Current_Rank
------------------------------------------------------------
SJ01 Smith John 11/13/2016 Cert AC
DJ01 Doe Jack 10/20/2020 Assignment BC
Table B
IDNO Date Rank
----------------------
SJ01 10/01/2010 CAP
SJ01 10/01/2016 BC
SJ01 10/01/2020 AC
DJ01 01/01/2010 LT
DJ01 01/01/2015 CAP
DJ01 01/01/2020 BC
I need to show the user's rank according to the start_date from the table A and bring in the rank from the table B. So my end result can look like this:
IDNO LName FName Start_Date PayType Rank
------------------------------------------------------------
SJ01 Smith John 11/13/2016 Cert BC
DJ01 Doe Jack 10/20/2020 Assignment BC
How can I join these two tables and compare the dates, so that I can bring in the rank from the history table based on the start_date from the table A?
Another very simple way is to just select the corresponding Rank directly using a correlated query
select a.*,
(select top(1) [rank] from TableB b
where b.idno=a.idno and a.start_date>b.date
order by b.date desc) as [Rank]
from TableA a
here is one way:
select a.* , c.Rank From TableA a
cross apply (select top 1 * from tableB b
where a.IdNo = b.IDno
and a.Start_Date > b.date
order by b.date desc
) c
;WITH CTE
AS
(
SELECT A.IDNO,A.LName,A.FName,A.Start_Date,A.PayType,B.Rank,RNo=ROW_NUMBER() OVER(PARTITION BY B.IDNO ORDER BY B.DATE DESC)
FROM Table_B B
JOIN Table_A A ON B.IDNO=A.IDNO AND A.Start_Date>=B.Date
) SELECT IDNO,LName,FName,Start_Date,PayType,Rank
FROM CTE WHERE RNo=1
ORDER BY Start_Date
select idno, lname, fname, start_date, paytype, pp.rank current_rank
from
(select xx.idno, xx.rank, min(xx.datediff) from
(select tableA.idno, tableA.start_date - tableB.date datediff, tableB.rank
from tableA, tableB
where tableA.idno = tableB.idno) XX
group by xx.idno, xx.rank)pp,
tableA
where tableA.idno = pp.idno

Procedure to copy data from a table to another table in SQL Server

I have a table A, with 4 columns:
first_name, invoice, value, date.
And a table B (first_name, max_invoice_name, max_invoice_value, last_date)
I want to create a procedure in order to move data from A, to B, but:
first_name should be one time in B,
max_invoice_name is the name of the max invoice value
max_invoice_value is the max value
last_date is the latest date from invoices from the same first_name.
For example:
TABLE A:
Smith | Invoice1 | 100 | 23.06.2016
John | Invoice13 | 23 | 18.07.2016
Smith | Invoice3 | 200 | 01.01.2015
Table B should be:
Smith |Invoice3 | 200 | 23.06.2016
John |Invoice13| 23 | 18.07.2016
Something like this should work:
select *, (select max(date) from #Table1 T1 where T1.first_name = X.first_name)
from (
select
*,
row_number() over (partition by first_name order by invoice_Value desc) as RN
from
#Table1
) X
where RN = 1
Row number takes care of selecting the row with biggest value, and the max get's the date. You'll need to list the columns in correct place instead of *
You will need to create 2 scalar functions getMaxNameForMaxValue AND getLastDateByFirstName to get the values you want.
INSERT INTO TableB (first_name, max_invoice_name, max_invoice_value, last_date) (SELECT DISTINCT first_name, getMaxNameForMaxValue(MAX(max_value)) AS 'max_invoice_name', MAX(max_invoice_value) AS 'max_invoice_value', getLastDateByFirstName(first_name) AS 'lastDate' FROM Table A)
You can use something like this:
--INSERT INTO TableB
SELECT first_name,
invoice_name,
invoice_value,
last_date
FROM (
SELECT a.first_name,
a.invoice_name,
a.invoice_value,
COALESCE(p.last_date,a.last_date) as last_date,
ROW_NUMBER() OVER (PARTITION BY a.first_name ORDER BY a.last_date) as rn
FROM TableA a
OUTER APPLY (SELECT TOP 1 * FROM TableA WHERE first_name = a.first_name and last_date > a.last_date) as p
) as res
WHERE rn = 1
As output:
first_name invoice_name invoice_value last_date
John Invoice13 23 2016-07-18
Smith Invoice3 200 2016-06-23
Try this
Insert into TableB(first_name, max_invoice_name, max_invoice_value, last_date)
select t1.first_name,t1.invoice,t1,value,t2.date from TableA as t1 inner join
(
select first_name, max(replace(invoice,'invoice','')) as invoice, max(date) as date
from TableA group by first_name
) as t2 on t1.first_name=t2.first_name and t1.invoice=t2.invoice

aggregate function, newest date

I have a table
date name
2014-01-01 AAA
2014-01-01 BBB
2014-01-01 CCC
2014-01-01 DDD
2015-05-05 AAA
2016-09-09 AAA
2016-09-09 BBB
and I want to have only the newest information about every person:
date name
2016-09-09 AAA
2016-09-09 BBB
2014-01-01 CCC
2014-01-01 DDD
I can simply write
SELECT MAX(date), name
FROM table1
GROUP BY name;
but if I want to add another column, it doesn't work
SELECT MAX(date), name, address
FROM table1
GROUP BY name;
(doesn't work)
SELECT MAX(date), name, address
FROM table1
GROUP BY name, address;
(it works, but I want to have only one record for one person)
How can I do it?
Assuming your dates for each name are unique, it's quite simple to solve using a derived table and join, like this:
SELECT maxdate, t.name, t.address
from table1 t
inner join
(
SELECT MAX(date) as maxdate, name
FROM table1
GROUP BY name
) d on(t.date = d.maxdate and t.name = d.name)
In SQL-Server you can use ROW_NUMBER() in following:
SELECT [date], name, address
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY name, address ORDER BY [date] DESC) rn
FROM Table1
)x
WHERE rn = 1
Try this
SELECT * FROM `table1` group by name order by name, date
Try using DISTINCT
SELECT MAX(date), distinct name
FROM table1
GROUP BY name;
may be this will work .

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