Select the last record in each group - mysql

There is a table Remark that contains data as shown below:
SerialNo | RemarkNo | Desp
=============================================
10 | 1 | rainy
10 | 2 | sunny
11 | 1 | sunny
11 | 2 | rainy
11 | 3 | cloudy
12 | 1 | rainy
If I run a query SELECT * FROM remark WHERE remark_no IN (SELECT MAX(remark_no) FROM remark GROUP BY serial_no);, I still get the above result:
What query will return the following result:
10 | 2 | sunny
11 | 3 | cloudy
12 | 1 | rainy
That is, the last record in each group should be returned??

SELECT
r1.*
FROM remark r1
LEFT JOIN remark r2
ON (r1.serial_no = r2.serial_no
AND r1.remark_no < r2.remark_no)
WHERE r2.remark_no IS NULL;

For the top RemarkNo for each SerialNo (along with the other fields from the same row) :
Select all records where there isn't a higher RemarkNo for the same SerialNo
SELECT *
FROM remark r1
WHERE NOT EXISTS
(SELECT SerialNo FROM remark r2
WHERE (r2.RemarkNo>r1.RemarkNo)
AND (r2.SerialNo=r1.SerialNo)
)
http://sqlfiddle.com/#!2/7da1b/21

You can do this by returning your results and using a sort order. For example...
SELECT * from GROUP Order By GROUP.ID DESC
This will return results in the order of last record first. Then if you do not loop through results you will return only one record... the last one recorded.

Related

How to display the days on the view page if days id is multiple in the single column?

I have two-three tables. First is days and in the second table I am inserting the data
days_id days_name
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday
7 Sunday
In the second table
id | days_id
1 | 2,3,5
2 | 1,3,7
3 | 1
4 | 2
I know, it's the not correct way to insert multiple data in a column but still, I have to do this.
Now My issue is, I have to display the list in the table.
Note(This is not 100% my output. I just want to know how to display the days)
id | days_id
1 | Tuesday,Wednesday,Friday
2 | Monday,Wednesday,Sunday
3 | Monday
4 | Tuesday
What query I have to use it? I tried below query but it's displaying only first day.
SELECT *
FROM `b_list`
JOIN `days` ON `b_list`.`b_days` = `days`.`days_id`
WHERE `b_list`.`b_status` = 1
Tagging CodeIgniter because I am using it.
Would you help me out in this?
$result = $this->db->where(['b_list.batch_status'=>1])
->select('*, group_concat(days_name ORDER BY days_id ASC) as days_list')
->from('b_list')
->join('days','FIND_IN_SET(days.days_id,b_list.days_id)')
->get()
->result();
and using above code I am getting the below query
SELECT *, group_concat(days.days_name ORDER BY days_id ASC) as days_list FROM `b_list` JOIN `days` USING (FIND_IN_SET(days.days_id,b_list.days_id)) WHERE `b_list`.`batch_status` = 1
You can use FIND_IN_SET() to join on a list and GROUP_CONCAT() to concatenate them into a string:
SELECT b_list.id, group_concat(days_name ORDER BY days_id ASC) as days_list
FROM b_list
INNER JOIN days ON FIND_IN_SET(days.days_id, b_list.days_ids)
GROUP BY b_list.id;
DEMO: https://www.db-fiddle.com/f/e9fCJRNiSPwk8jQtFWuEFB/4
This is awful. Don't do it. But if you must:
DROP TABLE terrible_idea;
CREATE TABLE terrible_idea
(id SERIAL PRIMARY KEY
,days_id VARCHAR(255) NOT NULL
);
INSERT INTO terrible_idea VALUES
(1,'2,3,5'),
(2,'1,3,7'),
(3,'1'),
(4,'2');
CREATE TABLE hacky_solution
(i SERIAL PRIMARY KEY);
INSERT INTO hacky_solution VALUES (1),(2),(3),(4),(5),(6),(7);
SELECT id, STR_TO_DATE(FIND_IN_SET(y.i,x.days_id),'%W')m FROM terrible_idea x,hacky_solution y;
SELECT id
, SUBSTRING_INDEX(SUBSTRING_INDEX('Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday'
,','
,FIND_IN_SET(y.i,CONCAT(',',x.days_id))
),',',-1)m
FROM terrible_idea x,hacky_solution y
HAVING m <> '';
SELECT id
, SUBSTRING_INDEX(SUBSTRING_INDEX('Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday'
,','
,FIND_IN_SET(y.i,CONCAT(',',x.days_id))
),',',-1)m
FROM terrible_idea x,hacky_solution y
HAVING m <> '';
+----+-----------+
| id | m |
+----+-----------+
| 2 | Tuesday |
| 3 | Tuesday |
| 1 | Tuesday |
| 4 | Tuesday |
| 1 | Wednesday |
| 2 | Wednesday |
| 1 | Thursday |
| 2 | Thursday |
+----+-----------+

Firstly do Group By then retrieve all rows as per order?

I have table where I have id and time.
ID | Time
1 | 8.35
1 | 8.40
3 | 8.43
4 | 8.45
1 | 8.50
2 | 8.52
3 | 8.54
4 | 8.55
1 | 8.57
2 | 9.01
3 | 9.05
5 | 9.06
Required Result
ID | Time
5 | 9.06
3 | 9.05
3 | 8.54
3 | 8.43
2 | 9.01
2 | 8.52
1 | 8.57
1 | 8.50
1 | 8.40
1 | 8.35
4 | 8.55
4 | 8.45
Currently I am doing it by Select * from table group by ID order by Time DESC and get
Result One:
ID | Time
5 | 9.06
3 | 9.05
2 | 9.01
1 | 8.57
4 | 8.55
then writing second query and storing data in list.
foreach value in Result one:
Select * from Table where ID = value
Instead of writing a loop, I will like to have only one query.
Basic problem is I want to group IDs and top group should be the item that occured recently. As in example 1 occurs many time but I will consider only the latest time while grouping.
Can I write only one query to get result?
SELECT ID, Time FROM Table ORDER BY ID, Time
Grouping combines matching rows, so you do not want to group, ordering puts them in order, and that's what you want, you want all the IDs in order then all the times in order within those ids, so you want to order by ID then order by time.
UPDATE due to question edit
This can be done with a join to a sub select
SELECT t.ID. t.Time FROM Table t
JOIN (SELECT ID, Max(Time) as Time FROM Table GROUP BY ID) ss
ON t.ID = ss.ID
ORDER BY ss.Time DESC, t.ID DESC, t.Time DESC
The sub select (ss) does the first query you have there, and joins it to the main table, letting you order by the highest(max) time for each ID, then by the ID and the Time for the row itself. Note that all the ordering is done on the final query, ordering in the sub select is useless, since the join will reorder it anyways.

Mysql join select max for all record

I am unable to map the record as my expectation.
Doc Table
-------+-------------------
doc_id | doc_title
-------+-------------------
1 | My book
-------+-------------------
2 | My sec Book
--------------------------
Doc details Table
-----------+--------------+-----------------------
fk_doc_id | doc_version | submit_date
-----------+--------------+-----------------------
1 | 1 | 2015-10-25 14:32:01
-----------+--------------+-----------------------
1 | 2 | 2015-10-26 13:00:01
-----------+--------------+-----------------------
1 | 3 | 2015-10-27 09:00:00
--------------------------+-----------------------
2 | 1 | 2015-10-25 11:15:01
-----------+--------------+-----------------------
2 | 2 | 2015-10-26 10:00:00
--------------------------+-----------------------
Question: How do I join this two tables to get each documents with the latest version doc info? even though I get the latest version but the row info which is not correct.
So far I have tried this query
SELECT *, max(doc_version) AS latest_version
FROM d_doc
JOIN d_doc_dtl ON d_doc.doc_id = d_doc_dtl.fk_doc_id
GROUP BY d_doc.doc_id;
My expected result is
--------+--------------+----------------+--------------------
doc_id | doc_title | latest_version | submit_date
--------+--------------+----------------+--------------------
1 | My book | 3 | 2015-10-27 09:00:00
--------+--------------+----------------+--------------------
2 | My sec book | 2 | 2015-10-26 10:00:00
----------------------------------------+--------------------
but my result is
--------+--------------+----------------+--------------------
doc_id | doc_title | latest_version | submit_date
--------+--------------+----------------+--------------------
1 | My book | 3 | 2015-10-25 14:32:01
--------+--------------+----------------+--------------------
2 | My sec book | 2 | 2015-10-25 11:15:01
----------------------------------------+--------------------
NOTE: the submit_date which is no correct.
SELECT d_doc.doc_id, d_doc.doc_title, max_table.latest_version
FROM d_doc JOIN (
select fk_doc_id, max(doc_version) as latest_version from d_doc_dtl group by fk_doc_id
) as max_table ON d_doc.doc_id = max_table.fk_doc_id
This query should work as you expect. It selects latest document versions in inner subquery and than joins it with documents.
SELECT d.doc_id,
d.doc_title,
dtl.doc_version latest_version,
dtl.submit_date
FROM d_doc d
INNER JOIN (SELECT dt.*
FROM d_doc_dtl dt
INNER JOIN (SELECT fk_doc_id, MAX(doc_version) doc_version
FROM d_doc_dtl
GROUP BY fk_doc_id) dm
ON dt.fk_doc_id = dm.fk_doc_id
AND dt.doc_version = dm.doc_version) dtl
ON d.doc_id = dtl.fk_doc_id
You get wrong results because you selected only max(version), but date as it is not in group by clause can contain any value. First you need to get records containing latest version as shown above.
Easy, instead of
SELECT *, max(doc_version) AS latest_version
Use this
SELECT d_doc.*, max(doc_version) AS latest_version
What you were doing by selecting * is getting all the results after the table is joined and you only wanted the original table results.
select * from doc_table , doc_version where exists( select
max(version_id)
from
doc_version vert
where
(doc_table .DOC_ID = vert.VERSION_DOC_ID) ) group by doc_id;
You can try something like this.

Select the SECOND LAST record in each group

There is a table Remark that contains data as shown below:
SerialNo | RemarkNo | Desp
=============================================
10 | 1 | rainy
10 | 2 | sunny
11 | 1 | sunny
11 | 2 | rainy
11 | 3 | cloudy
11 | 4 | sunny
12 | 1 | rainy
What query will return the following result:
10 | 1 | rainy
11 | 3 | cloudy
12 | null | null
That is, the second last record in each group should be returned?
Assuming all the RemarkNo for a SerialNo are continuous. The larger the remark number, the later the remark was made. Hence, the second last RemarkNo for SerialNo 10 is 1 with Desp 'rainy'.
Try:
select s.SerialNo, r.RemarkNo, r.Desp
from (select SerialNo, max(RemarkNo) maxRemark from Remark group by SerialNo) s
left join Remark r on s.SerialNo = r.SerialNo and s.maxRemark-1 = r.RemarkNo
(SQLFiddle here.)
Here is some sql pseudo-code to get you started:
select
penultimate.*
from data as penultimate
left join (
/* query on data to return last row frome each group */
) as ultimate
on /* sql to join 2nd last record on each group to last group */
where not ultimate.SerialNo is null
Completely ineffective solution, but works ...
SELECT
SerialNo,
RemarkNo,
(SELECT Desp
FROM Remarks
WHERE SerialNo = r3.SerialNo AND RemarkNo = r3.RemarkNo) Desp
FROM (
SELECT
r.SerialNo,
(SELECT r2.RemarkNo
FROM Remarks r2
WHERE r.SerialNo = r2.SerialNo
ORDER BY r2.RemarkNo DESC
LIMIT 1, 1) RemarkNo
FROM (SELECT DISTINCT SerialNo FROM Remarks) r) r3
Working example: http://sqlfiddle.com/#!2/a1f89/22

Batting average MySQL query

You may need some knowledge of cricket to help with this one. Given the following table, would this query produce this result?
Table: batsmen
batsmanname | runsscored | howout
Colin | 10 | bowled
Colin | 20 | caught
Steve | 10 | bowled
Steve | 20 | not out
SELECT batsmanname, SUM(runsscored) / COUNT(howout) AS battingavg
FROM batsmen
WHERE howout <> 'not out'
GROUP BY batsmananme
Result:
batsmanname | battingavg
Colin | 15
Steve | 30
This should produce the results that you want:
SELECT b1.name, SUM(b1.runsscored) / b2.NumOut as bat_avg
FROM batsmen b1
INNER JOIN
(
select name, COUNT(howout) as NumOut
from batsmen
WHERE howout <> 'not out'
GROUP BY name
) b2
ON b1.name = b2.name
GROUP BY b1.name
See SQL Fiddle with demo