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;
I have a MySQL table with a datetime row. How can I find all groups with at least 5 entries within 10 minutes?
My only idea is to write a program (in whatever language) and loop over the timestamps, check always 5 (..) successive entries, calculate the time span between the last and the first and check whether it is below the limit.
Can this be done using a single SQL query too?
(The scenario is is simplified and the numbers are just examples.)
As requested, here comes an example:
id | timestamp | other_column
---|---------------------|-------------
3 | 2017-01-01 11:00:00 | thank
2 | 2017-01-01 11:01:00 | you
1 | 2017-01-01 11:02:00 | for
* 6 | 2017-01-01 11:20:00 | your
* 5 | 2017-01-01 11:21:00 | efforts
* 4 | 2017-01-01 11:22:00 | to
* 7 | 2017-01-01 11:23:00 | help
* 8 | 2017-01-01 11:24:00 | me
9 | 2017-01-01 11:40:00 | :
10 | 2017-01-01 11:41:00 | )
If the count limit is 5 and the timespan limit is 10 minutes, I'd like to get the entries marked with "*". The "id" column is the primary key of the table, but the order is not always the order of the timestamps. The "other_column" is used for a where clause. The table has about 1 million entries.
Try to break this down logically. Sorry for the psuedo code bits, I'm a little short on time.
select t1.id, t1.timestamp, t2.timestamp
from yourtable t1
inner join yourtable t2 on t2.timestamp >= t1.timestamp and t2.timestamp < (t1.timestamp + 20 minutes)
(plus 20 minutes won't work as is, use appropriate add function)
So this will give you a relatively giant list of all ID's joined to any other id's within a 20 minute time interval (including one row for itself). (add, I'm only picking out the first row of the group at this point, easier just to grab the 'header row' here by this timestamp plus 20 minutes and worry about the rest in the next step) If we group by the ID and time, we get a count of how many rows were within 20 minutes:
select id, t1.timestamp, count(1)
from yourtable t1
inner join yourtable t2 on t2.timestamp >= t1.timestamp and t2.timestamp < (t1.timestamp + 20 minutes)
group by id, t1.timestamp
having count(1) > 4
This will now give you a list of all the ID's and it's timestamp that has itself and 4 other records or more within 20 minutes away from that timestamp. Now it depends on how you want to group from here, if you want each of the 5 lines, we can call the query above a subquery and join it back to the main table to get the rows you want returned.
select t3.*
from
(select id, t1.timestamp, count(1)
from yourtable t1
inner join yourtable t2
on t2.timestamp >= t1.timestamp and t2.timestamp < (t1.timestamp + 20 minutes)
group by id, t1.timestamp
having count(1) > 4) a
inner join yourtable t3 on t3.timestamp >= a.timestamp and t3.timestamp < (a.timestamp + 20 minutes)
And that should give you ID 4-8 and it's info returned (order as you see fit).
My apologies that I don't have the time to test, but the logic should work.
I have a table in my database that contains an ID and DATETIME column, here is some sample data:
ID | DATETIME
1 | 2014-05-06 01:12
1 | 2014-05-06 01:30
1 | 2014-05-06 01:45
1 | 2014-05-06 02:59
2 | 2014-05-06 01:17
2 | 2014-05-06 01:18
2 | 2014-05-06 01:19
2 | 2014-05-06 02:00
I need to produce a query that determines the ID belonging to the object that has the longest time between its DATETIME values, where the time between consecutive DATETIME values does not exceed 20 minutes.
For example, in the sample data, I would want to return 1 as it has DATETIME values from (01:12 - 01:45) without having a consecutive difference of 20 minutes between DATETIME values.
Thanks.
It looks like you will need a self-join. Because if you had 10 entries for an ID, your 20 minute gap might be between entries 3-6 vs 1-4 or even 4-9. So the second instance of the join would be on the same ID and have a date time higher than that of the primary entry, but less than 20 minutes. Then, it could be ordered by the time-gap and limit to the one you want. Something like:
select
YT.ID,
YT.DTColumn,
MAX( YT2.DTColumn ) as MaxDateWithin20Minutes
from
YourTable YT
JOIN YourTable YT2
ON YT.ID = YT2.ID
AND YT.DTColumn < YT2.DTColumn
AND YT2.DTColumn <= date_add( YT.DTColumn, INTERVAL 20 MINUTE )
group by
YT.ID,
YT.DTColumn
order by
timediff(MAX( YT2.DTColumn ), YT.DTColumn) DESC
limit
1
You need to get the next (or previous) value and get the time difference. I think the following does what you want:
select t.*
from (select t.*,
(select t2.datetime
from table t2
where t2.id = t.id and t2.datetime < t.datetime
order by t2.datetime desc
) prev_datetime
from table t
) t
where datetime <= prev_datetime + interval 20 minutes
order by timestampdiff(second, prev_datetime, datetime) desc
limit 1;
I am trying to generate a table in the following format.
Proday | 2014-04-01 | 2014-03-01
--------------------------------
1 | 12 | 17
2 | 6 | 0
7 | 0 | 24
13 | 3 | 7
Prodays (duration between two timestamps) is a calculated value and the data for months is a COUNT. I can output the data for a single month, but am having troubles joining queries to additional months. The index (prodays) may not match for each month. e.g.. 2014-04-01 may not have any data for Prodays 7, whereas 2014-03-01 may not have Proday 2. Should indicate with 0 or null.
I suspect FULL OUTER JOIN is what should do the trick. But have read that's not possible in Mysql?
This is the query to get data for a single month:
SELECT round((protime - createtime) / 86400) AS prodays, COUNT(id) AS '2014-04-01'
FROM `tbl_users` as t1
WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(createtime),'%Y-%m-%d') >= '2014-04-01'
AND DATE_FORMAT(FROM_UNIXTIME(createtime),'%Y-%m-%d') <= LAST_DAY('2014-04-01')
GROUP BY prodays
ORDER BY `prodays` ASC
How can I join/union an additional query to create a column for 2014-03-01?
You want to use conditional aggregation -- that is, move the filtering logic from the where clause to the select clause:
SELECT round((protime - createtime) / 86400) AS prodays,
sum(DATE_FORMAT(FROM_UNIXTIME(createtime),'%Y-%m-%d') >= '2014-04-01' AND
DATE_FORMAT(FROM_UNIXTIME(createtime),'%Y-%m-%d') <= LAST_DAY('2014-04-01')
) as `2014-04-01`,
sum(DATE_FORMAT(FROM_UNIXTIME(createtime),'%Y-%m-%d') >= '2014-03-01' AND
DATE_FORMAT(FROM_UNIXTIME(createtime),'%Y-%m-%d') <= LAST_DAY('2014-03-01')
) as `2014-03-01`
FROM `tbl_users` as t1
WHERE status = 1
GROUP BY prodays
ORDER BY `prodays` ASC;
A rental system uses a booking table to store all bookings and reservations:
booking | item | startdate | enddate
1 | 42 | 2013-10-25 16:00 | 2013-10-27 12:00
2 | 42 | 2013-10-27 14:00 | 2013-10-28 18:00
3 | 42 | 2013-10-30 09:00 | 2013-11-01 09:00
…
Let’s say a user wants to rent item 42 from 2013-10-27 12:00 until 2013-10-28 12:00 which is a period of one day. The system will tell him, that the item is not available in the given time frame, since booking no. 2 collides.
Now I want to suggest the earliest rental date and time when the selected item is available again. Of course considering the user’s requested period (1 day) beginning with the user’s desired date and time.
So in the case above, I’m looking for an SQL query that returns 2013-10-28 18:00, since the earliest date since 2013-10-27 12:00 at which item 42 will be available for 1 day, is from 2013-10-28 18:00 until 2013-10-29 18:00.
So I need to to find a gap between bookings, that is big enough to hold the user’s reservation and that is as close a possible to the desired start date.
Or in other words: I need to find the first booking for a given item, after which there’s enough free time to place the user’s booking.
Is this possible in plain SQL without having to iterate over every booking and its successor?
If you can't redesign your database to use something more efficient, this will get the answer. You'll obviously want to parameterize it. It says find either the desired date, or the earliest end date where the hire interval doesn't overlap an existing booking:
Select
min(startdate)
From (
select
cast('2013-10-27 12:00' as datetime) startdate
from
dual
union all
select
enddate
from
booking
where
enddate > cast('2013-10-27 12:00' as datetime) and
item = 42
) b1
Where
not exists (
select
'x'
from
booking b2
where
item = 42 and
b1.startdate < b2.enddate and
b2.startdate < date_add(b1.startdate, interval 24 hour)
);
Example Fiddle
SELECT startfree,secondsfree FROM (
SELECT
#lastenddate AS startfree,
UNIX_TIMESTAMP(startdate)-UNIX_TIMESTAMP(#lastenddate) AS secondsfree,
#lastenddate:=enddate AS ignoreme
FROM
(SELECT startdate,enddate FROM bookings WHERE item=42) AS schedule,
(SELECT #lastenddate:=NOW()) AS init
ORDER BY startdate
) AS baseview
WHERE startfree>='2013-10-27 12:00:00'
AND secondsfree>=86400
ORDER BY startfree
LIMIT 1
;
Some explanation: The inner query uses a variable to move the iteration into SQL, the outer query finds the needed row.
That said, I would not do this in SQL, if the DB structure is like the given. You could reduce the iteration count by using some smort WHERE in the inner query to a sane timespan, but chances are, this won't perform well.
EDIT
A caveat: I did not check, but I assume, this won't work, if there are no prior reservations in the list - this should not be a problem, as in this case your first reservation attempt (original time) will work.
EDIT
SQLfiddle
Searching for overlapping date ranges generally yields poor performance in SQL. For that reason having a "Calendar" of available slots often makes things a lot more efficient.
For example, the booking 2013-10-25 16:00 => 2013-10-27 12:00 would actually be represented by 44 records, each one hour long.
The "gap" until the next booking at 2013-10-27 14:00 would then be represented by 2 records, each one hours long.
Then, each record could also have the duration (in time, or number of slots) until the next change.
slot_start_time | booking | item | remaining_duration
------------------+---------+------+--------------------
2013-10-27 10:00 | 1 | 42 | 2
2013-10-27 11:00 | 1 | 42 | 1
2013-10-27 12:00 | NULL | 42 | 2
2013-10-27 13:00 | NULL | 42 | 1
2013-10-27 14:00 | 2 | 42 | 28
2013-10-27 15:00 | 2 | 42 | 27
... | ... | ... | ...
2013-10-28 17:00 | 2 | 42 | 1
2013-10-28 18:00 | NULL | 42 | 39
2013-10-28 19:00 | NULL | 42 | 38
Then your query just becomes:
SELECT
*
FROM
slots
WHERE
slot_start_time >= '2013-10-27 12:00'
AND remaining_duration >= 24
AND booking IS NULL
ORDER BY
slot_start_time ASC
LIMIT
1
OK this isn't pretty in MySQL. That's because we have to fake rownum values in subqueries.
The basic approach is to join the appropriate subset of the booking table to itself offset by one.
Here's the basic list of reservations for item 42, ordered by reservation time. We can't order by booking_id, because those aren't guaranteed to be in order of reservation time. (You're trying to insert a new reservation between two existing ones, eh?) http://sqlfiddle.com/#!2/62383/9/0
SELECT #aserial := #aserial+1 AS rownum,
booking.*
FROM booking,
(SELECT #aserial:= 0) AS q
WHERE item = 42
ORDER BY startdate, enddate
Here is that subset joined to itself. The trick is the a.rownum+1 = b.rownum, which joins each row to the one that comes right after it in the booking table subset. http://sqlfiddle.com/#!2/62383/8/0
SELECT a.booking_id, a.startdate asta, a.enddate aend,
b.startdate bsta, b.enddate bend
FROM (
SELECT #aserial := #aserial+1 AS rownum,
booking.*
FROM booking,
(SELECT #aserial:= 0) AS q
WHERE item = 42
ORDER BY startdate, enddate
) AS a
JOIN (
SELECT #bserial := #bserial+1 AS rownum,
booking.*
FROM booking,
(SELECT #bserial:= 0) AS q
WHERE item = 42
ORDER BY startdate, enddate
) AS b ON a.rownum+1 = b.rownum
Here it is again, showing each reservation (except the last one) and the number of hours following it. http://sqlfiddle.com/#!2/62383/15/0
SELECT a.booking_id, a.startdate, a.enddate,
TIMESTAMPDIFF(HOUR, a.enddate, b.startdate) gaphours
FROM (
SELECT #aserial := #aserial+1 AS rownum,
booking.*
FROM booking,
(SELECT #aserial:= 0) AS q
WHERE item = 42
ORDER BY startdate, enddate
) AS a
JOIN (
SELECT #bserial := #bserial+1 AS rownum,
booking.*
FROM booking,
(SELECT #bserial:= 0) AS q
WHERE item = 42
ORDER BY startdate, enddate
) AS b ON a.rownum+1 = b.rownum
So, if you're looking for the starting time and ending time of the earliest twelve-hour slot you can use that result set to do this: http://sqlfiddle.com/#!2/62383/18/0
SELECT MIN(enddate) startdate, MIN(enddate) + INTERVAL 12 HOUR as enddate
FROM (
SELECT a.booking_id, a.startdate, a.enddate,
TIMESTAMPDIFF(HOUR, a.enddate, b.startdate) gaphours
FROM (
SELECT #aserial := #aserial+1 AS rownum,
booking.*
FROM booking,
(SELECT #aserial:= 0) AS q
WHERE item = 42
ORDER BY startdate, enddate
) AS a
JOIN (
SELECT #bserial := #bserial+1 AS rownum,
booking.*
FROM booking,
(SELECT #bserial:= 0) AS q
WHERE item = 42
ORDER BY startdate, enddate
) AS b ON a.rownum+1 = b.rownum
) AS gaps
WHERE gaphours >= 12
here is the query, it will return needed date, obvious condition - there should be some bookings in table, but as I see from question - you do this check:
SELECT min(enddate)
FROM
(
select a.enddate from table4 as a
where
a.item=42
and
DATE_ADD(a.enddate, INTERVAL 1 day) <= ifnull(
(select min(b.startdate)
from table4 as b where b.startdate>=a.enddate and a.item=b.item),
a.enddate)
and
a.enddate>=now()
union all
select greatest(ifnull(max(enddate), now()),now()) from table4
) as q
you change change INTERVAL 1 day to INTERVAL ### hour
If I have understood your requirements correctly, you could try self-JOINing book with itself, to get the "empty" spaces, and then fit. This is MySQL only (I believe it can be adapted to others - certainly PostgreSQL):
SELECT book.*, TIMESTAMPDIFF(MINUTE, book.enddate, book.best) AS width FROM
(
SELECT book.*, MIN(book1.startdate) AS best
FROM book
JOIN book AS book1 USING (item)
WHERE item = 42 AND book1.startdate >= book.enddate
GROUP BY book.booking
) AS book HAVING width > 110 ORDER BY startdate LIMIT 1;
In the above example, "110" is the looked-for minimum width in minutes.
Same thing, a bit less readable (for me), a SELECT removed (very fast SELECT, so little advantage):
SELECT book.*, MIN(book1.startdate) AS best
FROM book
JOIN book AS book1 ON (book.item = book1.item AND book.item = 42)
WHERE book1.startdate >= book.enddate
GROUP BY book.booking
HAVING TIMESTAMPDIFF(MINUTE, book.enddate, best) > 110
ORDER BY startdate LIMIT 1;
In your case, one day is 1440 minutes and
SELECT book.*, MIN(book1.startdate) AS best FROM book JOIN book AS book1 ON (book.item = book1.item AND book.item = 42) WHERE book1.startdate >= book.enddate GROUP BY book.booking HAVING TIMESTAMPDIFF(MINUTE, book.enddate, best) >= 1440 ORDER BY startdate LIMIT 1;
+---------+------+---------------------+---------------------+---------------------+
| booking | item | startdate | enddate | best |
+---------+------+---------------------+---------------------+---------------------+
| 2 | 42 | 2013-10-27 14:00:00 | 2013-10-28 18:00:00 | 2013-10-30 09:00:00 |
+---------+------+---------------------+---------------------+---------------------+
1 row in set (0.00 sec)
...the period returned is 2, i.e., at the end of booking 2, and until "best" which is booking 3, a period of at least 1440 minutes is available.
An issue could be that if no periods are available, the query returns nothing -- then you need another query to fetch the farthest enddate. You can do this with an UNION and LIMIT 1 of course, but I think it would be best to only run the 'recovery' query on demand, programmatically (i.e. if empty(query) then new_query...).
Also, in the inner WHERE you should add a check for NOW() to avoid dates in the past. If expired bookings are moved to inactive storage, this could be unnecessary.