Difficult query for calculating user appeared certain days before - mysql

I have a table
id, date
a , 2017-01-01
a , 2017-01-02
b , 2017-02-03
...
and I'd like to compute for each day D, how many distinct user appeared exactly 7 days ago (on that day), but not in-between D-7 and D. Don't care about if they appear before day D
And the output shall be
date, count
2017-01-01, 23
2017-01-02, 33
etc
I've been thinking about this for quite a while, but can't figure out the D to D+7 part out. Easily converted into python, but I'd like to sharpen my SQL skills :)
I know basic select, group by clauses, but I'm just wondering if there're any advanced techniques I should know about.
Any help would be appreciated

You can check if the user appeared on that day and 7 days ago
SELECT DDate,
COUNT(*) cnt
FROM tablename a
WHERE id IN (SELECT id
FROM tablename
WHERE DDate = DATE_SUB(a.DDate, INTERVAL 7 DAY)
)
GROUP BY DDate

I'm just trying to help you with the assumption of what I understand about your question
just from the documentation
select count(date), date from tablename where date<=CURDATE() + interval 7 day group by date

You can use a left join on the same table for the 7 days in the future to see if the ID shows up. If it doesn't show up, the left joined table's id will be null.
select count(distinct t1.id), t1.date + interval 7 day
from table t1
left join table t2 on t2.id = t1.id and t2.date < t1.date + interval 7 day and t2.date >= t1.date
where t2.id is null;

Similar to Ferdinand Gasper's answer, but this excludes the users who appeared less than 7 days before:
SELECT date, COUNT(DISTINCT id)
FROM yourTable AS t1
WHERE id IN (SELECT id
FROM yourTable AS t2
WHERE t2.date = DATE_SUB(t1.date, INTERVAL 7 DAY))
AND id NOT IN (SELECT id
FROM yourTable AS t2
WHERE t2.date BETWEEN DATE_SUB(t1.date, INTERVAL 6 DAY) AND DATE_SUB(t1.date, INTERVAL 1 DAY))
GROUP BY date

Related

How to remove users from Table1 which has more than 90 days from register date specified in Table2?

I need LEFT JOIN ON Table1.userid=Table2.id and delete users from Table1 which has more than 90 days since register date in Table2.registerDate (datetime format). How to build SQL query for this?
It would look something like this:
delete t1
from table1 t1 join
table2 t2
on t1.userid = t2.id
where t1.date > t2.registerdate + interval 90 day;
I am not sure if "90 days since" means before or after. The above tests for dates that are 90 days after the register date. < t2.registerdate - interval 90 day would be for "before".

subtracting days from a given day in mysql

Personal leave is 10 days. I need the remaining leave to be subtracted from the total personal leave i.e 10 days
result i have
SELECT id, firstname,lastname,join_date, completion_date,title, leave_from,leave_to, Given_leave_Days,Taken_Leave_Days,Remaining_Leave_Days FROM (SELECT T1.id,T1.firstname,T1.lastname,T1.join_date,T1.completion_date,T0.title,T2.leave_from,T2.leave_to,T2.user_id,T0.Leave_Days as Given_leave_Days,SUM(DATEDIFF(T2.leave_to,T2.leave_from)+1) AS Taken_Leave_Days ,T0.Leave_Days - SUM(DATEDIFF(T2.leave_to,T2.leave_from)+1) as 'Remaining_Leave_Days' FROM users_leave_request T2 INNER JOIN users T1 ON T2.user_id = T1.id INNER JOIN leave_type T0 ON T2.leave_type_id = T0.id GROUP BY T1.id, T1.firstname, T1.lastname, T1.join_date,T1.completion_date,T0.title,T2.leave_from,T2.leave_to,Given_leave_Days order by firstname) A
i have to display the result in frontend.
result in frontend
The general answer is
select date_sub(now(), interval 10 day)
You can replace now() for any date from a table, you can change day for month, year, etc.
The result from my example is a date 10 days before today. For example, if you want to filter only the dates older than 10 days you would do like this:
select * from mytable t where t.mydate < date_sub(now(), interval 10 day)

mysql multiple tables select last 7 days

I am having 3 tables, containing some records which have a date and a numeric value (the tables can't be merged). I want to make up a bar chart using the information from the tables. The bar chart is grouped by days and should display the last seven days.
Earlier i had two tables and used the following query-scheme:
SELECT
t.credits1,
t.credits2,
t.date
FROM
(
(
SELECT
t1.credits1,
t2.credits2,
t1.date
FROM
(
SELECT
SUM(credits) AS credits1,
date
FROM
table1
WHERE
table1.date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY
DATE(table1.date)
) t1
LEFT JOIN
(
SELECT
SUM(credits) AS credits2,
date
FROM
table2
WHERE
table2.date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY
DATE(table2.date)
) t2
ON t1.date = t2.date
)
UNION
(
SELECT
t1.credits1,
t2.credits2,
t1.date
FROM
(
SELECT
SUM(credits) AS credits1,
date
FROM
table1
WHERE
table1.date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY
DATE(table1.date)
) t1
RIGHT JOIN
(
SELECT
SUM(credits) AS credits2,
date
FROM
table2
WHERE
table2.date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY
DATE(table2.date)
) t2
ON t1.date = t2.date
)
) t GROUP BY
DATE(date)
(pseudo code)
But how can i do this with more than 2 tables?
Is there any chance to set the dates of the past 7 days as a base, so that i get 7 records everytime?
To point out the problem: If I dont have records in the first table for a day, i won't get the records from the other tables for that day.
I assume the 3 tables have similar schemas? Try using UNION ALL to join the tables together.
SELECT ABB1.date, SUM(ABB1.credit) AS daily_total
FROM
(SELECT date, credits
FROM table1
UNION ALL
SELECT date, credits
FROM table1
UNION ALL
SELECT date, credits
FROM table2) AS ABB1
WHERE DATE >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)

Return a zero for a day with no results

I have a query which returns the total of users who registered for each day. Problem is if a day had no one register it doesn't return any value, it just skips it. I would rather it returned zero
this is my query so far
SELECT count(*) total FROM users WHERE created_at < NOW() AND created_at >
DATE_SUB(NOW(), INTERVAL 7 DAY) AND owner_id = ? GROUP BY DAY(created_at)
ORDER BY created_at DESC
Edit
i grouped the data so i would get a count for each day- As for the date range, i wanted the total users registered for the previous seven days
A variation on the theme "build your on 7 day calendar inline":
SELECT D, count(created_at) AS total FROM
(SELECT DATE_SUB(NOW(), INTERVAL D DAY) AS D
FROM
(SELECT 0 as D
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
) AS D
) AS D
LEFT JOIN users ON date(created_at) = date(D)
WHERE owner_id = ? or owner_id is null
GROUP BY D
ORDER BY D DESC
I don't have your table structure at hand, so that would need adjustment probably. In the same order of idea, you will see I use NOW() as a reference date. But that's easily adjustable. Anyway that's the spirit...
See for a live demo http://sqlfiddle.com/#!2/ab5cf/11
If you had a table that held all of your days you could do a left join from there to your users table.
SELECT SUM(CASE WHEN U.Id IS NOT NULL THEN 1 ELSE 0 END)
FROM DimDate D
LEFT JOIN Users U ON CONVERT(DATE,U.Created_at) = D.DateValue
WHERE YourCriteria
GROUP BY YourGroupBy
The tricky bit is that you group by the date field in your data, which might have 'holes' in it, and thus miss records for that date.
A way to solve it is by filling a table with all dates for the past 10 and next 100 years or so, and to (outer)join that to your data. Then you will have one record for each day (or week or whatever) for sure.
I had to do this only for MS SqlServer, so how to fill a date table (or perhaps you can do it dynamically) is for someone else to answer.
A bit long winded, but I think this will work...
SELECT count(users.created_at) total FROM
(SELECT DATE_SUB(CURDATE(),INTERVAL 6 DAY) as cdate UNION ALL
SELECT DATE_SUB(CURDATE(),INTERVAL 5 DAY) UNION ALL
SELECT DATE_SUB(CURDATE(),INTERVAL 4 DAY) UNION ALL
SELECT DATE_SUB(CURDATE(),INTERVAL 3 DAY) UNION ALL
SELECT DATE_SUB(CURDATE(),INTERVAL 2 DAY) UNION ALL
SELECT DATE_SUB(CURDATE(),INTERVAL 1 DAY) UNION ALL
SELECT CURDATE()) t1 left join users
ON date(created_at)=t1.cdate
WHERE owner_id = ? or owner_id is null
GROUP BY t1.cdate
ORDER BY t1.cdate DESC
It differs from your query slightly in that it works on dates rather than date times which your query is doing. From your description I have assumed you mean to use whole days and therefore have used dates.

Optimize a subquery searching for records in another table based on dates not in the past 360 days

Problem:
Find people whose birthdays are tomorrow (table a), who havent got a record with an issue date set in the past 360 days from (table b)
Table a
ID, DOB
Table b
ID, PID, Issued
I've got a query but it's pretty slow, not sure if a join would be quicker - any help appreciated..
SELECT a.ID, a.DOB FROM a
WHERE MONTH(a.DOB)=MONTH(now()) # match month
AND DAYOFMONTH(a.DOB)=DAYOFMONTH(now()+ INTERVAL 1 DAY) # match day of month
AND (
SELECT COUNT(*) FROM b
WHERE b.PID = a.ID
AND b.Issued < DATE_SUB(SYSDATE(), INTERVAL 360 DAY)
) < 1 # hacky subquery to find not issued in past 360 days
SELECT *
FROM A
LEFT JOIN B
ON A.Id = B.PID AND B.Issued >= DATE_SUB(CURDATE(), INTERVAL 360 DAY)
WHERE B.ID IS NULL AND
DATE_ADD(A.DOB, INTERVAL YEAR(CURDATE()) - YEAR(A.DOB) YEAR) =
DATE_ADD(CURDATE(), INTERVAL 1 DAY)