How to count at specific date range in mysql - mysql

I have created this mysql code to count at only specific date range, i dumped the output of my mysql code and here it is:
SELECT COUNT(
IF(
DATE(q.date_emailed) BETWEEN '2014-02-01' AND '2014-02-28',
1,
0
)
) AS 'Feb',
COUNT(
IF(
DATE(q.date_emailed) BETWEEN '2014-03-01' AND '2014-03-31',
1,
0
)
) AS 'March',
COUNT(
IF(
DATE(q.date_emailed) BETWEEN '2014-01-01' AND '2014-01-31',
1,
0
)
) AS 'Jan'
FROM
database.quotes q
WHERE (DATE(q.date_emailed) BETWEEN '2014-01-01' AND '2014-03-31')
But this outputs same count for each month, in which i confirmed that february and march has zero counts. What am I missing here?

COUNT() counts non-null values. You can fix your code by using sum() instead. You can also simplify it by removing the if statements:
SELECT SUM(DATE(q.date_emailed) BETWEEN '2014-02-01' AND '2014-02-28') AS Feb,
SUM(DATE(q.date_emailed) BETWEEN '2014-03-01' AND '2014-03-31') AS March,
SUM(DATE(q.date_emailed) BETWEEN '2014-01-01' AND '2014-01-31') AS Jan
FROM database.quotes q
WHERE DATE(q.date_emailed) BETWEEN '2014-01-01' AND '2014-03-31';
In MySQL, a boolean result is treated as 0 for "false" and 1 for "true". This is a great convenience and allows you to use sum() to count the number of matches.
Note that I also removed the single quotes around the column names. Single quotes should be used for string and date constants, not for identifiers.
EDIT:
You can have this query run faster by using an index on q.date_emailed. However, I don't think the index will be used because of the date() function. You can fix this by changing the logic:
SELECT SUM(DATE(q.date_emailed) BETWEEN '2014-02-01' AND '2014-02-28') AS Feb,
SUM(DATE(q.date_emailed) BETWEEN '2014-03-01' AND '2014-03-31') AS March,
SUM(DATE(q.date_emailed) BETWEEN '2014-01-01' AND '2014-01-31') AS Jan
FROM database.quotes q
WHERE q.date_emailed >= '2014-01-01' AND
q.date_emailed < '2014-04-01';

You should use SUM instead of COUNT if you want to SUM up the 0 and 1.
The COUNT will COUNT the number of row, the SUM will sum up the values.

Related

Avg function not returning proper value

I expect this query to give me the avg value from daily active users up to date and grouped by month (from Oct to December). But the result is 164K aprox when it should be 128K. Why avg is not working? Avg should be SUM of values / number of current month days up to today.
SELECT sq.month_year AS 'month_year', AVG(number)
FROM
(
SELECT CONCAT(MONTHNAME(date), "-", YEAR(DATE)) AS 'month_year', count(distinct id_user) AS number
FROM table1
WHERE date between '2020-10-01' and '2020-12-31 23:59:59'
GROUP BY EXTRACT(year_month FROM date)
) sq
GROUP BY 1
Ok guys thanks for your help. The problem was that on the subquery I was pulling the info by month and not by day. So I should pull the info by day there and group by month in the outer query. This finally worked:
SELECT sq.day_month, AVG(number)
FROM (SELECT date(date) AS day_month,
count(distinct id_user) AS number
FROM table_1
WHERE date >= '2020-10-01' AND
date < '2021-01-01'
GROUP BY 1
) sq
GROUP BY EXTRACT(year_month FROM day_month)
Do not use single quotes for column aliases!
SELECT sq.month_year, AVG(number)
FROM (SELECT CONCAT(MONTHNAME(date), '-', YEAR(DATE)) AS month_year,
count(distinct id_user) AS number
FROM table1
WHERE date >= '2020-10-01' AND
date < '2021-01-01'
GROUP BY month_year
) sq
GROUP BY 1;
Note the fixes to the query:
The GROUP BY uses the same columns as the SELECT. Your query should return an error (although it works in older versions of MySQL).
The date comparisons have been simplified.
No single quotes on column aliases.
Note that the outer query is not needed. I assume it is there just to illustrate the issue you are having.

Count Values with same Month Timestamp

I am struggling to count all the values that have the same Timestamp. This is how my database looks like:
Let's say I would like to get the amount of orders in May 2013. What is the right Syntax to get this done?
To get a count for a timestamp range, we can compare the timestamp column to a lower and upper bounds, for example:
SELECT COUNT(*)
FROM mytable t
WHERE t.orderdate >= '2013-05-01 00:00:00'
AND t.orderdate < '2013-06-01 00:00:00'
(All orders on or after the first second of May 1st AND before the first second of June.)
We can also do a similar comparison in an expression in the SELECT list, a conditional aggregation pattern:
SELECT SUM(IF(t.orderdate >= '2013-05-01' AND t.orderdate < '2013-06-01',1,0)) AS cnt_may
FROM mytable t
equivalently
SELECT SUM(CASE WHEN DATE_FORMAT(t.orderdate,'%Y-%m') = '2013-05' THEN 1 ELSE 0 END) AS cnt_may
FROM mytable t
Note that the first query (with conditions in the WHERE clause on the bare orderdate column) can take advantage of an index that has orderdate as the leading column, to perform an efficient range scan operation.

How to query rows by month and group by column using mysql?

I have a database that has two columns - result and time
I'm trying to get a count of how many rows exist of each result in a particular month. There are only two options for result success and failure
I've managed to get a count of how many rows there are in each month, but I can't get the individual count of how many success and how many failure there were in each month.
Here is what I have:
SELECT result, MONTH(time) MONTH, COUNT(*) COUNT
FROM mytable
WHERE YEAR(time)=2017
GROUP BY MONTH(time);
I'm looking for a result that provides me with something like there were 12 successes and 8 failures in a particular month.
Any help would be appreciated.
Use conditional aggregation
SELECT result, MONTH(time) MONTH,
sum(result = 'success') as success_count,
sum(result = 'failure') as failure_count
FROM mytable
WHERE YEAR(time) = 2017
GROUP BY result, MONTH(time);
I would use the following query:
SELECT,
DATE_FORMAT(time, '%m %Y') AS month_year,
SUM(CASE WHEN result = 'success' THEN 1 ELSE 0 END) AS success_count,
SUM(CASE WHEN result = 'failure' THEN 1 ELSE 0 END) AS failure_count
FROM mytable
WHERE YEAR(time) = 2017
GROUP BY
DATE_FORMAT(time, '%m %Y')
Note that you should be aggregating by time period alone, and not by the result, which instead is part of the sum in the CASE expression.

Query with sum case group by

I am facing problem with following query:
SELECT sum(CASE WHEN status.new_reg_yn='n'
AND month(status.visit_date)-1 = 8
AND year(status.visit_date) = 2015 THEN 1 ELSE 0 END)
FROM customer_visit_status_tbl status,
customer_details_tbl cust
WHERE status.customer_id = cust.customer_id
AND cust.client_id=65
GROUP BY status.customer_id
The problem is that this query is returning results for customer with same id though I used group by. For example, in the month of September, if same customer visits 5 times it is returning count as 5 instead of 1 though I used group by.
It is really unclear what you want... Yes, distinct customers for a given time period, but then you are taking the month of the date visited -1 and looking for that equal to 8. Being that current month is 9 (September), Are you just looking for those based on activity the month prior to whatever the current is? So, for example, if Sept, 2015, you want totals for Aug, 2015. In Jan, 2016, you would want Dec, 2015? If that is the case, you can use the current date to subtract 1 month and get that as basis of the query. Then you can have your additional specific client (65 in this case).
My (subselect sqlvars) pre-creates variables applied for the query. It computes one month ago by subtracting 1 month from whatever the current date it. Then uses that as basis of the month representing whatever was the prior month, and similarly for that respective year.
Since this will in essence create a single row return, there is no Cartesian result and you can just run with your original other tables for final counts.
select
count( distinct s.customer_id ) as UniqueCustomers
from
( select #oneMonthAgo := DATE_ADD(CURRENT_DATE, INTERVAL -1 MONTH),
#finalMonth := MONTH( #oneMonthAgo ),
#finalYear := YEAR( #oneMonthAgo ) sqlvars,
customer_visit_status_tbl s
JOIN customer_details_tbl c
on s.customer_id = c.customer_id
AND c.client_id = 65
where
s.new_reg_yn='n'
Update Ans -
Select count(*)
from
( SELECT distinct status.customer_id
FROM customer_visit_status_tbl status
, customer_details_tbl cust
WHERE status.customer_id = cust.customer_id
AND cust.client_id = 65
and status.new_reg_yn = 'n'
AND month(status.visit_date)-1 = 8
AND year(status.visit_date) = 2015
) customer_visited

Mysql Frequency within date range

I have a MySQL DB as such:
Date Customer_ID
How can I turn it into:
Customer_ID | Count_Visits_Past_Week | Count_Visits_Past_Month | Count_Visits_Past_90days | Count_Total
Note : Count_Total =sum of the other three counts
Thanks
The first step is to determine the demarcation points for the specified date ranges.
There's several questions to answer here: did you want to compare just the DATE ('yyyy-mm-dd') and disregard any time component?
By "past week", does that mean within the last seven days, or does it mean so far since the previous Sunday, or does it mean the last last full week, from Sunday through Saturday.
For "past month", does that mean the previous whole month, from the first through the end of the month? Or does it mean that if the query is run on the 20th of the month, we want dates since the 20th of the previous month up until today? Or yesterday?
Once we know the points in time that begin and end each specified period, relative to today's date, we can build expressions that evaluate to those dates.
For example, "past week" could be represented as the most recent seven day period:
DATE(NOW())-INTERVAL 1 WEEK -thru- DATE(NOW())
And "past month" can be represented as the same "day of month" (e.g. 17th) of the immediately preceding month up until today:
DATE(NOW())-INTERVAL 1 MONTH -thru- DATE(NOW())
That's really the first step, to determine the begin and end dates of each specified period.
Once we have that, we can move on to building a query that gets a "count" of rows with a date column that falls within each period.
The "trick" is to use conditional tests in expressions in the SELECT list of the query. We'll use those conditional tests to return a 1 if the row is to be included in the "count", and return 0 or NULL if the row should be excluded.
I prefer to use the SUM() aggregate function to get the "count". But it's also possible to use COUNT() aggregate. (If we use COUNT(), we need to use an expression that returns NULL when the row is to be excluded. I prefer to return a 1 or 0; I think it makes debugging easier.
Here's an outline of what a "count" query would look like.
SELECT t.Customer_Id
, SUM(IF( <some_condition> ,1,0) AS Count_something
, SUM(IF( <other_condition> ,1,0) AS Count_something_else
FROM mytable t
GROUP BY t.Customer_Id
When <some_condition> is true, we return a 1, otherwise we return 0.
To test the conditional expressions, it's often easiest to avoid doing the aggregation, and just return the individual rows:
That way, we can see which individual rows are going to be included in each "count".
For example:
SELECT t.Customer_ID
, t.date
, IF(t.date BETWEEN DATE(NOW())-INTERVAL 1 WEEK AND DATE(NOW()),1,0)
AS visit_past_week
, IF(t.date BETWEEN DATE(NOW())-INTERVAL 1 MONTH AND DATE(NOW()),1,0)
AS visit_past_month
FROM mytable t
ORDER BY t.date, t.Customer_Id
That query doesn't return the "count", it just returns the results of the expressions, which can be useful in testing. And of course we want to test the expressions that return the beginning and ending date of each period:
SELECT DATE(NOW()) - INTERVAL 1 WEEK AS past_week_begin
, DATE(NOW()) AS past_week_end
With this approach, the same row can be included in multiple "counts" with one query and one pass through the table.
Note that the expressions inside the SUM() aggregate in the query below are taking advantage of a convenient shorthand, an expression evaluated as a boolean will return 1 if TRUE, 0 if false, or a NULL.
To use the COUNT aggregate, we need to insure that the expression being aggregated returns a non-NULL when the row is to be "counted", and a NULL when the row is to be excluded from the count. So we use the convenient NULLIF function to return NULL if the value returned by the expression is a zero.
SELECT t.Customer_ID
, COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 1 WEEK AND DATE(NOW()),0))
AS Count_Visits_Past_Week
, COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 1 MONTH AND DATE(NOW()),0))
AS Count_Visits_Past_Month
, COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 90 DAY AND DATE(NOW()),0))
AS Count_Visits_Past_90days
, COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 1 WEEK AND DATE(NOW()),0))
+ COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 1 MONTH AND DATE(NOW()),0))
+ COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 90 DAY AND DATE(NOW()),0))
AS Count_Total
FROM mytable t
GROUP BY t.Customer_Id
NOTE: NULLIF(a,b) is a convenient shorthand for IF a IS NULL THEN return b ELSE return a
Returning the Count_Total is a bit odd, since it's got the potential to "count" the same row multiple times... but the value it returns should match the total of the individual counts.
I think this will give you what you want.
select customer_id,
sum(case when splitter = 'week' then num_visits else 0 end) as visits_this_week,
sum(case when splitter = 'month' then num_visits else 0 end) as visits_this_month,
sum(case when splitter = '90days' then num_visits else 0 end) as visits_last_90days,
sum(num_visits) as total
from (select customer_id, 'week' as splitter, count(*) as num_visits
from tbl
where extract(week from date) = extract(week from sysdate())
and extract(year from date) = extract(year from sysdate())
group by customer_id
union all
select customer_id, 'month' as splitter, count(*) as num_visits
from tbl
where extract(month from date) = extract(month from sysdate())
and extract(year from date) = extract(year from sysdate())
group by customer_id
union all
select customer_id, '90days' as splitter, count(*) as num_visits
from tbl
where date between date_sub(sysdate(), interval 90 day) and
sysdate()) x
group by customer_id
sql fiddle example: http://sqlfiddle.com/#!2/a762c/12/0