How to count consecutive number of 10 days - mysql

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.

Related

How would I build an SQL query to select first time deposits, second time deposits and additional deposits from a transactions table

In this scenario I have two tables users and transactions. I would like to filter all the transactions for a specified time period into 3 categories, first time deposit, second time deposit and additional deposits.
To work out a first time deposit you would check if the user has no transactions before that one using the created_at field, for second time deposit they would have one other transaction before that one and for the rest they should have 2 or more before that one.
The transactions table has 2 fields we care about here:
user (user id)
created_at (time transaction was created)
Here is my attempt but I am having trouble visualising the whole query. Any ideas on how I would do this?
SELECT
COUNT(t.id) as first_time_deposits
FROM
transactions t
WHERE
status = 'approved' AND DATE(t.created_at) BETWEEN (CURDATE() - INTERVAL 0 DAY) AND CURDATE()
GROUP BY user
HAVING NOT EXISTS
(
SELECT
u.id
FROM
transactions u
WHERE
u.created_at < t.created_at
)
I use the date interval here just for filtering transactions between a day, week etc. This query doesn't work because I am trying to reference the date of outer query in the sub query. I am also missing second time deposits and additionald deposits.
Example output I am looking for:
first_time_deposits
second_time_deposits
additional_deposits
15
5
6
All for a selected time period.
Any help would be greatly appreciated.
This is how I'd do that. The solution works fine if, for example, "first" transactions took place at the same time. Same for others
"first_to_last" is a recursive query just to display numbers we need to get transactions for (1 to 3 in your case). This makes the query easy adjustable in case if you suddenly need not first 3 but first 10 transactions
"numbered" - ranks transactions by date
Main query joins first 2 CTEs and replaces numbers with words like "first", "second", and "third". I didn't find other way rather than to hardcode values.
with recursive first_to_last(step) as (
select 1
union all
select step + 1
from first_to_last
where step < 3 -- how many lines to display
),
numbered as (
select dense_rank() over(partition by user_id order by created_at) rnk, created_at, user_id
from transactions
)
select user_id,
concat(case when f.step = 1 then 'first_deposit: '
when f.step = 2 then 'second_deposit: '
when f.step = 3 then 'third_deposit: '
end,
count(rnk))
from numbered n
join first_to_last f
on n.rnk = f.step
group by user_id, f.step
order by user_id, f.step
dbfiddle
UPD. Answer to the additional question: ". I just want the count of all first, second and any deposit that isn't first or second"
Just remove the "first_to_last" cte
with numbered as (
select dense_rank() over(partition by user_id order by created_at) rnk, created_at, user_id
from transactions
)
select user_id,
concat(case when n.rnk = 1 then 'first_deposit: '
when n.rnk = 2 then 'second_deposit: '
else 'other_deposits: '
end,
count(rnk))
from numbered n
group by user_id, case when n.rnk = 1 then 'first_deposit: '
when n.rnk = 2 then 'second_deposit: '
else 'other_deposits: '
end
order by user_id, rnk
UPD2. output in 3 columns: first, second and others
with numbered as (
select dense_rank() over(partition by user_id order by created_at) rnk, created_at, user_id
from transactions
)
select
sum(case when n.rnk = 1 then 1 else 0 end) first_deposit,
sum(case when n.rnk = 2 then 1 else 0 end) second_deposit,
sum(case when n.rnk not in (1,2) then 1 else 0 end) other_deposit
from numbered n

calculating variance with mysql week over week

I need to display week over week difference with mysql in Week Over Week Users column. My data looks like the following:
Date Users Week Over Week Users
06-01-2019 10 10
06-08-2019 15 15
06-15-2019 5 5
Currently, Week Over Week Users only reflects the data that I have in Users column. The desired output would be:
Date Users Week Over Week Users
06-01-2019 10 10
06-08-2019 15 5
06-15-2019 5 -10
Basically if on the second week the number of users grew up to 15 users, then I need to display 5 (as in +5 users since last week, so new week Users - last week Users would be the formula)
Here is my code:
(
SUM(
CASE
WHEN WEEK(`Date`) = WEEK(CURRENT_DATE()) THEN `Users`
ELSE 0
END
) - SUM(
CASE
WHEN WEEK(`Date`) = WEEK(CURRENT_DATE()) - 1 THEN `Users`
ELSE 0
END
)
)
But it doesn't work as it duplicates the Users column.
You want lag():
select t.*,
(users - lag(users, 1, 0) over (order by date)) as week_over_week
from t;
If you are running MySQL 5.x, where window functions such as lag() are not available, you can use a correlated subquery to get the "previous" value:
select
t.date,
t.users,
t.users - coalesce(
(
select t1.users
from mytable t1
where t1.date < t.date
order by t1.date desc
limit 1
),
0
) week_over_week_users
from mytable t

count consecutive days (streak) and number of records for current day

The following code was taken from another question on SO. Original Q&A
I would like to count the number of consecutive days (Streak) with records since today AND also how many records were made today. I'm using this to send notifications. If a user submits a new record the same day, they should not get a second notification telling them that they are on a streak (they were made aware the first time they submitted a record for the current day).
I tried adding a COUNT() function before #streak, after the first SELECT and pretty much everywhere that seemed reasonable but this query is too complex for me to figure it out.
SELECT streak + 1 as realStreak
FROM (
SELECT dt,
#streak := #streak+1 streak,
datediff(curdate(),dt) diff
FROM (
SELECT distinct date(dt) dt
FROM glucose where uid = 1
) t1
CROSS JOIN (SELECT #streak := -1) t2
ORDER BY dt desc
)
t1 where streak = diff
ORDER BY streak DESC LIMIT 1
http://sqlfiddle.com/#!9/45d386/1/0
The result of the above should be:
realStreak | RecordsToday
3 | 3
Just add a subquery for the today check
SELECT streak + 1 as realStreak,cdt
FROM (
SELECT dt,
#streak := #streak+1 streak,
datediff(curdate(),dt) diff
FROM (
SELECT distinct date(dt) dt
FROM gl where uid = 1
) t1
CROSS JOIN (SELECT #streak := -1) t2
ORDER BY dt desc
)t1
JOIN
(SELECT COUNT(CASE WHEN DATE(dt)=CURDATE() THEN 1 END) cdt FROM gl)x
where streak = diff
ORDER BY streak DESC LIMIT 1

Select before specific date from one day only

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)

Sort date by day before and by night after in one query

(first : I'm french and I'm sorry if I make some grammaticals faults...)
I have a table with TV programs. I want, in one query, to search the programs in this table and to sort the results with all the programs of the day before the programs of the night.
I have an input name fullDateStart with the date in DATETIME format for extract HOUR().
I use a LEFT JOIN in my research. Here my actual request :
SELECT programId, programTitle, COUNT(*) AS score,
ROUND(startDate / 1000) AS start, ROUND(endDate / 1000) AS end
FROM people_appearances AS a
LEFT JOIN programsTable AS b ON a.programId = b.program_id
WHERE peopleId = :id AND timestamp > :twoWeeksAgo AND programId != 0
AND redif = 0 AND channel_id IN(1,2,3,5,6,7,8,9)
GROUP BY programId
ORDER BY score DESC, start DESC
LIMIT 0, 10
Here my try with UNION :
SELECT * FROM (
SELECT fullDateStart, programId, programTitle, COUNT(*) AS score1,
ROUND(startDate / 1000) AS start, ROUND(endDate / 1000) AS end
FROM people_appearances AS a
LEFT JOIN db.epg_programs AS b ON a.programId = b.program_id
WHERE HOUR(fullDateStart) > 6 AND HOUR(fullDateStart) <= 23
AND peopleId = 826 AND timestamp > 1353420511000 AND programId != 0
AND redif = 0 AND channel_id IN(1,2,3,5,6,7,8,9)
GROUP BY programId
UNION
SELECT fullDateStart, programId, programTitle, COUNT(*) AS score2,
ROUND(startDate / 1000) AS start, ROUND(endDate / 1000) AS end
FROM people_appearances AS c
LEFT JOIN db.epg_programs AS d ON c.programId =d.program_id
WHERE HOUR(fullDateStart) >= 0 AND HOUR(fullDateStart) <= 6
AND peopleId = 826 AND timestamp > 1353420511000 AND programId != 0
AND redif = 0 AND channel_id IN(1,2,3,5,6,7,8,9)
GROUP BY programId
) AS s3
ORDER BY score1 DESC,start DESC
LIMIT 0, 10
Is somebody can help me please ? (I try with a Union with two request [one for the day, one for the night] but I don't succeed to sort the results, even if they was in two requests...)
The issue in your query is the order by. You are ordering everything first by the score, then by the start date.
If the goal is to keep everything in one day, then do something like:
order by score desc, date(fulldatestart),
(case when hour(fulldatestart) between 7 and 23 then 1
else 2
end),
hour(fulldatestart)
I added a third clause for the order by, so programs with the same score are ordered by hour.
If you want the early morning hours associated with the previous day, then you need to do something like:
order by score desc, date(fulldatestart - interval 7 hour),
hour(fulldatestart - interval 7 hour)
(once you subtract 7 hours, you can keep things in the order by hour.)