How to select different values in different dates in SQL - mysql

Im trying to get the number of customers that a company had each day for the last 7 days. But when I run my code I get the total of customers for the week and is just displaying the last order date.
SELECT order_date, COUNT(DISTINCT buyerid) as 'customers' from orders
WHERE
date(order_date) >= curdate() - 7
AND date(order_date) <= curdate()

Your code is able to run because it's not in 'full group by only' mode. That is because you're not aggregating the order_date, so you end up displaying only the last date and counting all the buyerids. This is almost never an expected result, I might say.
I would rewrite it like so:
SELECT order_date, COUNT(DISTINCT buyerid) as 'customers'
FROM orders
WHERE
order_date >= date_sub(curdate(), interval 1 week)
GROUP BY order_date
Now it will count the distinct buyerids in each day.
Also, curdate() - 7 doesn't seem to work so I rewrote it entirely, assuming order_date is of date type.
Mind you might miss results if its a datetime, and in that case compare it to now() or strip time entirely.
I don't get the meaning of the line AND date(order_date) = curdate(). It seems to be right only if you want today's sales.

Related

Count only where certain fields are unique? (Multiple GROUP BYs)

So I have a query which will count how many records there are for each date in the past 3 days, from my last question with the help of others I was able to come up with the following:
SELECT COUNT(*) AS dailyCount
FROM highscores
WHERE DATE(date) BETWEEN CURDATE() - INTERVAL 3 DAY AND CURDATE()
GROUP BY DATE(date)
The query does the following, for the past 30 days, it will get how many records there are on each individual day and that will be returned as dailyCount.
However what I want to do now is get it so that dailyCount will only count fields where username has not been counted before.
I have tried the following (adding username to the GROUP BYs):
SELECT COUNT(*) AS dailyCount
FROM highscores
WHERE DATE(date) BETWEEN CURDATE() - INTERVAL 3 DAY AND CURDATE()
GROUP BY DATE(date), username
However that did not give the expected result.
Credits to Wrikken
The following worked:
SELECT COUNT(DISTINCT username) AS dailyCount
FROM highscores
WHERE DATE(date) BETWEEN CURDATE() - INTERVAL 3 DAY AND CURDATE()
GROUP BY DATE(date)

How to count records as "0" that have value "0" when using WHERE x = 1

I have a table structure that looks like this:
I have a perfectly working query that counts how many records there have been per day the last 30 days. It looks likes this:
SELECT DATE(timestamp) AS date, COUNT(id) AS emails FROM 'emails WHERE timestamp >= now() - interval 1 month GROUP BY DATE(timestamp)
This outputs the following which is perfectly fine:
However, the next thing seems too difficult for me to imagine. Now I want to count how many records there have been per day the last 30 days BUT only where newsletter = 1.
I've tried to put a WHERE statement looking like this:
SELECT DATE(timestamp) AS date, COUNT(*) AS emails, nyhedsbrev FROM emails WHERE timestamp >= now() - interval 1 month AND nyhedsbrev = 1 GROUP BY DATE(timestamp)
... And that outputs the following:
The problem is, that its omitting the records with newsletter = 0 and there by I cant compare my first query against the new one, as the dates doesnt match. I know that is because I use WHERE newsletter = 1.
In stead of omitting the record I want a query that just puts a "0" from that date. How can I do this? The final query should be outputting this:
You should be able to simply use SUM() and IF() to get the desired output:
SELECT
DATE(timestamp) AS date,
COUNT(*) AS emails,
SUM(IF(nyhedsbrev > 0, 1, 0)) as nyhedsbrev_count
FROM
emails
WHERE
timestamp >= now() - interval 1 month
GROUP BY
DATE(timestamp)
SQLFiddle DEMO
Edit: You might even be able to simplify it, since it's a boolean, and simply use SUM(nyhedsbrev), but this REQUIRES that nyhedsbrev is only 0 or 1:
SELECT
DATE(timestamp) AS date,
COUNT(*) AS emails,
SUM(nyhedsbrev) as nyhedsbrev_count
FROM
emails
WHERE
timestamp >= now() - interval 1 month
GROUP BY
DATE(timestamp)
Possibly best to get a list of the dates and then left join that against sub queries to get the counts you require.
Something like this
SELECT Sub1.date, Sub2.emails, IFNULL(Sub3.emails, 0)
FROM (SELECT DISTINCT DATE(timestamp) AS date
FROM emails
WHERE timestamp >= now() - interval 1 month) Sub1
LEFT OUTER JOIN (SELECT DATE(timestamp) AS date, COUNT(id) AS emails
FROM emails WHERE timestamp >= now() - interval 1 month
GROUP BY DATE(timestamp)) Sub2
ON Sub2.date = Sub3.date
LEFT OUTER JOIN (SELECT DATE(timestamp) AS date, COUNT(*) AS emails
FROM emails
WHERE timestamp >= now() - interval 1 month AND nyhedsbrev = 1
GROUP BY DATE(timestamp)) Sub3
ON Sub1.date = Sub3.date
(you can probably optimise one subselect of this away, but I have done it in full to make it obvious how it is working)
Assuming newsletter is boolean 1/0 values then this might give you the table that you want:
SELECT DATE(timestamp) AS date, COUNT(*) AS emails, nyhedsbrev
FROM emails WHERE timestamp >= now() - interval 1 month GROUP BY DATE(timestamp),nyhedsbrev ;
Just adding another GROUP BY parameter.

Average posts per hour on MySQL?

I have a number of posts saved into a InnoDB table on MySQL. The table has the columns "id", "date", "user", "content". I wanted to make some statistic graphs, so I ended up using the following query to get the amount of posts per hour of yesterday:
SELECT HOUR(FROM_UNIXTIME(`date`)) AS `hour`, COUNT(date) from fb_posts
WHERE DATE(FROM_UNIXTIME(`date`)) = CURDATE() - INTERVAL 1 DAY GROUP BY hour
This outputs the following data:
I can edit this query to get any day I want. But what I want now is the AVERAGE of each hour of every day, so that if on Day 1 at 00 hours I have 20 posts and on Day 2 at 00 hours I have 40, I want the output to be "30". I'd like to be able to pick date periods as well if it's possible.
Thanks in advance!
You can use a sub-query to group the data by day/hour, then take the average by hour across the sub-query.
Here's an example to give you the average count by hour for the past 7 days:
select the_hour,avg(the_count)
from
(
select date(from_unixtime(`date`)) as the_day,
hour(from_unixtime(`date`)) as the_hour,
count(*) as the_count
from fb_posts
where `date` >= unix_timestamp(current_date() - interval 7 day)
and created_on < unix_timestamp(current_date())
group by the_day,the_hour
) s
group by the_hour
Aggregate the information by date and hour, and then take the average by hour:
select hour, avg(numposts)
from (SELECT date(`date`) as day, HOUR(FROM_UNIXTIME(`date`)) AS `hour`,
count(*) as numposts
from fb_posts
WHERE DATE(FROM_UNIXTIME(`date`)) between <date1> and <date2>
GROUP BY date(`date`), hour
) d
group by hour
order by 1
By the way, I prefer including the explicit order by, since most databases do not order the results of a group by. Mysql happens to be one database that does.
SELECT
HOUR(FROM_UNIXTIME(`date`)) AS `hour`
, COUNT(`id`) \ COUNT(DISTINCT TO_DAYS(`date`)) AS avgHourlyPostCount
FROM fb_posts
WHERE `date` > '2012-01-01' -- your optional date criteria
GROUP BY hour
This gives you a count of all the posts, divided by the number of days, by hour.

SQL Count Totals Within Date Ranges

I have a CHANGES table with fields VALUE(integer) and CREATED_AT(timestamp). I want to know the total of the VALUE column grouped by each of the past 30 days (without making 30 queries).
So if yesterday there were records created with VALUEs of 10, -7, and 12; I would want a record returned with CREATED_AT = yesterday and TOTAL = 15.
Any help?
SELECT date(created_at) as CREATED_AT, sum(value) as TOTAL
FROM changes
WHERE created_at >= curdate() - interval 30 day
GROUP BY date(created_at);
Well, it slightly depends on what kind the timestamp is formatted in (SQL/ Unix/ etc). But this type of query might help you along:
SELECT
DATE_FORMAT(CREATED_AT, '%Y-%m-%d') ym,
COUNT(VALUE)
FROM foo
GROUP BY ym

Combine two MySQL Count Queries

I have a table with columns: NAME, CHANGE_ID, and CHANGE_DATE, where each row constitutes a single change, the columns indicated who made the change(name), when it was made(timestamp), and an id for the change(integer).
I can retrieve a list of names sorted by those that have made the most changes(in the last month) with the following query:
SELECT
NAME AS name,
COUNT(DISTINCT CHANGE_ID) AS changes
FROM
CHANGE_TABLE
WHERE
DATE(CHANGE_DATE) > DATE(now() - INTERVAL 1 MONTH)
GROUP BY
name
ORDER BY
changes DESC
And I can retrieve a list of changes made per month in the last 10 months with the following query:
SELECT
DATE_FORMAT(CHANGE_DATE, '%Y-%m') AS date,
COUNT(DISTINCT CHANGE_ID) AS change_count
FROM
CHANGE_TABLE
WHERE
CHANGE_DATE > curdate() - INTERVAL 10 MONTH
GROUP BY
date
What I want is a query that will return the combined information of these queries: I want the names of the top change-makers and how many changes they have made each month for the last 10 months. I don't particularly care how the resulting table looks as long as the data is there. I have wracked my brain, but my SQL understanding is not great enough to solve the problem. Any help would be appreciated.
Have you tried grouping on date and name, something like:
SELECT
DATE_FORMAT(CHANGE_DATE, '%Y-%m') AS date,
COUNT(DISTINCT CHANGE_ID) AS change_count,
NAME
FROM
CHANGE_TABLE, (SELECT
NAME AS name,
COUNT(DISTINCT CHANGE_ID) AS changes
FROM CHANGE_TABLE
WHERE DATE(CHANGE_DATE) > DATE(now() - INTERVAL 1 MONTH)
GROUP BY name
ORDER BY changes DESC
) subq
WHERE CHANGE_DATE > curdate() - INTERVAL 10 MONTH AND change_table.name = subq.name
GROUP BY date, name