Group by Day of Month - mysql

Say I have a table logins with just the bigint id ,a date_login of type datetime, and fk_user of type bigint. I need to select the first and last logins for each day of the month in the specified year.
I was guessing something like:
select *theDayOfTheMonth*,min(date_login), max(date_login)
from logins
where year(date_login) = *theYearInput* and
and fk_user = *theKeySpecified* and
month(date_login) = *theMonthInput*
group by *theDayOfTheMonth*
but I don't know how to group by that day of month. How can I do this?

You were close. It will look something like this:
SELECT DATE(date_login) as dayOfMonth, min(date_login), max(date_login)
FROM logins
WHERE year(date_login) = #theYearInput
and fk_user = #theKeySpecified
and month(date_login) = #theMonthInput
GROUP BY DATE(date_login)
Alternatively you might use the Day() function instead, which should still work because the query is limited to a single specific month via conditions in the WHERE clause:
SELECT DAY(date_login) as dayOfMonth, min(date_login), max(date_login)
FROM logins
WHERE year(date_login) = #theYearInput
and fk_user = #theKeySpecified
and month(date_login) = #theMonthInput
GROUP BY DAY(date_login)
Functionally, given these conditions the only difference between them is the format of the first column in the output. However, if you later need to expand across larger date ranges the first version will handle that better.

The function that you are looking for is DayOfMonth()
create table logins(
fk_user bigint,
date_login datetime);
insert into logins values
(1,'2022-01-01 09:00:00'),(1,'2022-01-01 18:00:00'),(1,'2022-01-02 08:00:00'),(1,'2022-01-02 16:00:00')
select
DayOfMonth(date_login) day,
min(date_login) first_login,
max(date_login) last_login
from logins
where year(date_login) = 2022
and fk_user = 1 and
month(date_login) = 1
group by DayOfMonth(date_login);
day | first_login | last_login
--: | :------------------ | :------------------
1 | 2022-01-01 09:00:00 | 2022-01-01 18:00:00
2 | 2022-01-02 08:00:00 | 2022-01-02 16:00:00
db<>fiddle here

Related

How to select rows in every day between two dates using MySQL

I want to keep track of users logged in every day between two dates.
Let's say I have a table my_table like this:
user_id
login_datetime
1
2021-10-02 10:00:00
1
2021-10-02 12:00:00
2
2021-10-02 12:20:00
1
2021-10-03 17:00:00
1
2021-10-04 22:00:00
2
2021-10-04 23:00:00
and given date range is from '2021-10-02' to '2021-10-04'.
I want to get user_id = 1 in this case, because user_id = 2 is not logged in at '2021-10-03'
result
user_id
login_date
1
2021-10-02
1
2021-10-03
1
2021-10-04
Is there any solution for this?
One approach uses aggregation:
SELECT user_id
FROM my_table
WHERE login_datetime >= '2021-10-02' AND login_datetime < '2021-10-05'
GROUP BY user_id
HAVING COUNT(DISTINCT DATE(login_datetime)) = 3; -- range has 3 dates in it
Demo
The HAVING clause asserts that any matching user must have 3 distinct dates present, which would imply that such a user would have login activity on all dates from 2021-10-02 to 2021-10-04 inclusive.
Edit:
To get the exact output in your question, you may use:
SELECT DISTINCT user_id, DATE(login_datetime) AS login_date
FROM my_table
WHERE user_id IN (
SELECT user_id
FROM my_table
WHERE login_datetime >= '2021-10-02' AND login_datetime < '2021-10-05'
GROUP BY user_id
HAVING COUNT(DISTINCT DATE(login_datetime)) = 3
);

Select nearest date in the interval

I'm trying to select rows in which 3+ posts is in the interval 14 days.
For example:
User | id_post | date
1 | 12 | 2018-01-01
1 | 13 | 2018-01-05
1 | 14 | 2018-01-21
1 | 15 | 2018-01-27
1 | 16 | 2018-01-29
2 | 17 | 2018-01-01
2 | 18 | 2018-01-20
2 | 19 | 2018-02-17
2 | 20 | 2018-03-07
2 | 21 | 2018-04-29
User = OwnerUserId
date = CreationDate
In this case I need to return just User 1 because he has posts which are in 14 days.
Please, help me how I can get it. Thank you
Update: A user should have posts which were published in the interval of 14 days. It can be more, for example if the last day is in 2019 but in 2018 there was 3posts published within 14 days - it's ok
now i have (data get from data.stackexchange stackoverflow) and tried to apply
select OwnerUserId from Posts as p
where OwnerUserId in (select Users.id from Users WHERE YEAR (Users.CreationDate) >= 2017)
AND YEAR (p.CreationDate) >= 2018
AND p.Tags like '%sql%'
join (select OwnerUserId, CreationDate as startdate, dateadd(day,14,CreationDate) as enddate
from Posts) as r
on p.OwnerUserId = r.OwnerUserId and p.CreationDate between r.startdate and r.enddate
group by p.OwnerUserId, CreationDate
having count(*) >= 3
but it replies
Incorrect syntax near the keyword 'join'.
Incorrect syntax near the keyword 'as'.
I'm a begginner here and in the sql, so i dont exactly know how to combine my previous 'filtr' and current join with date
I'll not tell you the solution, but give you some pseudo-code and you figure out how to code it in SQL-
a) You should restrict your data for just 14 days.
b) Now, make groupings by User and find the count of records/lines present (for each User).
c) Now, again do a filter check to find users whose count of records is greater than 3.
Now, tell us which SQL keywords will be used for each points above.
I think something like
select p.user_id
from posts p
join (select user_id, xdate start_date, date_add(xdate, interval 14 day) end_date
from posts) r
on p.user_id = r.user_id and p.xdate between r.start_date and r.end_date
group by user_id, start_date
having count(*) >= 3
can help. It may not be the best possible solution, but it works.
Check it on SQL Fiddle
If you just want to select users by id you may try
Select id_post, date from yourtable where user = 2 order by id DESC limit 10;
You should have Colum called id with auto increment so new posts will have higher id so when it's sorted in descending it will start with post with higher id also you should have index on that id colum auto increment and index
If you don't want to use the above method then you will do that with date range like this
$date = gmdate() - (3600*24); 24 is 24 hours past
Select id_post, title from mutable where add_date > 'value of $date'
In both cases you should have index on user id
The second query is what you need but you should get the date from the equation first then apply it to the query
First, I think you mean user 1 not 2.
In MySQL 8+, this is pretty easy. If you want the first such post:
select t.*
from (select t.*,
lead(date, 2) over (partition by user order by date) as next_date2
from t
) t
where next_date2 <= date + interval 14 day;

group by year on multiple date columns mysql

I have table as following:
hours | ... | task_assigned | task_deadline | task_completion
----------------------------------------------------------------
123 | ... | 2019-08-01 | - | -
234 | ... | - | 2018-08-01 | 2019-08-01
145 | ... | 2017-08-01 | 2017-08-01 | 2018-01-01
I want to calculate total hours for each year, i.e. grouping by year.
Currently I'm only taking into account task_completion field.
If there's no value in task_completion field, the record is not included in SUM calculation.
To elaborate further, say for year 2019, row 1 and 1 both should be considered. Hence the total hours should be 123 + 234 = 357.
And for year 2018, row 2 and 3.
Similarly, for year 2017, row 3.
SELECT YEAR(task_completion) as year, ROUND(SUM(total_hours), 2) as hours
FROM task
GROUP BY year
HAVING year BETWEEN '$year_from' AND '$year_to'
The resultset:
year | hours
--------------------
2017 | <somevalue>
2018 | <somevalue>
2019 | <somevalue>
How can I include other two date fields too?
You want to consider each row once for each of its years. Use UNION to get these years:
select year, round(sum(total_hours), 2) as hours
from
(
select year(task_assigned) as year, total_hours from task
union
select year(task_deadline) as year, total_hours from task
union
select year(task_completion) as year, total_hours from task
) years_and_hours
group by year
having year between $year_from and $year_to
order by year;
If you want to consider a row with one year twice or thrice also as often in the sum, then change UNION to UNION ALL.
Basically, you want to unpivot the data. I will assume that the - represents a NULL value and your dates are real dates.
select year(dte) as year, sum(total_hours) as hours
from ((select task_assigned as dte, total_hours
from task
) union all
(select task_deadline, total_hours
from task
) union all
(select task_completion, total_hours
from task
)
) d
where dte is not null
group by year(dte)
order by year(dte);
Based on your sample data, the round() is not necessary so I removed it.
If you want to filter for particular years, the filtering should be in a where clause -- so it filters the data before aggregation.
Change the where to:
where year(dte) >= ? and year(dte) <= ?
or:
where dte >= ? and dte <= ?
to pass in the dates.
The ? are for parameter placeholders. Learn how to use parameters rather than munging query strings.
This answer is no langer valid with the updated request.
If I understand correctly, you want to use task_assigned if the task_completion is still null. Use COALEASCE for this.
SELECT
YEAR(COALESCE(task_completion, task_assigned)) as year,
ROUND(SUM(total_hours), 2) as hours
FROM task
GROUP BY year
HAVING year BETWEEN $year_from AND $year_to
ORDER BY year;
(I don't think you actually want to use task_deadline, too, for how could a task get completed before getting assigned first? If such can occur, then include it in the COALESCE expression. Probably: COALESCE(task_completion, task_assigned, task_deadline)` then.)

Count unique days grouped by user

I have the following SQL table
|user|log_date |
| 2 |2016-06-23 10:55:52 |
| 2 |2016-06-23 10:55:54 |
| 2 |2016-06-24 10:53:54 |
| 2 |2016-06-24 10:54:54 |
and so on with many other users and log_dates. What I want is to check for whole month :
where left(log_date,7)="2016-06"
But I want to count one day only once per day. So the result for my table in this example should be :
|user|count of unique days|
| 2 | 2 |
and I want it to be grouped by users. So for every user in table I want to count unique days.
Can anybody give me a hint?
Writing the condition this way allows the use of an index...
SELECT t.user
, COUNT(DISTINCT DATE(t.log_date)) unique_days
FROM my_table t
WHERE t.log_date BETWEEN '2016-06-01 00:00:00' AND '2016-06-31 23:59:59' -- or t.log_date >= '2016-06-01 00:00:00' AND t.log_date < '2016-07-01 00:00:00'
GROUP
BY t.user;
(Not sure why Sagi deleted their answer after correcting it)
Try this:
select
user,
count(distinct day(log_date)) as `count of unique days`
from yourtable
where left(log_date, 7) = '2016-06'
group by user
SQLFiddle Demo

SQL query to check if column includes some data

I need to select all free rooms from hotel DB and I think I can do it with two steps:
bookings = select * from booking where booking.startDate>=selectedStartDate and booking.endDate=<selectedEndDate.
pseudo query: select * from room where room.room_id not includes bookings.room_id.
I wrote my second query like pseudo query because I can't find how I can do what I want to do. How to check that bookings doesn't includes room_id's?
My booking table looks like:
+-----------+------------+------------+---------+---------+
| bookingId | startDate | endDate | room_id | guestId |
+-----------+------------+------------+---------+---------+
| 1 | 2016-03-12 | 2016-03-22 | 1 | 1 |
| 2 | 2016-03-12 | 2016-03-22 | 2 | 2 |
+-----------+------------+------------+---------+---------+
You could transform the first query to a subquery of the second query by using the not in operator:
SELECT *
FROM room
WHERE room.room_id NOT IN (SELECT room_id
FROM booking
WHERE startDate >= selectedEndDate AND
endDate <= selectedStartDate)
If you want rooms free during a period of time, use not exists. The correct logic is:
select r.*
from room r
where not exists (select 1
from booking b
where $startdate <= b.enddate and $enddate >= b.startdate
);
Two periods overlap when one starts before the second ends and the first ends after the second starts.
Note that the <= and >= might be strict inequalities, depending on whether the first and last dates are included in the period.
You can use a more optimized query
SELECT * FROM room JOIN booking ON room.room_id = booking.room_id
WHERE booking.startDate >= selectedStartDate AND booking.endDate <= selectedEndDate