Read user registration with count per day - mysql

I have a table with user data and I would like to do an efficient query to SELECT the user Count (userId e.g.) PER DAY.
So the output will be:
20/07/2015 - 50 new users
21/07/2015 - 23 new users
22/07/2015 - 19 new users
How can I do it with one query? A while() loop and then doing a query per day each seems very inefficient for me.

You could have a schema which has the following structure(I'm using MySql's syntax):
create table users(
user_id int,
create_date date
);
Say it's populated with following data:
insert into users values (1, STR_TO_DATE('1-01-2012', '%d-%m-%Y'));
insert into users values (2, STR_TO_DATE('1-01-2012', '%d-%m-%Y'));
insert into users values (3, STR_TO_DATE('1-01-2012', '%d-%m-%Y'));
insert into users values (4, STR_TO_DATE('2-01-2012', '%d-%m-%Y'));
If you select the user id's and group them by create_date, you'll have the required response using single query:
select count(user_id) as count,create_date from users group by create_date;
count create_date
3 January, 01 2012 00:00:00
1 January, 02 2012 00:00:00

Related

sum of months like jan=Jan and feb =jan+feb using mysql and march = jan+feb+march

My query is I have Name column, Amount column, month column.
example if name is Sandeep he is having amount 90 in month Jan
like below
Name Amount month new column
Sandeep 90 Jan Jan(amount)=90
Sandeep 100 Feb. (jan+feb)amount=190
Sandeep 120 March (jan+feb+march)=310
Mandeep 70 April (0+0+0+April)=70
Mandeep 20 May (0+0+0+70+20)=90
I want the new column with the same format as above using MySQL query
I would suggest a complete other approach.
Store your monthly data in one row per name. Make your data an Integer containing Year and Month
CREATE TABLE t
(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
yearmonth INT,
amount INT
);
INSERT INTO t (name, yearmonth, amount)
VALUES ('Sandeep', 202101, 90),
('Sandeep', 202102, 100),
('Sandeep', 202103, 120),
('Mandeep', 202104, 70),
('Mandeep', 202105, 20);
Then you can easily request all data you need
For example:
SELECT name, SUM(amount)
FROM t
WHERE name = 'Sandeep' AND yearmonth BETWEEN 202101 AND 202103
I have created a little fiddle for you.
https://www.db-fiddle.com/f/gZ1u7yB65FddjfKLU92tHE/0

SQL query to order by day, date and lead count

Trying to figure out how to pull data arranged by day, date and leads stats in the following format
Example output format
Day Date Leads
Today 2020/09/14 3
Yesterday 2020/09/13 64
Saturday 2020/09/12 18
Friday 2020/09/11 29
Thursday 2020/09/10 17
Wednesday 2020/09/09 94
A lead will is either a email or number
What SQL query can I use to get this
Example data
CREATE TABLE weektest(
date datetime,
lead VARCHAR(100)
);
INSERT INTO weektest(date, lead)
VALUES
(
'2020/09/04 10:36:51', 'number'
);
INSERT INTO weektest(date, lead)
VALUES
(
'2020/09/08 00:47:52', 'email'
);
INSERT INTO weektest(date, lead)
VALUES
(
'2020/09/11 03:03:41', ''
);
Do you just want aggregation?
select dayname(w.date) day, date(w.date) as date, count(*) cnt
from weektest w
group by date(w.date)
order by date(w.date)
I am not sure what you want to count: the above query gives you the number of rows per day. If you want the count of distinct lead values, then use count(distinct leads) instead of count(*).

Find the user who increased their hours streamed from the previous calendar month

This table is a "heartbeat" tracking event where one row is genereated each minute for each streamer while that streamer is live. If a streamer is live for 60 minutes, 60 rows would be generated in this table
Create Table minute_streamed
(
time_minute datetime ,
username varchar(50) ,
category varchar(50) ,
concurrent_viewers int
)
Insert into minute_streamed values ('2020-03-18 12:00:00', 'alex','Fornite',125) ;
Insert into minute_streamed values ('2020-03-18 12:01:00', 'alex','Fornite',130) ;
Insert into minute_streamed values ('2020-03-19 15:30:00', 'jamie','Just Chatting',13) ;
Insert into minute_streamed values ('2020-03-19 15:31:00', 'jamie','Food & Drink',15) ;
Insert into minute_streamed values ('2020-03-20 10:30:00', 'rick','Call of Duty: Black Ops',150) ;
Insert into minute_streamed values ('2020-03-20 10:31:00', 'rick','Call of Duty: Modern Warfare',120) ;
Insert into minute_streamed values ('2020-04-21 09:30:00', 'rick','Fornite',120) ;
Insert into minute_streamed values ('2020-04-20 10:31:00', 'rick','Call of Duty: Modern Warfare',120) ;
Insert into minute_streamed values ('2020-04-21 09:30:00', 'rick','Fornite',120) ;
Insert into minute_streamed values ('2020-04-20 10:31:00', 'jamie','Call of Duty: Modern Warfare',120) ;
Insert into minute_streamed values ('2020-04-21 09:30:00', 'jamie','Fornite',120) ;
Insert into minute_streamed values ('2020-04-18 12:00:00', 'alex','Fornite',125) ;
Insert into minute_streamed values ('2020-04-18 12:01:00', 'alex','Fornite',130) ;
Insert into minute_streamed values ('2020-06-18 14:00:00', 'alex','Fornite',120) ;
Alex has two entries in March. That means he streamed for 2 minutes. So, his hourly streamed for March will be 2/60.
I am trying to write a query: For each calendar month, output the list of streamers, who increased their hours streamed from the previous calendar month
For example, Alex has two entries for March, two entries for April, and one entry for June. So he streamed 2 minutes in March (because he has two entries), 2 minutes in April and 1 minute in June. I want to compare his last month, which is June and the previous calendar month. In this case, the previous calendar month is May, which Alex did not stream. So I need to say that he did not stream in May and he streamed in June. So, he increased his streaming compare to the previous calendar month
This my code below, but I want to compare the current streaming hour with the previous calendar month. Can you please help modify my query?
select
*
from(
select
*
,lag(total_monthly_hours,1) over(partition by username order by year,month) as prev_month
from(
select
username
,year(time_minute) as year
,month(time_minute) as month
,count(*)/60 as total_monthly_hours
from minute_streamed
group by year(time_minute), month(time_minute), username
order by month(time_minute) desc ) as temp ) as temp2
where total_monthly_hours > prev_month
If I understand correctly, you want to compare both the previous value and the previous time period. One way to do this is to calculate a "number of months" by multiplying the year by 12 and adding in the month number. Then you can see if the lag() is getting the value from the previous row:
select uym.*
from (select uym.*,
lag(total_monthly_hours) over (partition by username order by year, month) as prev_total_monthly_hours,
lag(month_cnt) over (partition by username order by year, month) as prev_month_cnt
from (select username,
year(time_minute) as year,
month(time_minute) as month,
year(time_minute) * 12 + month(time_minute) as month_cnt,
count(*)/60 as total_monthly_hours
from minute_streamed
group by year(time_minute), month(time_minute), username, month_cnt
) uym
) uym
where prev_month_cnt is null or
prev_month_cnt <> month_cnt - 1 or
(prev_month_cnt = month_cnt - 1 and prev_total_monthly_hours < total_monthly_hours);
Here is a db<>fiddle.

Padding MYSQL data with missing dates when comparing year over year stats?

I have a table that tracks emails sent. It is pretty simple.
ID | DATETIME | E-MAIL | SUBJECT | MESSAGE
I have been collecting data for several years. Some days I don't have any entries in the table.
query1:
SELECT COUNT(ID) FROM emails
WHERE DATE(datetime) >= 'XXXX-XX-XX'
AND DATE(datetime) is <= 'ZZZZ-ZZ-ZZ'
GROUP BY DATE(datetime)
I then use a some php to get one year prior for both XXXX and YYYY and run the second query which is the same as the first...
query2:
SELECT COUNT(ID) from emails
WHERE DATE(datetime) >= 'XXXX-XX-XX'
AND DATE(datetime) is <= 'ZZZZ-ZZ-ZZ'
GROUP BY DATE(datetime)
I am using a charting package to compare how many emails I got for a date range and then I overlay how many emails I got for the same range only one year prior. This is two queries right now and I chart the results.
The issue is where mysql does not have any emails for 2011 for a day in question, but has a few in 2012 for the same day.
Combining the results and graphing them skews the results since I am missing a date and a 0 value for last year for that day, effectively making all my values no longer match up.
2011-03-01 10 2012-03-01 4
2011-03-02 4 2012-03-02 2
2011-03-03 6 2012-03-04 1 <---- see where the two queries
end up diverging? (I had nothing
logged for 2012-03-03 so naturally
it was not in the results.
Is there a way I can get mysql to output the data I need including dates where value appear in one year but not another OR if no values appear in either year (still need date and 0) so my chart works?
I cannot seem to figure out how to do this...
Thanks!
There are a few different ways to get the results for a contiguous set of dates. My favourite one is to create the full set that is required using a dummy table or an existing contiguous set of ids from an AI PK. Something like this -
SELECT '2011-01-01' + INTERVAL (id -1) DAY
FROM dummy
WHERE id BETWEEN 1 AND 365
This will return a full set of days for 2011 which can then be LEFT JOINed to your emails table to get the counts -
SELECT `dates`.`date`, COUNT(emails.id)
FROM (
SELECT '2011-01-01' + INTERVAL (id - 1) DAY AS `date`, '2011-01-01 23:59:59' + INTERVAL (id - 1) DAY AS `end_of_day`
FROM dummy
WHERE id BETWEEN 1 AND 365
) `dates`
LEFT JOIN emails
ON `emails`.`datetime` BETWEEN `dates`.`date` AND `dates`.`end_of_day`
GROUP BY `dates`.`date`
To populate your dummy / seq table you can insert the first ten values manually and then use INSERT ... SELECT to add the rest -
CREATE TABLE dummy (id INTEGER NOT NULL PRIMARY KEY);
INSERT INTO dummy VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
SET #tmp := (SELECT MAX(id) FROM dummy) + 1;
INSERT INTO dummy
SELECT #tmp + id
FROM dummy;
You need to execute the SET query before each run of the INSERT ... SELECT query.

SQL query that returns all dates not used in a table

So lets say I have some records that look like:
2011-01-01 Cat
2011-01-02 Dog
2011-01-04 Horse
2011-01-06 Lion
How can I construct a query that will return 2011-01-03 and 2011-01-05, ie the unused dates. I postdate blogs into the future and I want a query that will show me the days I don't have anything posted yet. It would look from the current date to 2 weeks into the future.
Update:
I am not too excited about building a permanent table of dates. After thinking about it though it seems like the solution might be to make a small stored procedure that creates a temp table. Something like:
CREATE PROCEDURE MISSING_DATES()
BEGIN
CREATE TABLE TEMPORARY DATES (FUTURE DATETIME NULL)
INSERT INTO DATES (FUTURE) VALUES (CURDATE())
INSERT INTO DATES (FUTURE) VALUES (ADDDATE(CURDATE(), INTERVAL 1 DAY))
...
INSERT INTO DATES (FUTURE) VALUES (ADDDATE(CURDATE(), INTERVAL 14 DAY))
SELECT FUTURE FROM DATES WHERE FUTURE NOT IN (SELECT POSTDATE FROM POSTS)
DROP TABLE TEMPORARY DATES
END
I guess it just isn't possible to select the absence of data.
You're right — SQL does not make it easy to identify missing data. The usual technique is to join your sequence (with gaps) against a complete sequence, and select those elements in the latter sequence without a corresponding partner in your data.
So, #BenHoffstein's suggestion to maintain a permanent date table is a good one.
Short of that, you can dynamically create that date range with an integers table. Assuming the integers table has a column i with numbers at least 0 – 13, and that your table has its date column named datestamp:
SELECT candidate_date AS missing
FROM (SELECT CURRENT_DATE + INTERVAL i DAY AS candidate_date
FROM integers
WHERE i < 14) AS next_two_weeks
LEFT JOIN my_table ON candidate_date = datestamp
WHERE datestamp is NULL;
One solution would be to create a separate table with one column to hold all dates from now until eternity (or whenever you expect to stop blogging). For example:
CREATE TABLE Dates (dt DATE);
INSERT INTO Dates VALUES ('2011-01-01');
INSERT INTO Dates VALUES ('2011-01-02');
...etc...
INSERT INTO Dates VALUES ('2099-12-31');
Once this reference table is set up, you can simply outer join to determine the unused dates like so:
SELECT d.dt
FROM Dates d LEFT JOIN Blogs b ON d.dt = b.dt
WHERE b.dt IS NULL
If you want to limit the search to two weeks in the future, you could add this to the WHERE clause:
AND d.dt BETWEEN NOW() AND ADDDATE(NOW(), INTERVAL 14 DAY)
The way to extract rows from the mysql database is via SELECT. Thus you cannot select rows that do not exist.
What I would do is fill my blog table with all possible dates (for a year, then repeat the process)
create table blog (
thedate date not null,
thetext text null,
primary key (thedate));
doing a loop to create all dates entries for 2011 (using a program, eg $mydate is the date you want to insert)
insert IGNORE into blog (thedate,thetext) values ($mydate, null);
(the IGNORE keyword to not create an error (thedate is a primary key) if thedate exists already).
Then you insert the values normally
insert into blog (thedate,thetext) values ($mydate, "newtext")
on duplicate key update thetext="newtext";
Finally to select empty entries, you just have to
select thedate from blog where thetext is null;
You probably not going to like this:
select '2011-01-03', count(*) from TABLE where postdate='2011-01-03'
having count(*)=0 union
select '2011-01-04', count(*) from TABLE where postdate='2011-01-04'
having count(*)=0 union
select '2011-01-05', count(*) from TABLE where postdate='2011-01-05'
having count(*)=0 union
... repeat for 2 weeks
OR
create a table with all days in 2011, then do a left join, like
select a.days_2011
from all_days_2011
left join TABLE on a.days_2011=TABLE.postdate
where a.days_2011 between date(now()) and date(date_add(now(), interval 2 week))
and TABLE.postdate is null;