sql group by with group condition - mysql

I have a product table Like:
id p_name s_date
1 A 2014-10-10
2 B 2014-10-02
3 A 2014-10-08
4 A 2014-10-11
5 B 2014-10-08
I need to group on p_name. query like
SELECT * FROM product GROUP BY p_name;
This query retrieving record 1 and 2. But I need to check p_date with current date when grouping on p_name. If current date is '2014-10-11' I need record 4 for product group 'A' because record 4 matched with current date and record 2 for product group 'B' because no one of p_name 'B' matched with current date. A query to check p_date with current date when grouping on p_name. i need to retrieve the record from group which matched with current date, if no record matched with current date then the record of minimum date.

Maybe you can have a check with the MySQL Having clause.. and modify the query to somewhat like...
SELECT * FROM product GROUP BY p_name HAVING p_date = CURDATE();

Related

Which bikes were live on December 8?

I am new to SQL (MySql) and I got stuck with this problem with one of my assignments and couldn't get answer from anyone or searching online about the subject. The question goes like:
Which bikes were live (is_live = TRUE) on December 8? (Display only bike_id).
Note: The Table does not have a row entry for December 8, and that is as intended
Name Type Description
id int LOG ID(PK)
bike_id int id of the bike
is_live boolean flag to indicate whether bike is live (TRUE/FALSE)
updated_on Date Date on which status of bike was updated
BIKE_LIVE_LOG table:
id bike_id is_live updated_on
1 1 TRUE 2018-12-01
2 2 TRUE 2018-12-01
3 3 TRUE 2018-12-01
4 3 FALSE 2018-12-02
5 2 FALSE 2018-12-05
6 3 TRUE 2018-12-10
I couldn't move forward with the question as I am not even getting the approach for it with my current knowledge.
I have used this query to generate the the last_update_date grouped by each bike_id.
select bll.bike_id, max(bll.updated_on) as last_update_date
from bike_live_log as bll
where bll.updated_on <= '2018-12-08'
group by bll.bike_id;
The output will be 1.
I'll try to help you get to the last step. You're really, really close!
You were correct in going down the road of finding the most recent updated_on date for each bike_id. It doesn't matter how many times a bike has been turned on or off; you really only care about the most recent status prior to the date you're interested in.
With your current query, you already know when each bike_id was last updated prior to December 8th.
Next, you can use that information to find out what the is_live value was for each of those bike_id values as of that last_update_date.
You can do that by using your existing query as a sub-query, or a CTE if you prefer, and join back to your main table again. Your JOIN criteria will be bike_id to bike_id and updated_on to last_update_date. By joining on the dates, you'll only return a single record for each bike_id, and that record will be the one you're interested in.
After you have your JOIN put together, you'll just need to add a WHERE clause to limit your result set to the rows where is_live = 'TRUE', which will return just bike_id of 1.
Your requirement can be expressed more data-centrically as find bikes whose last known status on or before Dec 8 was live.
This is one way (IMHO the most readable way) to express that in SQL:
select bike_id
from bike_live_log bll
where updated_on = (
select max(updated_on)
from bike_live_log
where bike_id = bll.bike_id
and updated_on <= '2018-12-08'
)
and is_live
The (corelated) subquery finds the date of the last update on or before Dec 8 for the current row of the outer query. If there's no such row, null will be returned, which won't match any rows from the outer query so, only bikes that have data on or before Dec 8 will be returned.
tl;dr
Here is the MySQL code you need:
SELECT bike_id FROM (
SELECT * FROM (
SELECT * FROM bikes
WHERE updated_on < '20181209'
ORDER BY updated_on DESC, id DESC
) AS sub
GROUP BY bike_id
) AS sub2
WHERE is_live = true
Why does this work?
We need to break down the question a little bit. I like to start from a position of "how can I get the information I need in a format that makes sense to me (as a human)?".
So the first thing I did was to get a list of all bikes with updated_on dates before the 9th Dec (i.e. were updated on the 8th Dec or before). I also ordered this by updated_on field so I (as a human) could easily see the "latest" record which will tell me the most recent status of each bike on or before the 8th Dec:
SELECT * FROM bikes
WHERE updated_on < '20181209'
ORDER BY updated_on DESC, id DESC
From this I can see there are 5 records of bike status changes before the 9th Dec. I can easily see that for each bike there are multiple "update" records, but I can now start from the top of the list and each bike id I encounter is the status on the 8th Dec.
Additionally I included an order_by for the record id. This is necessary because there could be multiple updates per day. The date itself won't tell us which of those updates was the latest ON THE SAME DAY, so we use the ID to determine that. (assuming it's chronologically incremental).
Now we have a list of all statuses and bike ids in the database before the 9th Dec we need to limit this to only one record per bike. This is easy, we can wrap the original query with a new query with a Group By directive on the bike_id.
SELECT * FROM (
SELECT * FROM bikes
WHERE updated_on < '20181209'
ORDER BY updated_on DESC, id DESC
) AS sub
GROUP BY bike_id
Group By in MySQL selects the first record it comes across for each group. We have already ordered the list by date and id in the original query, so the first record for each bike_id will be the latest status for that bike as of the 8th Dec.
Now, all that's left for us to do is to select the bike_id and filter out non-live bikes by using WHERE is_live = true on this latest query. That's how we end up with the query at the start of this answer:
SELECT bike_id FROM (
SELECT * FROM (
SELECT * FROM bikes
WHERE updated_on < '20181209'
ORDER BY updated_on DESC, id DESC
) AS sub
GROUP BY bike_id
) AS sub2
WHERE is_live = true
I hope this helps.
SELECT
bike_id,
MAX(CASE WHEN last_live_date <= '2018-12-08' AND ( last_not_live_date < last_live_date OR last_not_live_date IS NULL) THEN 1 ELSE 0 END) AS final_status
FROM
(
SELECT
bike_id,
MAX(CASE WHEN is_live = TRUE THEN updated_on END) AS last_live_date,
MAX(CASE WHEN is_live = FALSE THEN updated_on END) AS last_not_live_date
FROM `BIKE_LIVE_LOG`
WHERE updated_on <= '2018-12-08'
GROUP BY bike_id
) AS a
GROUP BY bike_id
HAVING final_status = 1;
with A as (select * from bike_live_log
where updated_on <= '2018-12-08'),
B as (select bike_id,max(updated_on) as updated from A
group by bike_id)
select A.bike_id from A inner join B
on A.updated_on = B.updated
where is_live = True;

get the most recent two dates for a customer MySQL

I need to retrieve the last two dates for customers with entries in at least two different dates, implying there are some customer who had purchased only in one date, the table is as follow
client_id date
1 2016-07-02
1 2016-07-02
1 2016-06-01
2 2015-06-01
as a response, I would get
client_id previous_date last_date
1 2016-06-01 2016-07-02
important:
a client can have multiple entries for the same date
a client can have entries only for one date, such customer should be discarded
Try this: group by the client_id column, with a having of count(*) > 1 to find results with more than one result. Then do a check of the min and max date, to ensure they aren't the same. Then just select the date, and order the results by date in desc order, with a limit of 2.
select
date
from
my_table
group by
client_id
having
min(date) <> max(date)
and count(*) > 1
order by
date desc
limit 2

SQL multiple occurrences of same variables and max function

Here is my table
serial Number Job Date
1 12/12/2013
1 1/2/2014
1 2/23/2010
1 3/15/2010
2 11/11/2012
2 6/5/2011
2 1/23/2010
I want only serial number which have job date older that 12/31/2013, in the above table it should be only the serial number 2 and the latest job date which is 11/11/2012
The result table should be
serial Number Job Date
2 11/11/2012
#ohho
using where will not work since it will also display the row,
Serial_number Job Date
1 12/12/2013
2 11/11/2012
select serial_number, max(job_date) from computers
where job_date < '2013-12-31'
Group by serial_number
Question is not very clear. As I understand, you are looking for all the serialnumbers which do not have any jobdate greater than or equal to 12/31/3013.
If so, below query will work.
select serialnumber -- if you want the biggest serialnumber, use max(serialnumber)
from myTable tt
where not exists (select * from myTable where serialnumber = tt.serialnumber
and jobdate > '12/31/3013')

how to write a Query in Mysql

I have 2 tables.ms_expese and track_expense.Using this table generate a fact table
I want the expense_name in ms_expense,expense_amount from track_expense.
I want to get the sum of expense_amount for a particular expense_name based on date.The date in the order of 1,2...12 as month id
SELECT DATE_Format(a.date,'%b') as month_id,b.expense_name AS expense_type, sum(a.expense_amount) AS expense_amount FROM ms_expense b JOIN track_expense a on a.`expense_id`=b.`expense_id` group by DATE_Format(a.date,'%b')
how to put the month id in the order of 1,2,..12 and my date format y-m-d
I get the month in apr,aug and so on but i need jan as 1,feb as 2
I have 25 expenses(expense name).In this query i got the total expense amount of first expense only.I want the total expense of all expenses in every month
CREATE TABLE fact AS
(<your select query>)
Your select query can be in the following form
SELECT MONTH(date)as month_id,expense_name,sum(expense_amount)
FROM ms_expense JOIN track_expense using (expense_id)
group by expense_name,MONTH(date)

Get closest value lower than a specific value and group by

Is there a possibility to get the closest value lower than a specific value with a group function without a join?
date productId stock
2014-12-27 1 10
2014-12-31 1 20
2015-01-05 1 30
2014-12-28 2 10
2015-01-04 2 20
The value is for example the date and should be lower than 2015-01-01 but the highest date value and the result should be ordered by the stock sac, so the result should be:
date productId stock
2014-12-28 2 10
2014-12-31 1 20
Of course, this could be solves with a join, but a join is slower in large tables, isn't it?
You're looking for the last day of 2014, it seems, for each distinct product id.
You do that with
SELECT MAX(date) date, product_id
FROM yourtable
WHERE date < '2015-01-01'
GROUP BY product_id
That gives you a collection of date, product_id. A compound index on (date, product_id) will make this query very efficient to evaluate.
Then you join that to your main table, like so.
SELECT a.*
FROM yourtable AS a
JOIN (
SELECT MAX(date) date, product_id
FROM yourtable
WHERE date < '2015-01-01'
GROUP BY product_id
) AS b USING(date,product_id)
ORDER BY a.product_id, a.date
and that retrieves the detail records for the last item in 2014. The same compound index will accelerate the JOIN.
You're worried about JOIN performance, and that's legitimate. But it can be improved with proper indexing. There really isn't a better way to do it.