Select the SECOND 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
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

Related

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 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
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.

Filter query with order by and limit when using left join

How to filter query with order by and limit when using left join
store_profile
id + store_name
1 | Accessorize.me
2 | Active IT
3 | Edushop
4 | Gift2Kids
5 | Heavyarm
6 | Bamboo
store_fee
id + store_id + date_end
1 | 1 | 27-6-2013
2 | 2 | 29-8-2013
3 | 3 | 02-6-2013
4 | 4 | 20-4-2013
5 | 4 | 01-7-2013
6 | 4 | 28-9-2013
7 | 5 | 03-9-2013
8 | 6 | 01-9-2013
my previous query
$order_by_for_sort_column = "order by $column" //sorting column
$query = "SELECT * FROM store_profile sp LEFT JOIN store_fee sf ON (sf.store_id = sp.id) $order_by_for_sort_column";
what i want is order by id desc and limit 1 for table store_fee not for for entire query. So i can grab the latest date in date_end for each store.
As you can see for store_id 4(store_fee) i have 3 different date and i just want grab the latest date.
and the result should be something like this
1 | Accessorize.me 27-6-2013
2 | Active IT 29-8-2013
3 | Edushop 02-6-2013
4 | Gift2Kids 28-9-2013
5 | Heavyarm 03-9-2013
6 | Bamboo 01-9-2013
SELECT a.id, a.store_name, MAX(b.date_End) date_end
FROM store_profile a
LEFT JOIN store_fee b
ON a.ID = b.store_ID
GROUP BY a.id, a.store_name
SQLFiddle Demo
but if the datatype date_End column is varchar, the above query won't work because it sorts the value by character and that it can mistakenly gives undesired result. 18-1-2013 is greater than 01-6-2013.
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
SELECT *
FROM store_profile AS sp
LEFT JOIN (
SELECT store_id, MAX(date_end)
FROM store_fee
GROUP BY store_id
) AS sf
ON sp.id=sf.store_id;

nested query & transaction

Update #1: query gives me syntax error on Left Join line (running the query within the left join independently works perfectly though)
SELECT b1.company_id, ((sum(b1.credit)-sum(b1.debit)) as 'Balance'
FROM MyTable b1
JOIN CustomerInfoTable c on c.id = b1.company_id
#Filter for Clients of particular brand, package and active status
where c.brand_id = 2 and c.status = 2 and c.package_id = 3
LEFT JOIN
(
SELECT b2.company_id, sum(b2.debit) as 'Current_Usage'
FROM MyTable b2
WHERE year(b2.timestamp) = '2012' and month(b2.timestamp) = '06'
GROUP BY b2.company_id
)
b3 on b3.company_id = b1.company_id
group by b1.company_id;
Original Post:
I keep track of debits and credits in the same table. The table has the following schema:
| company_id | timestamp | credit | debit |
| 10 | MAY-25 | 100 | 000 |
| 11 | MAY-25 | 000 | 054 |
| 10 | MAY-28 | 000 | 040 |
| 12 | JUN-01 | 100 | 000 |
| 10 | JUN-25 | 150 | 000 |
| 10 | JUN-25 | 000 | 025 |
As my result, I want to to see:
| Grouped by: company_id | Balance* | Current_Usage (in June) |
| 10 | 185 | 25 |
| 12 | 100 | 0 |
| 11 | -54 | 0 |
Balance: Calculated by (sum(credit) - sum(debits))* - timestamp does not matter
Current_Usage: Calculated by sum(debits) - but only for debits in JUN.
The problem: If I filter by JUN timestamp right away, it does not calculate the balance of all time but only the balance of any transactions in June.
How can I calculate the current usage by month but the balance on all transactions in the table. I have everything working, except that it filters only the JUN results into the current usage calculation in my code:
SELECT b.company_id, ((sum(b.credit)-sum(b.debit))/1024/1024/1024/1024) as 'BW_remaining', sum(b.debit/1024/1024/1024/1024/28*30) as 'Usage_per_month'
FROM mytable b
#How to filter this only for the current_usage calculation?
WHERE month(a.timestamp) = 'JUN' and a.credit = 0
#Group by company in order to sum all entries for balance
group by b.company_id
order by b.balance desc;
what you will need here is a join with sub query which will filter based on month.
SELECT T1.company_id,
((sum(T1.credit)-sum(T1.debit))/1024/1024/1024/1024) as 'BW_remaining',
MAX(T3.DEBIT_PER_MONTH)
FROM MYTABLE T1
LEFT JOIN
(
SELECT T2.company_id, SUM(T2.debit) T3.DEBIT_PER_MONTH
FROM MYTABLE T2
WHERE month(T2.timestamp) = 'JUN'
GROUP BY T2.company_id
)
T3 ON T1.company_id-T3.company_id
GROUP BY T1.company_id
I havn't tested the query. The point here i am trying to make is how you can join your existing query to get usage per month.
alright, thanks to #Kshitij I got it working. In case somebody else is running into the same issue, this is how I solved it:
SELECT b1.company_id, ((sum(b1.credit)-sum(b1.debit)) as 'Balance',
(
SELECT sum(b2.debit)
FROM MYTABLE b2
WHERE b2.company_id = b1.company_id and year(b2.timestamp) = '2012' and month(b2.timestamp) = '06'
GROUP BY b2.company_id
) AS 'Usage_June'
FROM MYTABLE b1
#Group by company in order to add sum of all zones the company is using
group by b1.company_id
order by Usage_June desc;

Getting unique values in sql server

I have a table say :
id| AccID | Subject | Date
1 | 103 | Open HOuse 1 | 11/24/2011 9:00:00 AM
2 | 103 | Open HOuse 2 | 11/25/2011 10:00:00 AM
3 | 72 | Open House 3 | 11/26/2011 1:10:28 AM
4 | 82 | OPen House 4 | 11/27/2011 5:00:29 PM
5 | 82 | OPen House 5 | 11/22/2011 5:00:29 PM
From the above table, i need all the unique values for the Accid. But say, if there are two or more columns with the same Accid, then i need the one which has the smaller date (among the columns which have the same Accid)
So, from the above table, the o/p should be :
1
3
5
Can any1 please help me in this ? Thanks
SELECT t1.*
FROM [MyTable] t1
INNER JOIN
(
SELECT AccID, MIN(Date) Date
FROM [MyTable]
GROUP BY AccID
) t2 ON t1.AccID = t2.AccID AND t1.Date = t2.Date
More than just the AccID but...
WITH SEL
AS
(
SELECT AccID, MIN(DATE)
FROM table
GROUP BY AccID
)
SELECT table.*
FROM table
JOIN SEL ON SEL.AccID = table.AccID