Select before specific date from one day only - mysql

I need to retrieve data from MySQL from one day, but I don't know the interval between two rows (there may be 4 rows from one day and a gap of a week for example).
Let's say I have: (following isn't code)
3 rows where date = 2015-06-15
1 row where date = 2015-06-09
4 rows where date = 2015-06-05
I want to retrieve all 4 rows from 2015-06-05 with
[...] WHERE `date` < '2015-06-07'
or only one row from 2015-06-09 with
[...] WHERE `date` < '2015-06-14'
Is that possible with SQL only?

If I understand correctly, you want to receive one days worth of rows before a given date. I think that would be:
SELECT t.*
FROM table t
WHERE date = (SELECT MAX(t2.date) FROM table t2 WHERE t2.`date` < '2015-06-07')

I think you want something like this:
select * from table
where date = (select max(date) from table where date < '2015-06-14')

Yes. You can do like this:
(SELECT * FROM `table` WHERE DATE(`date`) = '2015-06-15' LIMIT 0, 3)
UNION
(SELECT * FROM `table` WHERE DATE(`date`) = '2015-06-09' LIMIT 0, 1)
UNION
(SELECT * FROM `table` WHERE DATE(`date`) = '2015-06-09' LIMIT 0, 4)
UNION
SELECT * FROM `table` WHERE DATE(`date`) < '2015-06-07'
UNION
(SELECT * FROM `table` WHERE DATE(`date`) < '2015-06-14' LIMIT 0, 1)

Related

(MySQL 5.7) How to combine my two queries?

How can I combine my two queries in MySQL 5.7:
(windows functions doesn't work)
1: This query is finding all dialogues where more than 10 messages and spaced at least an hour apart.
SELECT `dialog_id`
FROM `messages`
GROUP BY `dialog_id`
HAVING COUNT(*) >= 10
AND MIN(`timestamp`) + INTERVAL 1 HOUR < MAX(`timestamp`)
2: The second query selects two last rows for each dialogue.
SELECT * FROM messages tbl WHERE
(SELECT COUNT(*) FROM messages tbl1 WHERE tbl1.dialog_id = tbl.dialog_id AND tbl1.id >= tbl.id AND
(user_from = :user_from OR user_to = :user_to)) <= 2 ORDER BY dialog_id DESC
So, what I want is to select last two rows for each dialogue that lasted more than an hour and where more than 10 messages were sent.
Though I didn't got you properly is this what you are looking for:
SELECT * FROM messages tbl WHERE
(SELECT COUNT(*) FROM messages tbl1 WHERE tbl1.dialog_id = tbl.dialog_id AND tbl1.id >= tbl.id AND
(user_from = :user_from OR user_to = :user_to)) <= 2
and dialog_id in (SELECT `dialog_id`
FROM `messages`
GROUP BY `dialog_id`
HAVING COUNT(*) >= 10
AND MIN(`timestamp`) + INTERVAL 1 HOUR < MAX(`timestamp`))
ORDER BY dialog_id DESC
One way you can accomplish this with a subquery:
SELECT *
FROM messages tbl
WHERE (
SELECT COUNT(*)
messages tbl1
tbl1.dialog_id = tbl.dialog_id
tbl1.id >= tbl.id
(user_from = :user_from OR user_to = :user_to)) <= 2
and dialog_id in (
SELECT `dialog_id`
FROM `messages`
GROUP BY `dialog_id`
HAVING COUNT(*) >= 10
AND MIN(`timestamp`) + INTERVAL 1 HOUR < MAX(`timestamp`))
ORDER BY dialog_id DESC
You might have to adjust it slightly, since I don't have the full structure of your tables, but the principle would go as follows: get all the dialogue_ids matching your 10 and over an hour criteria, then use it to limit the messages returned from your "Get the most recent two" logic.

Getting all previous records of table by date MySQL

My table currently has 21000 records, it's daily updated and almost 300 entries are inserted. Now, what I want is to have a query which will fetch the counts of elements that my table had for the previous 10 days, so it returns:
26000
21300
21000
etc
Right now, I wrote this:
"SELECT COUNT(*) from tbl_task where `task_start_time` < '2020-12-01'"
And it returns 21000 but only for 1 day. I want by query to return records according to 10 days.
However, this does it for only 1 day.
edit : database flavor is mysql and date column is date not datetime
The most efficient method may be aggregation and cumulative sums:
select date(task_start_time) as dte, count(*) as cnt_on_day,
sum(count(*)) over (order by date(task_start_time)) as running_cnt
from tbl_task
group by dte
order by dte desc
limit 10;
This returns the last 10 days in the data. You can easily adjust to more days if you like -- in fact all of them -- without much trouble.
I don't know if I'm wrong, but could you not simple add a GROUP BY - statement? Like:
"SELECT COUNT(*) from tbl_task where `task_start_time` < '2020-12-01' GROUP
BY task_start_time"
EDIT:
This should only work if task_start_time is a date, not if it is a datetime
EDIT2:
If it is a datetime you could use the date function:
SELECT COUNT(*) from tbl_task where `task_start_time` < '2020-12-01' GROUP
BY DATE(task_start_time)
You can use UNION ALL and date arithmetic.
SELECT count(*)
FROM tbl_task
WHERE task_start_time < current_date
UNION ALL
SELECT count(*)
FROM tbl_task
WHERE task_start_time < date_sub(current_date, INTERVAL 1 DAY)
...
UNION ALL
SELECT count(*)
FROM tbl_task
WHERE task_start_time < date_sub(current_date, INTERVAL 9 DAY);
Edit:
You might also join a derived table that uses FROM-less SELECTs and UNION ALL to get the days to look back and then aggregate. This might be a little easier to construct dynamically. (But it may be slower I suspect.)
SELECT count(*)
FROM (SELECT 0 x
UNION ALL
SELECT 1
...
UNION ALL
SELECT 9)
INNER JOIN tbl_task t
ON t.task_start_time < date_sub(current_date, INTERVAL x.x DAY)
GROUP BY x.x;
In MySQL version 8+ you can even use a recursive CTE to construct the table with the days.
WITH RECURSIVE x
AS
(
SELECT 0 x
UNION ALL
SELECT x + 1
FROM x
WHERE x + 1 < 10
)
SELECT count(*)
FROM x
INNER JOIN tbl_task t
ON t.task_start_time < date_sub(current_date, INTERVAL x.x DAY)
GROUP BY x.x;

MySQL multiple SELECT statements in one query

I'm storing some information in a MySQL table including a date without time.
The date format is a string looking like this: "25.08.2016" (Day.Month.Year).
I want to select the top 50 records from a table descending by a column, but I only want to display the rows with a specific column entry (date).
It is a ranking system and I want to update inactive people.
I would need to combine these 3 queries:
SELECT * FROM `rank` ORDER BY `rank`.`Score` DESC LIMIT 0 , 50;
SELECT * FROM `rank` WHERE NOT (`TimeStamp1` = DATE_FORMAT(NOW(), '%d.%m.%Y') OR `TimeStamp1` = DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 DAY), '%d.%m.%Y'));
UPDATE `rank` SET `inactive` = '1';
Selecting the top 50 people.
Selecting the inactive people of the first query.
Updating the people to inactive.
The most Score is rank 1 and that's why I need DESC, I only want to mark the top 50 people as inactive nothing below, below 50 those people are irrelevant that's why I can't use a statement like this:
SELECT * FROM `rank` WHERE NOT (`TimeStamp1` = DATE_FORMAT(NOW(), '%d.%m.%Y') OR `TimeStamp1` = DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 DAY), '%d.%m.%Y')) ORDER BY `rank`.`Score` DESC LIMIT 0 , 50
Yes, it would select 50 rows but not the top 50.
BTW I'm doing it in php.
And I could solve the problem by fetching:
SELECT * FROM `rank` ORDER BY `rank`.`Score` DESC LIMIT 0 , 50;
Then storing the Accound IDs to an array, then query:
SELECT * FROM `rank` WHERE NOT (`TimeStamp1` = DATE_FORMAT(NOW(), '%d.%m.%Y') OR `TimeStamp1` = DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 DAY), '%d.%m.%Y')) ORDER BY `rank`.`Score` DESC LIMIT 0 , 50;
And compare the Accound IDs to the other result, when no match is found I just break the loop.
Can't I just do it with pure MySQL? Can't I query a thing and then filter the results?
Please help me, any more questions?
Here's my Answer.
UPDATE rank AS target
INNER JOIN (
SELECT w.id
FROM rank AS w
INNER JOIN rank AS e ON e.id = w.id
WHERE (w.`TimeStamp1` = DATE_FORMAT(NOW(), '%Y-%m-%d') OR w.`TimeStamp1` = DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 DAY),'%Y-%m-%d'))
ORDER BY w.`Score` DESC
LIMIT 50
) AS source ON source.id = target.id
SET inactive = 1;
One way of executing queries sequentially is by using transaction.
But that won't combine your queries into one.
BEGIN TRANSACTION;
SELECT *
FROM `rank`
ORDER BY `rank`.`Score` DESC LIMIT 0 , 50;
SELECT *
FROM `rank`
WHERE NOT (`TimeStamp1` = DATE_FORMAT(NOW(), '%d.%m.%Y') OR `TimeStamp1` = DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 DAY), '%d.%m.%Y'));
UPDATE `rank` SET `inactive` = '1';
COMMIT;
Maybe this might be a combination of the three queries:
update rank r
set r.inactive = 1
from
(select a.account_id from rank a
join (select account_id from rank order by rank.score desc limit 0, 50) b on (a.account_id = b.account_id )
where (a.`TimeStamp1` = DATE_FORMAT(NOW(), '%d.%m.%Y') OR a.`TimeStamp1` = DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 DAY), '%d.%m.%Y'))) l
where r.account_id = l.account_id
Let me know if anything fails :)
Edit: swapped rank_id with account_id

Select with 'negative' offset

I need to select 40 rows with date from today and 10 records with older date, ordered by date.
If MySQL supported negative offset, it would look like this:
SELECT * FROM `mytable` WHERE `date` >= '2013-10-29' ORDER BY date LIMIT -10, 40;
Negative offset is not supported. How can I solve the problem? Thanks!!!
Use UNION to combine two queries:
(
SELECT *
FROM mytable
WHERE date < '2013-10-29'
ORDER BY date DESC
LIMIT 10
) UNION ALL (
SELECT *
FROM mytable
WHERE date >= '2013-10-29'
ORDER BY date
LIMIT 40
)
ORDER BY date -- if results need to be sorted

How to count consecutive number of 10 days

I have table with columns: id, name, date, present
Column present have values 0 or 1 or 2 and ... more
I need to count how many 0 valous is in current month 2013-07-01 - 2013-07-31 but count only when there are or more than 10 times.
for example if i have
2013-07-01 to 2013-07-10 valoues 0 it should count it and let me know that is 10 or more consecutives days like 11, 12 or more, but if it was less than 10 should count nothing.
I was trying some examples from stack... but they are different problems... so i need little help with that mysql query.
i have smth like this but need consecutives 10 days like >= 10
$sql = mysql_query("SELECT COUNT(name) as count FROM `table` WHERE (`present` = 0) AND (`date` BETWEEN '2013-07-01' AND '2013-07-31')");
while($row = mysql_fetch_array($sql)){
$result = $row['count'];
}
It counts me every 0 values in date between 2013-07-01 and 2013-07-31 but i need count how many days start from 10 or more consecutives days
column present have 0 and other numbers like 1, 2, 3... so i need count only 0 with 10 or more consecutives days
here is SqlFiddle i was trying to make warking from answer
http://sqlfiddle.com/#!2/1bde8/2
best regards
m.
This approach uses correlated subqueries to calculate two values.
The first value is the date of the previous record where Present = 1. This allows you to get the number of days in a row where Present = 0 by using datediff().
The second is the Present value of tomorrow, which will be NULL on the last day of the month. When today has Present = 0 and tomorrow is either 1 or NULL, then we can use this record. It is the end of a sequence of 0s.
From there is it just a question of adding up the values according to the conditions that you set. The following query assumes that you want to do this for each name:
select name, sum(case when datediff(date, lastPresentDate) >= 10
then datediff(date, lastPresentDate)
else 0 end) as DaysCounted
from (select t.*,
(select coalesce(max(date), '2013-06-30')
from t t2
where t2.name = t.name and
t2.present <> 0 and
t2.date <= t.date and
t2.date between '2013-07-01' and '2013-07-31'
) as lastPresentDate,
(select t2.present
from t t2
where t2.name = t.name and
t2.date = adddate(t.date, 1)
order by t2.date
limit 1
) as TomorrowPresent
from t
where date between '2013-07-01' and '2013-07-31'
) t
where Present = 0 and (TomorrowPresent = 1 and TomorrowPresent is null)
group by name
This query should give you a count only when it is 10 or greater than 10.
SELECT COUNT(`name`) as `count`
FROM `table`
WHERE (`present` = 0)
AND (`date` BETWEEN '2013-07-01' AND '2013-07-31')
HAVING `count` >= 10;
Hope it helps!
Not tested, but you could use user variables like this:-
SELECT SUM(if(ConsCounter=10, 1, 0))
FROM
(
SELECT id, name, date, present, #Counter := IF(#PrevPresent = present AND present = 0, #Counter + 1, 0) AS ConsCounter, #PrevPresent = present
FROM
(
SELECT id, name, date, present
FROM `table`
ORDER BY date
) Sub1
CROSS JOIN (SELECT #PrevPresent:=-99999, #Counter:=0) Sub2
) Sub4
Get all the records in date order and add a sequence number for the count since the present was first 0. Then count the number of times that counter is 10.