MySQL Query to find monthly active user - mysql

I have the following table:
CREATE TABLE account (
account_id bigint(20) NOT NULL AUTO_INCREMENT,
time_start datetime NOT NULL,
time_end datetime DEFAULT NULL,
PRIMARY KEY (account_id),
KEY idx_start (account_id,time_start),
KEY idx_end (account_id,time_end)
) ENGINE=MyISAM
How can I write a query to find how many users log on monthly?
I want to find for the last 90 days how many different account_id are in the table group by month. Group by month means here every 30 days: for example from 2011-12-05 to 2011-11-06, from 2011-12-04 to 2011-11-05 and so on for the last 90 days.

You can trivially get years/months out of a datetime field with YEAR() and MONTH() respectively. But your periods don't match start/end on month boundaries, so you'll need some ugly-looking query logic to handle that conversion.
You should start by writing a stored function/procedure that'll convert a regular date/time to a "fiscal" date time, after which the query should become much cleaner looking. Once you've got the procedure done, it can be reused everywhere, as fiscal period calculations will undoubtedly be repeated elsewhere as well.

This query assumes two things:
1) you have your month logic squared-away (see #Marc's post) and added as an extra column (month) on the table.
2) time_start is the time that the user has "logged-on".
SELECT COUNT(*), month
FROM account
GROUP BY month
HAVING time_start > ADDDATE(CURDATE(),- INTERVAL 90 DAY);
Try messing around with it and see if that helps. I'm not too sure on the negative ADDDATE bit there, so you'll want to check-out MySQL's reference page for date and time functions.

try this
select count(distinct account_id)
from account
where
time_start >= date_sub(now(), interval 90 day)
group by
floor(datediff(now(), time_start) / 30)

Related

How to maintain different values for every hour of day in week

I want to add different rate(cost of hour) for different hours of day from Monday to Sunday in a mysql DB table.
I thought of 2 ways
creating a column of every day(Week day) so that when I fetch a column I will get entire days rate in one column
creating columns for every hour so that I have to fetch based on week day number(1 for Monday 2 for Tuesday). I will get rates of entire day in row
Which is good approach from above 2 or any other good approach for doing this ?
Thanks
Given the available MySQL time functions, WEEKDAY and HOUR are the easiest way to get to the values you need.
So a table like:
CREATE TABLE rate (day TINYINT NOT NULL,
hour TINYINT NOT NULL,
rate FLOAT NOT NULL,
PRIMARY KEY (DAY, HOUR))
So a query could look like:
SELECT rate
FROM rate
WHERE day=WEEKDAY(NOW())
AND hour >= HOUR(NOW())
ORDER BY day, hour LIMIT 1
This >= and ORDER/LIMIT just allows the table to have gaps of hours.

Unable to create computed field with date_add in SQL

In my database, I have a table called 'fine', in that table I have three fields, issue_date, expiry_date and fine_amount. I want the expiry_date to be computed from the issue date. The expiry date should always have 20 days more than the issue_date, So I wrote the query as:
ALTER TABLE fine ADD
expiry_date AS DATE_ADD(CURRENT_DATE,INTERVAL 20 DAY)
But there is a syntax error. I can't seem to find the solution.
Also I want the fine_amount to be 10 * (difference in days between current date and expiry date if current days exceeds expiry date). How do I go about doing that?
You can't implement the fine logic using a computed column because the formula involves the current time which is non deterministic. From the MySQL documentation:
Literals, deterministic built-in functions, and operators are permitted. A function is deterministic if, given the same data in tables, multiple invocations produce the same result, independently of the connected user. Examples of functions that fail this definition: CONNECTION_ID(), CURRENT_USER(), NOW().
So your best bet probably is to just compute values for these columns at the time you actually select. For example:
SELECT issue_date,
DATE_ADD(issue_date, INTERVAL 20 DAY) AS expiry_date,
CASE WHEN NOW() > DATE_ADD(issue_date, INTERVAL 20 DAY)
THEN 10*DATEDIFF(NOW(), DATE_ADD(issue_date, INTERVAL 20 DAY))
ELSE 0 END AS fine_amount
FROM fine

How can I get the date difference of a timestamp

I am trying to create a query that will limit insertion into a table based on the last time the poster sent data to the table.
For example if you posted data to the table then you are locked out of the system for another 10 hours. Here is what I came up with so far. But I get nowhere with the actual results on the data. Any help?
SELECT DATE( `date` )
FROM tablename
WHERE DATE( CURDATE( ) ) < CURDATE( ) - INTERVAL 1002
DAY
LIMIT 0 , 30
This will return a single post from the last 10 hours, if it exists:
SELECT *
FROM tablename
WHERE `date` >= NOW() - INTERVAL 10 HOUR
LIMIT 1
I'm assuming date is declared as DATETIME, since actual DATE does not contain the time part and hence is only day-accurate.
If date is an integer UNIX timestamp, use this:
SELECT *
FROM tablename
WHERE `date` >= UNIX_TIMESTAMP(NOW() - INTERVAL 10 HOUR)
LIMIT 1
There are a number of ways you could do this. Perhaps if you have a user settings table you could simply add a "last_insert" field, and store the timestamp as an integer value- that would be a super simple way to do it- you could check the current timestamp vs user_settings.last_insert and voila!
I suppose you could use datetime too. Whatever floats the boat.
First of all, you need a DATETIME column and not a DATE column. Assuming that tablename.date is a DATETIME column, then 10 hours before right now is CURRENT_TIMESTAMP - INTERVAL 10 HOUR.
First of all create a Time (TIMESTAMP DEFAULT CURRENT_TIMESTAMP) columnt in your table. It will be automatically set to current date on row insert
Then check:
SELECT COUNT(*) FROM Table WHERE Time > NOW() - INTERVAL 10 HOUR
If its 1 or more - block
You must compare the time last post was put with current time, not current time with current time :|

Grouping timestamp field by date

I am trying to write an SQL query to return how many links were submitted to my website over the last 7 day period. So far I have this:
SELECT COUNT(`id`) AS `count`
FROM `links`
WHERE `created` > NOW() - 86400
AND `created` < NOW()
this works for one day, it returns one row called count with the number of links submitted in the last 24 hours. I need to change it to return 2 columns called date and count, with 7 rows (one for each day).
The tricky part that I can't get my head around is that created is a timestamp column, and I don't have access to change it so I have to work with it.
Edit: work in progress for the query:
SELECT DAY(FROM_UNIXTIME(created)) AS day, COUNT(id) count
FROM links
GROUP BY DAY(FROM_UNIXTIME(created))
LIMIT 7
NOW() actually shouldn't be working as it returns a datetime. Also, if you want to fetch 7 days worth of data, you want to subtract 604800 from UNIX_TIMESTAMP(). You can use then date and time functions with FROM_UNIXTIME. This will make grouping easier. Optimally, your column should be of datetime type.
It would go something like:
SELECT DAY(FROM_UNIXTIME(created)) day, COUNT(id) count
FROM links
WHERE created > UNIX_TIMESTAMP() - 604800 AND created < UNIX_TIMESTAMP()
GROUP BY DAY(FROM_UNIXTIME(created))
You can alternatively use the BETWEEN operator:
WHERE created BETWEEN UNIX_TIMESTAMP() - 604800 AND UNIX_TIMESTAMP()
See the demo

How to get each day's information using group by keyword in mysql

I'm new in mysql and I'm currently having an issue with a query. I need to get an average duration for each activity each day within a week. The date format is like: '2000-01-01 01:01:01', but I want to get rid of the 01:01:01 thing and only care about the date. How do I do that?
The table is something like this:
record_id int(10) NOT NULL,
activity_id varchar(100) NOT NULL,
start_time datetime NOT NUll,
end_time datetime NOT NULL,
duration int(10) NOT NULL;
Thanks.
You could do something like the following
select activity_id, dayofweek(datetime) as day, avg(duration) as average
from table_name where datetime between start_date and end_date
group by activity_id,dayofweek(datetime)
If I'm understanding, you want to group by the different activity times and see the average days between the start and end of each activity. This should do it for you.
SELECT activity_id, avg(DATEDIFF(end_time, start_time)) AS Average
FROM tablename
GROUP BY activity_id, DAYOFWEEK(start_time)
Edit: Misunderstood, you want it broken down by the day as well, so this should pull each group, broken down by the day of the week that the start_time falls on, and then the average days between start_time and end_time.
You can use date_format(end_time, '%Y%m%d') to convert it to a sortable value by day. Put that in the group by expression, and that should do what you want.