How to select the latter row in SQL - sql

I have a result set that looks like this:
As you can see some of the contactID are repeated with same QuestionResponse. And there is one with a different QuestionResponse (the one with red lines).
I want to group this by ContactID, but select the latter row. Eg: In case of ContactID = 78100299, I want to select the row with CreateDate = 17:00:44.907 (or rowNum = 2).
I have tried this:
select
ContactID,
max(QuestionResponse) as QuestionResponse,
max(CreateDate) as CreateDate
from
theResultSet
group by
ContactID
This will NOT work because there could be QuestionResponse 2 and then 1 for the same contactID. In that case the latter one will be the one with response 1 not 2.
Thank you for you help.

I would use ROW_NUMBER() that way:
WITH Query AS
(
SELECT rowNum, ContactID, QuestionResponse, CreateDate,
ROW_NUMBER() OVER (PARTITION BY ContactID ORDER BY CreateDate DESC) Ordered
FROM theResultSet
)
SELECT * FROM Query WHERE Ordered=1
Assign numbers in ContactID group by date, descending
Filter results having number <> 1

This might work if your SQL Engine can handle it...
SELECT trs1.*
FROM theResultSet trs1
INNER JOIN
(SELECT ContactID, max(CreateDate) as CreateDate
FROM theResultSet
GROUP BY ContactID) trs2
ON trs1.ContactID = trs2.ContactID
AND trs1.CreateDate = trs2.CreateDate
The end result will be all rows from theResultSet where the creation date is the max creation date.

This should work too:
SELECT
ContactID, QuestionResponse,CreateDate
FROM (
select rowNum, ContactID, QuestionResponse,CreateDate,
max(rowNum) over(partition by ContactID) as maxrow
from theResultSet
) x
WHERE rowNum=maxrow

Related

How to find records which is unique by TypeOfCertifate and latest by CertificateStartDate?

Original Records
I need highlighted records in output result
I have filtered this result by this SQL Query
select *
from tbl_Certificate
where StoreNumber = 's28433'
group by Id, StoreName, StoreNumber, TypeOfCertificate, CertificateStartDate, CertificateExpiryDate, Comments, VisibleToStore, AdminFilePath, StoreFilePath, NoExpiryDate, CreatedBy, CreatedDate, ModifiedBy, ModifiedDate order by CertificateStartDate DESC
You could try something like this
;with
earliest_cte as (
select *, row_number() over (partition by TypeOfCertificate
order by CertificateStartDate) rn
from tbl_Certificate
where StoreNumber = 's28433')
select *
from earliest_cte
where rn=1
order by CertificateStartDate;

Turn these temp tables into one longer subquery (can't use Temp tables in Power BI)

Currently I have created these temp tables to get the desired output I need. However, Power BI doesn't allow the use of temp tables so I need to get this all into 1 query using inner selects.
drop table if exists #RowNumber
Select Date, ID, ListID
, row_number() over (partition by ID order by ID) as rownum
into #RowNumber
from Table
where Date= cast(getdate()-1 as date)
group by Date, ID, ListID
order by ID
drop table if exists #1stListIDs
select ListID as FirstID, ID, Date
into #1stListIDs
from #RowNumber
where rownum = 1
drop table if exists #2ndlistids
Select ListID as SecondListID, ID, Date
into #2ndlistids
from #RowNumber
where rownum = 2
--Joins the Two Tables back together to allow the listids to be in the same row
drop table if exists #FinalTableWithTwoListIDs
select b.FirstListID, a.SecondListID, a.ID, a.Date
into #FinalTableWithTwoListIDs
from #2ndlistids a
join #1stListIDs b on a.ID= b.ID
order by ID
This code is simple and straight forward. However I can't seem to figure out using a subquery. Here is what I have. It works for the FirstListID select statement, but not the SecondListID portion. I believe this is because you can't reference the inner most select statement with multiple different outer select statements, but I could be wrong.
Select a.ListId as SecondListID, a.ID, a.Date
from (
select a.ListId as FirstListID, a.ID, a.Date
from (
Select Date, ID, ListId
, row_number() over (partition by ID order by ID) as rownum
from Table
where Date = cast(getdate()-1 as date)
group by Date, ID, ListId
order by ID) a
where a.rownum = 1) b
where a.rownum = 2) c
Just to show, for completeness, how you could use CTEs to replace the #temp tables, it would be something along the lines of
with RowNumber as (
select Date, ID, ListID
, row_number() over (partition by ID order by ID) as rownum
from Table
where Date= cast(dateadd(day,-1,getdate()) as date)
group by Date, ID, ListID
),
FirstListIDs as (
select ListID as FirstID, ID, Date
from RowNumber
where rownum = 1
),
SecondListIDs as (
select ListID as SecondID, ID, Date
from RowNumber
where rownum = 2
)
select f.FirstID, s.SecondID, s.ID, s.Date
from Secondlistids s
join FirstListIDs f on s.ID=f.ID
order by s.ID
Note the use of dateadd which is recommended over the ambiguousdate +/- value assumed to be days, and where relevant meaningful table aliases.
You could do it with a CTE and joining the two together, but that is inefficient and unnecessary.
It looks like you just need LAG to get the previous ListID
I note that PARTITION BY ID ORDER BY ID is non-deterministic and the ordering will be random. I strongly suggest you find a deterministic ordering.
SELECT
PrevID AS b.FirstListID,
ListID AS a.SecondListID,
ID,
Date
FROM (
SELECT
Date,
ID,
ListID,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) AS rownum,
LAG(ListID) OVER (PARTITION BY ID ORDER BY ID) AS PrevID
from [Table]
where Date = cast(getdate() - 1 as date)
group by Date, ID, ListID
) AS WithRowAndLag
WHERE rownum = 2;
ORDER BY ID;

Select Person with only one value in the column

I can't correctly write a query.
I need to select a person who has only one value in the column. For example,
select * (
select PersonID, sum(TotalAmount)
from Table1
group by PersonID
HAVING sum(TotalAmount) = 0 )
where Group = A
It means that I would select all customers that belong to ONLY 'A' group...
Could someone help me?
If you want the persons with only one value, then having count(*) = 1 comes to mind:
select personid
from table1
group by personid
having count(*) = 1;

SQL Server Group By with Max on Date field

I hope i can explain the issue i'm having and hopefully so can point me in the same direction.
I'm trying to do a group by (Email Address) on a subset of data, then i'm using a max() on a date field but because of different values in other fields its bring back more rows then require.
I would just like to return the max record per email address and return the fields that are on the same row that are on the max record.
Not sure how i can write this query?
This is a task for ROW_NUMBER:
select *
from
(
select t.*,
-- assign sequential number starting with 1 for the maximum date
row_number() over (partiton by email_address order by datecol desc) as rn
from tab
) as dt
where rn = 1 -- only return the latest row
You can write this query using row_number():
select t.*
from (select t.*,
row_number() over (partition by emailaddress order by date desc) as seqnum
from t
) t
where seqnum = 1;
How about something like this?
select a.*
from baseTable as a
inner join
(select Email,
Max(EmailDate) as EmailDate
from baseTable
group by Email) as b
on a.Email = b.Email
and a.EmailDate = b.EmailDate

SQL for counting rows and categorize

Is it possible to do the following for count >=3,4,5,6,7,8 etc.
rather than repeating the entire code for each count category
Insert into OnePlus (SELECT DISTINCT Id, Name, COUNT(DISTINCT StartDate) AS OnePlusDays
FROM DataTable
HAVING OnePlusDays >= 1
GROUP BY Id, Name)
Insert into TwoPlus (SELECT DISTINCT Id, Name, COUNT(DISTINCT StartDate) AS TwoPlusDays
FROM DataTable
HAVING TwoPlusDays >= 2
GROUP BY Id, Name)
Finally
SELECT Id, Name, "1+" AS Categories
FROM OnePlus
UNION
SELECT Id, Name, "2+" AS Categories
FROM TwoPlus
You mention only sql in the tags. Depending on MySql or SQL Server, you may need to change the Cast/Convert and Concatenation. But this query may help. You really don't need to put a Distinct on top a group by, the fact that you are grouping by, means only distinct values and their counts will be fetched.
Of course, the table OnePlus, is really what you call Categories.
Insert into OnePlus
SELECT Id, Name, convert(varchar(10), COUNT(DISTINCT StartDate) ) + "+" AS Categories
FROM DataTable
GROUP BY Id, Name
In T-SQL you can write as:
SELECT Id,
NAME , -- make sure you write case statement in desc order
CASE WHEN PlusDays > = 2 THEN '2+'
WHEN PlusDays > = 1 THEN '1+' END AS Categories
FROM
(
SELECT DISTINCT Id, Name, COUNT(DISTINCT StartDate) PlusDays
FROM #DataTable
GROUP BY Id, Name
) AS T
ORDER BY Id asc