How to get record with earliest date per group - sql-server-2008

I need to get the earliest date from the following table based on column 'ItemNo'.
ItemNo PO_number Date
110913 PO-8048 9/15/2015
110913 PO-8036 9/30/2015
110652 PO-1011 10/19/2015
110652 PO-1011 10/10/2015
110009 PO-1016 7/1/2015
110009 PO-1087 6/20/2015
110888 PO-7171 4/1/2015
Your query result should be look like this.
ItemNo PO_number Date
110913 PO-8048 9/15/2015
110652 PO-1011 10/10/2015
110009 PO-1087 6/20/2015
110888 PO-7171 4/1/2015
Any help would be greatly appreciated.

Couple of different ways you could approach this, one reasonable approach would be something like:
with min_rec as
(
select t.ItemNo, t.PO_number, t.Date, row_number() over(partition by t.ItemNo order by t.Date asc) as rn
from your_table t
)
select m.ItemNo, m.PO_number, m.Date
from min_rec m
where m.rn = 1;
Leveraging a CROSS APPLY would be another approach that would work as well, though in this particular case it likely wouldn't be a better performing approach (though as always, it depends):
select distinct c.ItemNo, c.PO_number, c.Date
from your_table t
cross apply (
select top 1 i.ItemNo, i.PO_number, i.Date
from your_table i
where i.ItemNo = t.ItemNo
order by i.Date asc) c;
And naturally, you could simply use a self-joining subquery (I'll skip the example on that one).

Related

SQL code in order to select second value after first one

Help needed. Could someone help to generate code which would take only second value of IncurredAmount after first one from the same policid.
SELECT claims.claimid, claims.policyid, claims.IncurredAmount
FROM claims
GROUP BY claims.claimid, claims.policyid, claims.IncurredAmount
HAVING (((claims.policyid)=62));
That's what I have. I tried to take one policyid (62) in order to have less entries. But there I stuck. have no clue what clause can be used in order to take only second entries for all entries.
Try this, though whether it will work depends on the version of your database:
SELECT claimid, policyid, IncurredAmount
FROM (
SELECT *,
row_number() over (partition by policyid order by claimid) rn
FROM [MyTable]
) t
WHERE t.rn = 2
A solution exists for the old MySql versions (pre 8.0)
select *
from claims t
where exists (
select 1
from claims t2
where t2.policyid = t.policyid
and t2.claimid <= t.claimid
having count(distinct t2.claimid) = 2
)
order by policyid, claimid
db<>fiddle here
Although it's more equivalent to a DENSE_RANK.
I.e. if there's more with the 2nd lowest claimid then it'll get more than 1.

How to sort teams by summarized score in SQL? (two columns sorting with SUM())

Playerbase have 3 columns: PlrName, TeamTag, Score
I need to sort players grouping them by TeamTag. And put teams with best summarized score above.
So want to figure out what query will help make this:
PlrName|TeamTag|Score PlrName|TeamTag|Score
-------------------------------------------------------
Player1|TeamThr|0 Player6|TeamThr|9 \
Player2|TeamTwo|2 Player1|TeamThr|0 > 9
Player3|TeamOne|4 Player4|TeamThr|0 /
Player4|TeamThr|0 Player5|TeamOne|4 \
Player5|TeamOne|4 became-> Player3|TeamOne|4 > 8
Player6|TeamThr|9 Player8|TeamOne|0 /
Player7|TeamTwo|2 Player2|TeamTwo|2 \
Player8|TeamOne|0 Player7|TeamTwo|2 > 6
Player9|TeamTwo|2 Player9|TeamTwo|2 /
added:
With this query i can get an array(?) of TeamTag's ordered by team score:
SELECT TeamTag FROM Playerbase GROUP BY team ORDER BY SUM(Score) DESC
..can I sort then PlayerBase using this "array"? Prefably within one query :)
Also i need to get full lines (using *), not only three fields.
If your dbms doesn't support window functions (any MySQL version below 8.0):
SELECT PlrName, TeamTag, Score FROM table a
LEFT JOIN
(SELECT PlrName, TeamTag, Score ,SUM(score) ts FROM table GROUP BY TeamTag) b
ON a.TeamTag=b.TeamTag
ORDER BY b.ts DESC, a.TeamTag, score DESC;
Try this
; with cte as (
select TeamTag, sum(Score) as Sum
from tablename)
select t.PlrName, t.teamtag, t.Score from tablename as t inner join cte as c
on t.teamtag=c.teamtag order by c.sum desc, t.score desc
Assuming your dbms supports window functions
select PlrName, TeamTag, Score
from (
select PlrName, TeamTag, Score, sum(Score) over (partition by TeamTag) ms
from tablename ) t
order by ms desc, TeamTag, Score desc
EDIT changed max() to sum() as OP refined the problem explanation.
I made it like this, but feel little uncomfortable about sql :D
SELECT * FROM `Playerbase` as T1
JOIN
(SELECT TeamTag, SUM(Score) AS Sum FROM `Playerbase` GROUP BY TeamTag ORDER BY Sum DESC) AS T2
ON T2.TeamTag = T1.TeamTag
Didn't make what i was trying to, but anyway thanks for answers. They helped me to understand a bit more :D

Find MAX of a summed value on multiple group by clauses

As the title indicates, I am trying to find the maximum summed value in column C for an object in column A based on a subset of column B over a period of time (let's say column D). My current query looks something like this in which I return the summed values greater than 10,000.
select id_a, id_b, sum(column_c) from master_table where id_b in (1,2,3,4,5)
and ymdh >= '2017-11-01' group by 1,2 having sum(column_c) > 10000 order by 2,3
desc;
What I'm trying to get returned is the greatest value from sum(column_c). I tried using both the max() and distinct() functions. Specifically using max(sum(imps)), but aggregate function calls many not be nested. Would anyone be able to provide guidance here?
You can use a FROM ( select ) T
select max(my_sum)
from (
select id_a
, id_b
, sum(column_c) my_sum
from master_table
where id_b in (1,2,3,4,5)
and ymdh >= '2017-11-01'
group by 1,2 having my_sum > 10000
order by 2,3 desc;
) T
Does this do what you want?
select id_a, id_b, sum(column_c)
from master_table
where id_b in (1,2,3,4,5) and
ymdh >= '2017-11-01'
group by id_a, id_b
having sum(column_c) > 10000
order by sum(column_c) desc
limit 1;
That is, use order by and limit to get the value you want. (This query includes the group by keys as well, but that is not necessary.)
scaisEdge has the answer (and my +1) - but I just wanted to add a bit about the thought process when designing an SQL statement like you're working on.
Don't feel you need to compose the whole thing - that it's one big statement, or that it's one single query.
Instead, you'll often need to break up the problem into steps, solve the individual steps, and then use those steps as sources for a query - because you don't have to use tables in the FROM clause; you can use your own subqueries instead.
So for this problem? You've got the first step done - you figured out how to write the query that gets the Sum over a particular grouping:
select someCol, sum(otherCol) as groupSum from myTable
group by someCol
Great! Now, you can effectively use this like it's a table:
select someCol, groupSum
from
(
select someCol, sum(otherCol) as groupSum from myTable
group by someCol
) mySubquery
And in your case, you want to get the maximum sum?
select max(groupSum)
from
(
select someCol, sum(otherCol) as groupSum from myTable
group by someCol
) mySubquery
Not only will this help while composing the full SQL statement, it'll actually help the person trying to read/debug it down the line, especially if you name your subqueries/columns well:
select max(totalHitsForWeek) as maxWeeklyUsage
from
(
select week, sum(hits) as totalHitsForWeek
from requestsTable
) hitsPerWeekSubquery
Hope that helps add to scaisEdge's answer! :-)

Greatest 'n' per group by month

I have a mysql table with date, name and rating of a person. I need to build a query to show the best person of each month. The query above gives me maximum rating of the month but wrong name/id of person.
SELECT DATE_FORMAT(date,'%m.%Y') as date2, MAX(rating), name FROM test GROUP BY date2
Here's sqlfiddle with sample table: http://sqlfiddle.com/#!2/4dd54b/9
I read several greatest-n-per-group topics, but those queries didn't work, I suppose it's because of grouping by DATE_FORMAT. So here I ask.
The easiest way is to use the substring_index()/group_concat() trick:
SELECT DATE_FORMAT(date, '%m.%Y') as date2, MAX(rating),
substring_index(group_concat(name order by rating desc), ',', 1) as name
FROM test
GROUP BY date2;
A faster solution might look like this - although removal of the DATE_FORMAT function altogether will speed things up even further...
SELECT x.*
FROM test x
JOIN
( SELECT DATE_FORMAT(date,'%Y-%m') dt
, MAX(rating) max_rating
FROM test
GROUP
BY DATE_FORMAT(date,'%Y-%m')
) y
ON y.dt = DATE_FORMAT(x.date,'%Y-%m')
AND y.max_rating = x.rating;

ORDER BY date and time BEFORE GROUP BY name in mysql

i have a table like this:
name date time
tom | 2011-07-04 | 01:09:52
tom | 2011-07-04 | 01:09:52
mad | 2011-07-04 | 02:10:53
mad | 2009-06-03 | 00:01:01
i want oldest name first:
SELECT *
ORDER BY date ASC, time ASC
GROUP BY name
(->doesn't work!)
now it should give me first mad(has earlier date) then tom
but with GROUP BY name ORDER BY date ASC, time ASC gives me the newer mad first because it groups before it sorts!
again: the problem is that i can't sort by date and time before i group because GROUP BY must be before ORDER BY!
Another method:
SELECT *
FROM (
SELECT * FROM table_name
ORDER BY date ASC, time ASC
) AS sub
GROUP BY name
GROUP BY groups on the first matching result it hits. If that first matching hit happens to be the one you want then everything should work as expected.
I prefer this method as the subquery makes logical sense rather than peppering it with other conditions.
As I am not allowed to comment on user1908688's answer, here a hint for MariaDB users:
SELECT *
FROM (
SELECT *
ORDER BY date ASC, time ASC
LIMIT 18446744073709551615
) AS sub
GROUP BY sub.name
https://mariadb.com/kb/en/mariadb/why-is-order-by-in-a-from-subquery-ignored/
I think this is what you are seeking :
SELECT name, min(date)
FROM myTable
GROUP BY name
ORDER BY min(date)
For the time, you have to make a mysql date via STR_TO_DATE :
STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s')
So :
SELECT name, min(STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s'))
FROM myTable
GROUP BY name
ORDER BY min(STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s'))
This worked for me:
SELECT *
FROM your_table
WHERE id IN (
SELECT MAX(id)
FROM your_table
GROUP BY name
);
Use a subselect:
select name, date, time
from mytable main
where date + time = (select min(date + time) from mytable where name = main.mytable)
order by date + time;
If you wont sort by max date and group by name, you can do this query:
SELECT name,MAX(date) FROM table group by name ORDER BY name
where date may by some date or date time string. It`s response to you max value of date by each one name
Another way to solve this would be with a LEFT JOIN, which could be more efficient. I'll first start with an example that considers only the date field, as probably it is more common to store date + time in one datetime column, and I also want to keep the query simple so it's easier to understand.
So, with this particular example, if you want to show the oldest record based on the date column, and assuming that your table name is called people you can use the following query:
SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date > p2.date
WHERE p2.date is NULL
GROUP BY p.name
What the LEFT JOIN does, is when the p.date column is at its minimum value, there will be no p2.date with a smaller value on the left join and therefore the corresponding p2.date will be NULL. So, by adding WHERE p2.date is NULL, we make sure to show only the records with the oldest date.
And similarly, if you want to show the newest record instead, you can just change the comparison operator in the LEFT JOIN:
SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date < p2.date
WHERE p2.date is NULL
GROUP BY p.name
Now, for this particular example where date+time are separate columns, you would need to add them in some way if you want to query based on the datetime of two columns combined, for example:
SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date + INTERVAL TIME_TO_SEC(p.time) SECOND > p2.date + INTERVAL TIME_TO_SEC(p2.time) SECOND
WHERE p2.date is NULL
GROUP BY p.name
You can read more about this (and also see some other ways to accomplish this) on the The Rows Holding the Group-wise Maximum of a Certain Column page.
I had a different variation on this question where I only had a single DATETIME field and needed a limit after a group by or distinct after sorting descending based on the datetime field, but this is what helped me:
select distinct (column) from
(select column from database.table
order by date_column DESC) as hist limit 10
In this instance with the split fields, if you can sort on a concat, then you might be able to get away with something like:
select name,date,time from
(select name from table order by concat(date,' ',time) ASC)
as sorted
Then if you wanted to limit you would simply add your limit statement to the end:
select name,date,time from
(select name from table order by concat(date,' ',time) ASC)
as sorted limit 10
In Oracle, This work for me
SELECT name, min(date), min(time)
FROM table_name
GROUP BY name
work for me mysql
select * from (SELECT number,max(date_added) as datea FROM sms_chat group by number) as sup order by datea desc
This is not the exact answer, but this might be helpful for the people looking to solve some problem with the approach of ordering row before group by in mysql.
I came to this thread, when I wanted to find the latest row(which is order by date desc but get the only one result for a particular column type, which is group by column name).
One other approach to solve such problem is to make use of aggregation.
So, we can let the query run as usual, which sorted asc and introduce new field as max(doc) as latest_doc, which will give the latest date, with grouped by the same column.
Suppose, you want to find the data of a particular column now and max aggregation cannot be done.
In general, to finding the data of a particular column, you can make use of GROUP_CONCAT aggregator, with some unique separator which can't be present in that column, like GROUP_CONCAT(string SEPARATOR ' ') as new_column, and while you're accessing it, you can split/explode the new_column field.
Again, this might not sound to everyone. I did it, and liked it as well because I had written few functions and I couldn't run subqueries. I am working on codeigniter framework for php.
Not sure of the complexity as well, may be someone can put some light on that.
Regards :)