Is it possible to get all these stats with one query? - mysql

I have a Vote-scoring system, each user can score any product each day (maximum of 10 points a day, but they can go on the same product each day).
The schema for my vote table is like so:
Vote: ID, user_id, product_id, score, date.
What I'd like to do is not only fetch the total score and amount of individual votes, (so I can work out an average) but also get the unique amount of voters (DISTINCT user_id's) in the current time frame (in this example, a month). The current query I have is:
SELECT
SUM(`Vote`.`score`) AS `score`,
COUNT(*) AS `votes`,
CONCAT(YEAR(`Vote`.`date`), '-', MONTH(Vote.date)) AS `month`
FROM
`votes` AS `Vote`
WHERE
`product_id` = 4
GROUP BY
month
ORDER BY `Vote`.`date` DESC
Thanks in advance.

Use COUNT(DISTINCT user_id) in your SELECT list.
You can also have AVG(score) calculated.
SELECT
SUM(score) AS totalScore,
COUNT(*) AS totalVotes,
COUNT(DISTINCT user_id) AS voters,
AVG(score) AS averageScore,
CONCAT(YEAR(`date`), '-', MONTH(`date`)) AS `month`
FROM
votes
WHERE
product_id = 4
GROUP BY
`month`
ORDER BY `date` DESC

Related

How to count a field per day and then GROUP BY YEARWEEK

If i have a database with 2 columns, date and account and i want to first count account per day and then group by week. How wrong is my code and how to do it?
I edited my code a little bit, i was not thinking right from the beginning. I want the sum to be 9 for week 48.
SELECT date, account,
(SELECT date, COUNT(DISTINCT account)
FROM t1
GROUP BY date
) AS sum
FROM t1
GROUP BY YEARWEEK(date)
You seem to be looking for a simple aggregate query with count(distinct ...):
select yearweek(date) year_week, count(distinct account) cnt_account
from t1
group by yearweek(date)
order by year_week
Note: yearweek() gives you the year and week; this is better than week(), if your data spreads over several years.
EDIT
From the comments, you need two levels of aggregation:
select yearweek(dy) year_week, sum(cnt) cnt_account
from (
select date(t1.date) dy, count(distinct t1.account) cnt
from t1
group by date(t1.date)
) t
group by yearweek(dy)
order by year_week

MySQL Select Top Row Grouping By Another Row

I have a sales table and I want to get each members most frequently shopped store in the last 3 months. The following query will get the every member with every store, but I want just one store per member.
SELECT member_id, store_id, COUNT(DISTINCT docket) as docket_count, SUM(dollar_amount) as dollars
FROM sales
WHERE TIMESTAMPDIFF(MONTH, sale_date, CURDATE()) < 3
GROUP BY member_id, store_id
ORDER BY member_id, docket_count DESC, dollars DESC
Or to get the top store for a single member
SELECT store_id, COUNT(DISTINCT docket) as docket_count, SUM(dollar_amount) as dollars
FROM sales
WHERE TIMESTAMPDIFF(MONTH, sale_date, CURDATE()) < 3
AND member_id = 1
GROUP BY store_id
ORDER BY docket_count DESC, dollars DESC
This is tricky. In MySQL, this can be easiest using the group_concat()/substring_index() trick:
SELECT member_id,
SUBSTRING_INDEX(GROUP_CONCAT(store_id ORDER BY docket_count DESC dollars DESC), ',', 1) as Most_Common_Store
FROM (SELECT member_id, store_id, COUNT(DISTINCT docket) as docket_count,
SUM(dollar_amount) as dollars
FROM sales
WHERE sale_date >= CURDATE() - interval 3 month
GROUP BY member_id, store_id
) ms
GROUP BY member_id;

How to Group a table and get results for a row based on the previous rows' data

I have a lookup table that relates dates and people associated with those dates:
id, user_id,date
1,1,2014-11-01
2,2,2014-11-01
3,1,2014-11-02
4,3,2014-11-02
5,1,2014-11-03
I can group these by date(day):
SELECT DATE_FORMAT(
MIN(date),
'%Y/%m/%d 00:00:00 GMT-0'
) AS date,
COUNT(*) as count
FROM user_x_date
GROUP BY ROUND(UNIX_TIMESTAMP(created_at) / 43200)
But, how can get the number of unique users, that have now shown up previously? For instance this would be a valid result:
unique, non-unique, date
2,0,2014-11-01
1,1,2014-11-02
0,1,2014-11-03
Is this possibly without having to rely on a scripting language to keep track of this data?
I think this query will do what you want, at least it seems to work for your limited sample data.
The idea is to use a correlated sub-query to check if the user_id has occurred on a date before the date of the current row and then do some basic arithmetic to determine number of unique/non-unique users for each date.
Please give it a try.
select
sum(u) - sum(n) as "unique",
sum(n) as "non-unique",
date
from (
select
date,
count(user_id) u,
case when exists (
select 1
from Table1 i
where i.user_id = o.user_id
and i.date < o.date
) then 1 else 0
end n
from Table1 o
group by date, user_id
) q
group by date
order by date;
Sample SQL Fiddle
I didn't include the id column in the sample fiddle as it's not needed (or used) to produce the result and won't change anything.
This is the relevant question: "But, how can get the number of unique users, that have now shown up previously?"
Calculate the first time a person shows up, and then use that for the aggregation:
SELECT date, count(*) as FirstVisit
FROM (SELECT user_id, MIN(date) as date
FROM user_x_date
GROUP BY user_id
) x
GROUP BY date;
I would then use this as a subquery for another aggregation:
SELECT v.date, v.NumVisits, COALESCE(fv.FirstVisit, 0) as NumFirstVisit
FROM (SELECT date, count(*) as NumVisits
FROM user_x_date
GROUP BY date
) v LEFT JOIN
(SELECT date, count(*) as FirstVisit
FROM (SELECT user_id, MIN(date) as date
FROM user_x_date
GROUP BY user_id
) x
GROUP BY date
) fv
ON v.date = fv.date;

Select average monthly count, grouped by a field

I'm not sure if this has been asked before, as I don't know how to best phrase this question.
Given a query like:
SELECT DATE_FORMAT(i.created, '%Y-%m') as 'period',
COUNT(id) as 'total',
i.company_id
FROM invoice i
GROUP BY period, i.company_id
ORDER BY period DESC, total DESC
How can I return the average and/or mean count per month, grouped by company_id? It is important to only count those periods where there actually are any invoices.
If you want to exclude zero months then add HAVING condition and then select AVG() for each company using your query as a base:
SELECT company_id, AVG(total)
FROM
(SELECT DATE_FORMAT(i.created, '%Y-%m') as 'period',
COUNT(id) as 'total',
i.company_id
FROM invoice i
GROUP BY period, i.company_id
HAVING COUNT(id)>0
) as T1
GROUP BY company_id

Using DISTINCT, COUNT and HAVING in a MySQL Query

I want to count all players that have a sum of at least 300 points after a given date. I have this:
SELECT
COUNT(DISTINCT playerID)
FROM
table
WHERE
game_date > '$date'
HAVING
SUM(points) >= 300
Which is counting also the players that have a sum of less than 300 points. How can I solve this?
As pointed out by Andre Bossard:
To get the list of all players with points>=300:
SELECT * FROM (SELECT playerID, SUM(points) points FROM table WHERE game_date>'$date' GROUP BY playerID) a WHERE points>=300
To get the count:
SELECT COUNT(*) FROM (SELECT playerID, SUM(points) points FROM table WHERE game_date>'$date' GROUP BY playerID) a WHERE points>=300
Use a subquery for that:
SELECT COUNT(*) FROM
(
SELECT playerID, SUM(points) AS `total`
FROM `table`
WHERE game_date > '$date'
GROUP BY playerID
) tmp
WHERE total>=300