Find all mysql rows from last month with unixtimestamp - mysql

I am trying to find a better way of selecting all rows created in the previous month. Currently I am just going back x number of days, but I want it to run on the 1st of every month, and don't want to have to keep adjusting it.
Essentially, I just want it to select from the "date" field where the month is the previous month.
Here is my current sql:
SELECT a.*
FROM (SELECT a.article_id, a.date, a.tagline_image, a.article_top_image_filename, a.title, a.tagline, a.views, c.category_id
FROM
articles a
LEFT JOIN
`article_category_reference` c ON a.article_id = c.article_id
WHERE
a.date >= UNIX_TIMESTAMP(DATE(NOW() - INTERVAL 33 DAY)) AND c.category_id NOT IN (63) AND a.active = 1 group by `a`.`article_id`
ORDER BY
a.views DESC
LIMIT 30
) a
ORDER BY views ASC

Try that ?
WHERE MONTH(FROM_UNIXTIME(a.date)) = MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH))

A good strategy is to get the first day of the last month by calculating the last day of the month before that and then adding 1 day:
a.date >= UNIX_TIMESTAMP(DATE_ADD(LAST_DAY(DATE_SUB(NOW(), INTERVAL 2 MONTH)), INTERVAL 1 DAY))

Related

MySQL get number of records in each day between two given dates

I'm writing this query where it gets a row value and it will return the number of records for each day for that row between two given dates and returns 0 if there is no records for that day.
I've written a query which does this for the past week.
Current Query:
select d.day, count(e.event) as count
from (
select 0 day union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6
) d
left join event e
on e.timestamp >= current_date - interval d.day day
and e.timestamp < current_date - interval (d.day - 1) day
and e.event = ?
group by d.day
The problem is this returns only the results for a fixed number of days.. I want to be able to give it two dates (start and end dates) and get the record counts for each day where I don't know the number of dates in between.
You could use/create a bona-fide calendar table. Something like this:
SELECT
d.day,
COUNT(e.timestamp) AS cnt
FROM
(
SELECT '2020-01-01' AS day UNION ALL
SELECT '2020-01-02' UNION ALL
...
SELECT '2020-12-31'
) d
LEFT JOIN event e
ON e.timestamp >= d.day AND e.timestamp < DATE_ADD(d.day, INTERVAL 1 DAY)
WHERE
d.day BETWEEN <start_date> AND <end_date>
GROUP BY
d.day;
I have covered only the calendar year 2020, but you may extend to cover whatever range you want.

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.

Mysql subtracting values using row selected from min timestamp, grouping by id

I've been at this for a few hours now to no avail, pulling my hair out.
Edit: Im wanting to calculate the difference between the overall_exp column by using the same data from 1 day ago to calculate the greatest 'gain' for each user
Currently I'm take a row, then select a row from 1 day ago based on the first rows timestamp then subtract the overall_exp column from the 2 rows and order by that result whilst grouping by user_id
SQL Fiddle: http://sqlfiddle.com/#!2/501c8
Here is what i currently have, however the logic is completely wrong so im pulling 0 results
SELECT rsn, ts.timestamp, #original_ts := SUBDATE( ts.timestamp, INTERVAL 1 DAY), ts.overall_exp, ts.overall_exp - previous.overall_exp AS gained_exp
FROM tracker AS ts
INNER JOIN (
SELECT user_id, MIN( TIMESTAMP ) , overall_exp
FROM tracker
WHERE TIMESTAMP >= #original_ts
GROUP BY user_id
) previous
ON ts.user_id = previous.user_id
JOIN users
ON ts.user_id = users.id
GROUP BY ts.user_id
ORDER BY gained_exp DESC
You can do this with a self-join:
select t.user_id, max(t.overall_exp - tprev.overall_exp)
from tracker t join
tracker tprev
on tprev.user_id = t.user_id and
date(tprev.timestamp) = date(SUBDATE(t.timestamp, INTERVAL 1 DAY))
group by t.user_id
A key here is converting the timestamps to dates, so the comparison is exact.
Try:
select u.*, max(t.`timestamp`)-min(t.`timestamp`) gain
from users u
left join tracker t
on u.id = t.user_id and
t.`timestamp` >= date_sub(date(now()), interval 1 day) and
t.`timestamp` < date_add(date(now()), interval 1 day)
group by u.id
order by gain desc
SQLFiddle here.

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)

MySql Query: include days that have COUNT(id) == 0 but only in the last 30 days

I am doing a query to get the number of builds per day from our database for the last 30 days. But it has become needed to marked days where there were no builds also.
In my WHERE clause I use submittime to determine whether there were builds, how could I modify this to include days that have COUNT(id) == 0 but only in the last 30 days.
Original Query:
SELECT COUNT(id) AS 'Past-Month-Builds',
CONCAT(MONTH(submittime), '-', DAY(submittime)) as 'Month-Day'
FROM builds
WHERE DATE(submittime) >= DATE_SUB(CURDATE(), INTERVAL 30 day)
GROUP BY MONTH(submittime), DAY(submittime);
What I've Tried:
SELECT COUNT(id) AS 'Past-Month-Builds',
CONCAT(MONTH(submittime), '-', DAY(submittime)) as 'Month-Day'
FROM builds
WHERE DATE(submittime) >= DATE_SUB(CURDATE(), INTERVAL 30 day)
OR COUNT(id) = 0
GROUP BY MONTH(submittime), DAY(submittime);
You need a table of dates, then left join to the builds table.
Something like this:
SELECT
COUNT(id) AS 'Past-Month-Builds',
CONCAT(MONTH(DateTable.Date), '-', DAY(DateTable.Date)) as 'Month-Day'
FROM DateTable
LEFT JOIN builds ON DATE(builds.submittime) = DateTable.Date
WHERE DateTable.Date >= DATE_SUB(CURDATE(), INTERVAL 30 day)
GROUP BY MONTH(submittime), DAY(submittime);