selecting only newest row with specific value - mysql

Table:
person | borrow_date | is_borrowed | SN | date | id
1 | 2019-01-10...| 1 | 20 |2019-01-10...| 6
3 | 2019-01-09...| 3 | 10 |2019-01-09...| 5
1 | 2019-01-08...| 1 | 10 |2019-01-08...| 4
2 | 2019-01-08...| 1 | 10 |2019-01-08...| 3
1 | NULL | 2 | 20 |2019-01-07...| 2
1 | NULL | 2 | 10 |2019-01-07...| 1
My wanted output is to select newest rows where "is_borrowed" equals 1 and grouped by SN, so that when the query is executed with person=2 or person=3 then it would retrieve empty set. Whereas for person=1 it would give back two rows.
Wanted output (where person=1):
person | borrow_date | is_borrowed | SN | date |id
1 | 2019-01-10...| 1 | 20 | 2019-01-10...|6
1 | 2019-01-08...| 1 | 10 | 2019-01-08...|4
Wanted output (where person=2):
EMPTY SET
Wanted output (where person=3):
EMPTY SET
This is my current query and it sadly doesn't work.
SELECT a.SN, a.is_borrowed,a.max(date) as date, a.person
FROM table a
INNER JOIN (SELECT SN, MAX(date) as date, osoba from table where person like
"2" group by SN) as b
ON a.SN=b.SN and a.date=b.date
WHERE a.person like "2" and a.is_borrowed=1

If I correctly understood you from the question and the comment you made under it, here's one way to do it without specifying the person:
select *
from TableName as p
inner join (select max(borrow_date) as borrow_date,
SN
FROM TableName
where is_borrowed = 1
group by SN) as p2
on p.borrow_date = p2.borrow_date and p.SN = p2.SN
This should give you the result you're looking for. Here's a demo.
Note that I had to change the borrowed_date values in the table since yours contain hours and minutes while I didn't add those.
You can always specify it for each person by adding a where clause after the join.
select p.person,
p.borrow_date,
p.is_borrowed,
p.SN,
p.date,
p.id
from TableName as p
inner join (select max(borrow_date) as borrow_date,
SN
FROM TableName
where is_borrowed = 1
group by SN) as p2
on p.borrow_date = p2.borrow_date and p.SN = p2.SN
where p.person = '1'
Output:
person | borrow_date | is_borrowed | SN | date | id
1 | 2019-01-10 | 1 | 20 | 2019-01-10 | 6
1 | 2019-01-08 | 1 | 10 | 2019-01-08 | 4
While where p.person = '2' and where p.person = '3' will return empty sets.

Related

SQL Difference between two row in group by

I have a table records of store id, processing batch id and start time as follows:
|store_id | batch_id | process_start_time |
| A | 1 | 10 |
| B | 1 | 40 |
| C | 1 | 30 |
| A | 2 | 400 |
| B | 2 | 800 |
| C | 2 | 600 |
| A | 3 | 10 |
| B | 3 | 80 |
| C | 3 | 90 |
Here, rows needed to be grouped by batch_id and time_taken is difference of process_start_time of store A and store C.
So, the expected result would be:
batch_id | time_taken
1 | 20
2 | 200
3 | 80
I tried to do something like:
select batch_id, ((select process_start_time from records where store_id = 'C') - (select process_start_time from records where store_id = 'A')) as time_taken
from records group by batch_id;
But couldn't figure out to select specific rows in that particular group.
Thank you for looking into!
Update: the process_start_time column not necessarily max for store C
You seem to want conditional aggregation and arithmetic:
select batch_id,
(max(case when store_id = 'C' then process_start_time end) -
min(case when store_id = 'A' then process_start_time end)
) as diff
from records
group by batch_id;
You can try a self join.
SELECT r1.batch_id,
r1.process_start_time - r2.process_start_time time_taken
FROM records r1
INNER JOIN records r2
ON r1.batch_id = r2.batch_id
WHERE r1.store_id = 'C'
AND r2.store_id = 'A';
Here's another answer. This is using two instances of the records table and we link them up with where clauses and exists as follows:
select a.batch_id,
c.process_start_time - a.process_start_time as time_taken
from records a,
records c
where a.store_id = 'A'
and c.store_id = 'C'
and exists (
select 1
from records x
where x.batch_id = a.batch_id
and x.batch_id = c.batch_id
);
SELECT DISTINCT
store_a.batch_id,
store_c.process_start_time - store_a.process_start_time AS 'time_taken'
FROM records store_a
INNER JOIN records store_c
ON store_a.batch_id = store_c.batch_id
AND store_c.store_id = 'C'
AND store_a.store_id = 'A'

How to select rows, using group by with minimum field values?

Today I have posted a question and got a good answer: Stuck in building mysql query.
I though it helped me, but I've discovered that it returns wrong data. So I'm reposting the question here, with an answer I received, as well I will explain the problem why it is not working for me.
Example of data:
id | item_id | user_id | bid_price
----------------------------------
1 | 1 | 11 | 1
2 | 1 | 12 | 2
3 | 1 | 13 | 3
4 | 1 | 14 | 1
5 | 1 | 15 | 4
6 | 2 | 16 | 2
7 | 2 | 17 | 1
8 | 3 | 18 | 2
9 | 3 | 19 | 3
10 | 3 | 18 | 2
Expected result:
id | item_id | user_id | bid_price
----------------------------------
1 | 1 | 11 | 1
7 | 2 | 17 | 1
8 | 3 | 18 | 2
Offered solution:
select m.id, m.item_id, m.user_id, m.bid_price
from my_table m
inner join (
select item_id, min(id) min_id, min(bid_price) min_price
from my_table
where item_id IN (1,2,3)
group by item_id
) t on t.item_id = m.item_id
and t.min_price= m.bid_price
and t.min_id = m.id
The problem:
In the sub query the minimum ID is selected entire the group by (item_id) statement and doesn't reflects according to minimum bid_price.
In other words, the minimum id is selected not depending on the price field at all. So, in the result I will get minimum price and minimum id of the group, but this will not be the same row! The id can be related to the row with another bet_price value.
How this query can be adjusted? Thank you in advance!
SELECT min(m.id) AS id, m.item_id, m.user_id, m.bid_price
FROM my_table m
INNER JOIN (
SELECT item_id, min(bid_price) AS min_price
FROM my_table
GROUP BY item_id
) t ON t.item_id = m.item_id
AND t.min_price= m.bid_price
GROUP BY item_id
Output
id item_id user_id bid_price
1 1 11 1
7 2 17 1
8 3 18 2
Live Demo
http://sqlfiddle.com/#!9/a52dc6/13
SELECT DISTINCT
t1.item_id,
t1.bid_price
FROM tab1 t1
WHERE NOT exists(SELECT 1
FROM tab1 t2
WHERE t2.item_id = t1.item_id
AND t2.bid_price < t1.bid_price)
AND t1.item_id IN (1, 2, 3);
http://sqlfiddle.com/#!9/615e0a/5

How to ORDER BY CALCULATED SUM with Another Table in MySQL

For example, I have 2 table 'meta' and 'log'
in meta table:
| type | score |
|------|-------|
| a | 1 |
| b | 2 |
| c | 3 |
in log table:
| log_id | log_type | object_id |
|--------|----------|-----------|
| 1 | a | 13 |
| 2 | b | 13 |
| 3 | a | 14 |
| 4 | c | 14 |
| 5 | b | 15 |
| 6 | c | 15 |
so we know:
object 13 got score: a+b = 3
object 14 got score: a+c = 4
object 15 got score: b+c = 5
I want to query log table group by object id and order by sum of object score, is it possible?
select log.object_id, sum(meta.score)
from log
left join meta on meta.type = log.log_type
group by log.object_id
order by sum(meta.score) desc
This will produced the desired output
SELECT object_id, sum(score) from log
INNER JOIN meta on meta.type = log.log_type group by object_id ORDER BY sum(score);
But have you got the correct table design? You need to join on the meta.type and log.log_type column but this implies that if the log_type is 'a' the value of 3 is common for all object_ids is this really what you want?
Try this
Select object_id,sum(score) as c FROM `log` as a INNER JOIN `meta` as b on a.log_type=b.type group by object_id order by c desc
SELECT log.object_id, sum(score) total_score
FROM meta
INNER JOIN log on meta.type = log.log_type
GROUP BY log.object_id

MySQL select unique rows in two columns with the highest value in one column

I have a basic table:
+-----+--------+------+------+
| id, | name, | cat, | time |
+-----+--------+------+------+
| 1 | jamie | 1 | 100 |
| 2 | jamie | 2 | 100 |
| 3 | jamie | 1 | 50 |
| 4 | jamie | 2 | 150 |
| 5 | bob | 1 | 100 |
| 6 | tim | 1 | 300 |
| 7 | alice | 4 | 100 |
+-----+--------+------+------+
I tried using the "Left Joining with self, tweaking join conditions and filters" part of this answer: SQL Select only rows with Max Value on a Column but some reason when there are records with a value of 0 it breaks, and it also doesn't return every unique answer for some reason.
When doing the query on this table I'd like to receive the following values:
+-----+--------+------+------+
| id, | name, | cat, | time |
+-----+--------+------+------+
| 1 | jamie | 1 | 100 |
| 4 | jamie | 2 | 150 |
| 5 | bob | 1 | 100 |
| 6 | tim | 1 | 300 |
| 7 | alice | 4 | 100 |
+-----+--------+------+------+
Because they are unique on name and cat and have the highest time value.
The query I adapted from the answer above is:
SELECT a.name, a.cat, a.id, a.time
FROM data A
INNER JOIN (
SELECT name, cat, id, MAX(time) as time
FROM data
WHERE extra_column = 1
GROUP BY name, cat
) b ON a.id = b.id AND a.time = b.time
The issue here is that ID is unique per row you can't get the unique value when getting the max; you have to join on the grouped values instead.
SELECT a.name, a.cat, a.id, a.time
FROM data A
INNER JOIN (
SELECT name, cat, MAX(time) as time
FROM data
WHERE extra_column = 1
GROUP BY name, cat
) b ON A.Cat = B.cat and A.Name = B.Name AND a.time = b.time
Think about it... So what ID is mySQL returning form the Inline view? It could be 1 or 3 and 2 or 4 for jamie. Hows does the engine know to pick the one with the max ID? it is "free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. " it could pick the wrong one resulting in incorrect results. So you can't use it to join on.
https://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
If you want to use a self join, you could use this query:
SELECT
d1.*
FROM
date d1 LEFT JOIN date d2
ON d1.name=d2.name
AND d1.cat=d2.cat
AND d1.time<d2.time
WHERE
d2.time IS NULL
It is very simple
SELECT MAX(TIME),name,cat FROM table name group by cat

Joining tables but needs 0 for empty rows

I don't know how to explain the scenario using words. So am writing the examples:
I have a table named tblType:
type_id | type_name
---------------------
1 | abb
2 | cda
3 | edg
4 | hij
5 | klm
And I have another table named tblRequest:
req_id | type_id | user_id | duration
-------------------------------------------
1 | 4 | 1002 | 20
2 | 1 | 1002 | 60
3 | 5 | 1008 | 60
....
So what am trying to do is, fetch the SUM() of duration for each type, for a particular user.
This is what I tried:
SELECT
SUM(r.`duration`) AS `duration`,
t.`type_id`,
t.`type_name`
FROM `tblRequest` AS r
LEFT JOIN `tblType` AS t ON r.`type_id` = t.`type_id`
WHERE r.`user_id` = '1002'
GROUP BY r.`type_id`
It might return something like this:
type_id | type_name | duration
-------------------------------
1 | abb | 60
4 | hij | 20
It works. But the issue is, I want to get 0 as value for other types that doesn't have a row in tblRequest. I mean I want the output to be like this:
type_id | type_name | duration
-------------------------------
1 | abb | 60
2 | cda | 0
3 | edg | 0
4 | hij | 20
5 | klm | 0
I mean it should get the rows of all types, but 0 as value for those type that doesn't have a row in tblRequest
You could perform the aggregation on tblRequest and only then join it, using a left join to handle missing rows and coalesce to convert the nulls to 0s:
SELECT t.type_id, type_name, COALESCE(sum_duration, 0) AS duration
FROM tblType t
LEFT JOIN (SELECT type_id, SUM(duration) AS sum_duration
FROM tblRequest
WHERE user_id = '1002'
GROUP BY type_id) r ON t.type_id = r.type_id
Select a.type_id, isnull(sum(b.duration), 0)
From tblType a Left Outer Join tblRequest b
ON a.type_id = b.type_id and b.user_id = 1002
Group by a.type_id