tsql group by get alphanumeric column value with maximum length - sql

I have a sql view, let's call it SampleView, whose results have the following format.
Id (INT), NameA (VARVHAR(50)), NameB (VARCHAR(50)), ValueA (INT), ValueB (INT)
The result set of the view contains rows that may have the same Id or not. When there are two or more rows with the same Id, I would like to get something like the following
SELECT
Id,
MAX(NameA),
MAX(NameB),
MAX(ValueA),
MAX(ValueB)
FROM SampleView
GROUP BY Id
ORDER BY Id
Regarding the columns Id, ValueA and ValueB there isn't any problem. On the other hand using MAX for both NameA and NameB things are not as expected. After some googling and searching I realized that MAX has not the "expected" behavior for alphanumeric columns. Saying the expected, I mean using MAX in my case, it would be to return the value of NameA with the maximum number of characters, MAX(LEN(NameA)). I have to mention here that there ins't any possibility for NameA to have two values for the same Id with the same length. This might makes the problem more easy to be solved.
I use SQL Server 2012 and TSQL.
Have you any suggestion on how I could deal with this problem?
Thank you very much in advance for any help.

You can use window functions:
SELECT DISTINCT
id,
FIRST_VALUE(NameA) OVER (PARTITION BY id
ORDER BY len(NameA) DESC) AS MaxNameA,
MAX(ValueA) OVER (PARTITION BY id) AS MaxValueA,
FIRST_VALUE(NameB) OVER (PARTITION BY id
ORDER BY len(NameB) DESC) AS MaxNameB,
MAX(ValueB) OVER (PARTITION BY id) AS MaxValueB
FROM SampleView
Demo here

You can use correlated queries like this:
SELECT
t.Id,
(SELECT TOP 1 s.NameA FROM SampleView s
WHERE s.id = t.id
ORDER BY length(s.NameA) DESC) as NameA,
(SELECT TOP 1 s.NameB FROM SampleView s
WHERE s.id = t.id
ORDER BY length(s.NameB) DESC) as NameB,
MAX(t.ValueA),
MAX(t.ValueB)
FROM SampleView t
GROUP BY t.Id
ORDER BY t.Id

One possible variant is to use ROW_NUMBER twice:
WITH
CTE_NameA
AS
(
SELECT
Id,
NameA,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY LEN(NameA) DESC) AS rnA
FROM SampleView
)
,CTE_NameB
AS
(
SELECT
Id,
NameB,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY LEN(NameB) DESC) AS rnB
FROM SampleView
)
SELECT
Id,
CTE_NameA.NameA,
CTE_NameB.NameB,
MAX(ValueA),
MAX(ValueB)
FROM
SampleView
INNER JOIN CTE_NameA ON CTE_NameA.Id = SampleView.Id AND CTE_NameA.rnA = 1
INNER JOIN CTE_NameB ON CTE_NameB.Id = SampleView.Id AND CTE_NameB.rnB = 1
GROUP BY Id
ORDER BY Id;

Related

checking if the values in one column are in order for the ID field using SQL

I want to choose one record per ID, based on the values in the sort field.
if there is a gap of more than 1 in the Sort filed I would want to assign the maximum sort value for that ID field. If the sort field is in order (eg for ID B the sort filed is 9,10, 11,12)
I would like to choose the minimum value for the ID.
enter image description here
Try this:
SELECT i.ID, MIN(i.sort) as sort
FROM input as i
INNER JOIN
(
Select ID, MAX(diff) as gap
FROM
(
SELECT ID, Sort, Rank,
LEAD(sort) OVER (PARTITION BY ID ORDER BY sort desc) as lead,
ABS(sort - LEAD(sort) OVER (PARTITION BY ID ORDER BY sort desc)) as diff
FROM input
) as a
WHERE diff IS NOT NULL
GROUP BY ID) as s
ON i.ID=s.ID
WHERE s.gap=1
GROUP BY i.ID
UNION
SELECT i.ID, MAX(i.sort) as sort
FROM input as i
INNER JOIN
(
Select ID, MAX(diff) as gap
FROM
(
SELECT ID, Sort, Rank,
LEAD(sort) OVER (PARTITION BY ID ORDER BY sort desc) as lead,
ABS(sort - LEAD(sort) OVER (PARTITION BY ID ORDER BY sort desc)) as diff
FROM input
) as a
WHERE diff IS NOT NULL
GROUP BY ID) as s
ON i.ID=s.ID
WHERE s.gap<>1
GROUP BY i.ID
I have used multiple sub-query. You can use CTE or create intermediate tables if required.
db<>fiddle link here

Select distinct rows based on two columns with additional columns

I have a table such as this
Blog
Id ColumnId DateCreated Type
1 1 2018-01-01 1
1 1 2018-01-02 2
1 1 2018-02-01 3
I need to select all unique rows based on the combination of Id and ColumnId. Then it needs to grab me the latest date and largest Type. I can't seem to figure this out.
I started of with just getting distinct values like this:
SELECT Id, ColumnId
FROM Blog
GROUP BY Id, ColumnId
Then I figured if I maybe joined it on itself I can pull out the rest, but I'm having no luck how to accomplish this
SELECT *
FROM ( SELECT Id, ColumnId
FROM Blog
GROUP BY Id, ColumnId) A
INNER JOIN Blog B ON A.Id = B.Id AND A.ColumnId = B.ColumnId
But that just gives me back all 3 rows.
In my live example the Id column is not int, but uniqueidentifier. For sake of simplicity I made it int in my example.
Expected Result Sample:
Id ColumnId DateCreated Type
1 1 2018-02-01 3
Have you tried using MAX?
SELECT Id, ColumnId, MAX(DateCreated), MAX([Type])
FROM Blog
GROUP BY Id, ColumnId
Use row_number():
select b.*
from (select b.*,
row_number() over (partition by id, columnid order by date desc, type desc) as seqnum
from blog b
) b
where seqnum = 1;
This grabs the largest type on the latest date, which is how I interpret "it needs to grab me the latest date and largest Type."
seems you need the last
you could use an inner join on the max date
select * from blog
inner join (
select id, columnId, max(DateCreated) as max_date
from blog
group by id, columnId
) t on t.id = blog.id, t.columnId = blog.columnId and t.max_date = blog.DateCreated
Maybe is not the best solution, but you could try to use a subselect in your query.
Something like this
SELECT DISTINCT Id, ColumnId, (SELECT MAX(Type) FROM blog) AS type
FROM Blog

Select the Max date time for single User

I have a table like this,
Date User
15-06-2018 A
16-06-2018 A
15-06-2018 B
14-06-2018 C
16-06-2018 C
I want to get the output like this,
Date User
16-06-2018 A
15-06-2018 B
16-06-2018 C
I tried Select Max(date),User from Table group by User
Based on your comment, I assume you have duplicated results in those 80 columns when you group by them. Assuming so, here's one option using row_number to always return 1 row per user:
select *
from (
select *, row_number() over (partition by user order by date desc) rn
from yourtable
) t
where rn = 1
You can use correlation subquery :
select t.*
from table t
where date = (select max(t1.date)
from table t1
where t1.user = t.user
);
However, i would also recommend row_number() :
select top (1) with ties *
from table t
order by row_number() over (partition by user order by date desc);
You can also use a ranking function
SELECT User, Date
FROM
(
SELECT User, Date
, Row_id = Row_Number() OVER (Partition by User, ORDER BY User, Date desc)
FROM table
)q
WHERE Row_Id = 1
I would suggest you this
Select * from table t where exist
(Select 1 from
(Select user, max(date) as date from table) A
Where A.user = t.user and A.date = t.date )

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

How do I return a single row based on the aggregate of more than one column

Sorry for the ambiguous title, not sure how to search, or ask this question.
Lets say we have TableA:
RowID FkId Rank Date
ID1 A 1 2013-3-1
ID2 A 2 2013-3-2
ID3 A 2 2013-3-3
ID4 B 3 2013-3-4
ID5 A 1 2013-3-5
I need to create a view, that will return 1 row for each FkId. The row should be the max rank, and max date. So for FkId "A", the query would return the row for "ID3".
I was able to return a single row by using sub-queries; first I get the MAX(Rank), then join to another query that gets MAX(Date) group by FkId & Rank.
SELECT TableA.*
(Select FkId, MAX(Rank) AS Rank FROM TableA GROUP BY FkId) s1
INNER JOIN (Select FkId, Rank, MAX(Date) AS Date FROM TableA GROUP BY FkId,Rank) s2 ON s1.FkId = s2.FkId AND s1.Rank = s2.Rank
INNER JOIN TableA ON s2.FkId = TableA.FkId AND s2.Rank = TableA.Rank AND s2.Date = TableA.Date
Is there a more efficient query that would achieve the same results? Thanks for looking.
Edit: Added ID5 since the last answer. If I tried a normal MAX(rank),MAX(Date) GROUP BY FkId, then for "A", I would get A; 2; 2013-3-5. This result would not match up to a RowId.
You can use ROW_NUMBER with a CTE (presuming sql-server >= 2005):
WITH CTE AS
(
SELECT TableA.*,
RN = ROW_NUMBER() OVER (PARTITION BY FkId Order By Rank Desc, Date DESC)
FROM Table A
)
SELECT RowID,FkId, Rank,Date
FROM CTE WHERE RN = 1
Your question (clarified in the comments to this answer) asks for:
A single row for each FkId
The Max Date and Rank
The results to correspond to a row in the original data.
In the case that there are FkIds with rows such that the maximum date and maximum rank are in separate rows, you'll have to relax at least one of these requirements.
If you're willing to relax requirement (3), then you can use GROUP BY:
SELECT FkId, MAX(Rank) AS Rank, Max(Date) AS Date
FROM TableA
GROUP BY FkId
Given the extra information in the comments. That you want the latest, of the highest ranked entries for each FkId, the following should work:
SELECT FkId, Rank, MAX(Date) AS Date
FROM TableA A
WHERE Rank = (SELECT MAX(Rank)
FROM TableA sub
WHERE A.FkId = sub.FkId
GROUP BY sub.FkId)
GROUP BY FkId, Rank
Here's a sqlfiddle to show it in action.
You can use Rank() and inline query to achieve it.
select * from TableA
where RowID in (
select rowID from (
select FKID, RowID,
rank() over (partition by FKID order by [Rank] desc, [Date] desc) as RankNumber
from TableA ) A
where A.RankNumber=1 )
SQL Fiddle Demo
You can also be sneaky and accomplish what ljh suggested like this:
select top 1 with ties *
from TableA
order by rank() over (
partition by FKID
order by [Rank] desc, [Date] desc
)