Group by Member but Order by Count Product - mysql

I want to display promos from all members in the order of the highest number of clicks in the last 1 week, terms that 1 member only displays 1 promo
like this:
id promo
member/sales
order by count
9
B
10
5
A
9
6
M
8
The following is the data table
Tabel Promo (product_promo)>> | ppo_id | ppo_sales | ppo_content |
Tabel click recording (ipslsprod) >> | idip | dipp (id promo) | mxd (datetime visit click)
Tabel sales (sales) >> | id | name |
And I've written down the code
SELECT pp.*,
s.*,
Count(ip.dipp) AS VIEWS
FROM product_promo pp
left join (SELECT *
FROM ipslsprod
WHERE mxd >= Date_sub(Now(), interval 7 day)) ip
ON ip.dipp = pp.ppo_id
left join sales s
ON pp.ppo_sales = s.id
GROUP BY pp.ppo_id
ORDER BY VIEWS DESC
But showing more than 1 member
id promo
member/sales
order by count
9
B
10
5
A
9
4
B
7
I've tried changing
GROUP BY pp.ppo_sales
The data is messy.
Please help and instructions,

I want to display each sales only 1 product_promo in the order of the highest number of ip.dipps.
You can use aggregation and window functions. I'm not sure exactly what the query is, but I think:
SELECT v.*
FROM (SELECT s.id, pp.id_product_promo, COUNT(*) AS views ,
ROW_NUMBER() OVER (PARTITION BY s.id ORDER BY COUNT(*) DESC) as seqnum
FROM sales s JOIN
product_promo pp
ON pp.ppo_sales = s.id JOIN
ipslsprod ip
ON ip.dipp = pp.ppo_id
WHERE ip.mxd >= DATE_SUB(NOW(),INTERVAL 7 day)) ip
GROUP BY s.id, pp.id_product_promo
) v
WHERE seqnum = 1;

Maybe this will enlighten a bit more:
SELECT your_fields, MAX(count_click) FROM [table] GROUP BY count_click ORDER BY count_click
From there you can add all the date related stuff to fine-tune the query.
Perhaps it is a good idea to read up on the MySQL INTERVAL for that.

Related

MySQL - Count unique users each day considering all previous days

I would like to count how many new unique users the database gets each day for all days recorded.
There will not be any duplicate ids per day, but there will be duplicates over multiple days.
If my table looks like this :
ID | DATE
---------
1 | 2022-05-21
1 | 2022-05-22
2 | 2022-05-22
1 | 2022-05-23
2 | 2022-05-23
1 | 2022-05-24
2 | 2022-05-24
3 | 2022-05-24
I would like the results to look like this :
DATE | NEW UNIQUE IDs
---------------------------
2022-05-21 | 1
2022-05-22 | 1
2022-05-23 | 0
2022-05-24 | 1
A query such as :
SELECT `date` , COUNT( DISTINCT id)
FROM tbl
GROUP BY DATE( `date` )
Will return the count per day and will not take into account previous days.
Any assistance would be appreciated.
Edit : Using MySQL 8
The user is new when the date is the least date for this user.
So you need in something like
SELECT date, COUNT(new_users.id)
FROM calendar
LEFT JOIN ( SELECT id, MIN(date) date
FROM test
GROUP BY id ) new_users USING (date)
GROUP BY date
calendar is either static or dynamically generated table with needed dates list. It can be even SELECT DISTINCT date FROM test subquery.
Start with a subquery showing the earliest date where each id appears.
SELECT MIN(`date`) `firstdate`, id
FROM tbl
GROUP BY id
Then do your count on that subquery. here.
SELECT firstdate, COUNT(*)
FROM (
SELECT MIN(`date`) `firstdate`, id
FROM tbl
GROUP BY id
) m
GROUP BY firstdate
That gives you what you want.
But it doesn't have rows for the dates where no new user ids first appeared.
Only count (and sum) the rows where the left join fails:
SELECT
m1.`DATE` ,
sum(CASE WHEN m2.id is null THEN 1 ELSE 0 END) as C
FROM mytable m1
LEFT JOIN mytable m2 ON m2.`DATE`<m1.`DATE` AND m2.ID=m1.ID
GROUP BY m1.`DATE`
see: DBFIDDLE

How to "Group by" with multiple tables in MySQL

I've two tables.
1. Users (id,name,createdAt)
2. Images (id,createdAt)
I want to generate a report something like this
date | newUsers | newImages
2019-09-12 | 12 | 3
2019-09-13 | 15 | 5
2019-09-14 | 16 | 8
What I've done upto now is
SELECT
u.newUsers,
i.newImages
FROM
(SELECT
COUNT(*) AS newUsers,createdAt
FROM
users group by date(createdAt)) u
left join
(select count(*) as newImages,createdAt from images group by date(createdAt)) i
This has a syntax error unfortunately.
How do I achieve this using mysql?
Also I am using Server version: 5.7.27-0ubuntu0.18.04.1 (Ubuntu)
you have missing on clause
select
u.newUsers,
i.newImages,
u.createdAt
from
(select
count(*) AS newUsers
, date(createdAt) as createdAt
from
users
group by date(createdAt)) u
left join
(select count(*) as newImages
, date(createdAt) as createdAt
from images
group by date(createdAt)) i
on i.createdAt = u.createdAt
You can try below way -
SELECT u.createdAt,COUNT(*) AS newUsers,count(*) as newImages
FROM users u left join images i on u.createdAt=i.createdAt
left join
group by u.createdAt
If you want to be sure that you don't miss any dates, then left join is not sufficient. One method is union all/group by:
select date, sum(is_user) as num_users, sum(is_image) as num_images
from ((select date(createdat) as date, 1 as is_user, 0 as is_image
from users
) union all
(select date(createdat) as date, 0 as is_user, 1 as is_image
from images
)
) ui
group by date;

MySQL sum only new rows

I have a MySQL table with this format
transaction | user | amount | week
------------|---------|--------|------
1 | user_1 | 100 | 1
2 | user_2 | 50 | 1
3 | user_1 | 50 | 2
4 | user_3 | 200 | 2
I know how to calculate the sum amount for each week in MySQL, but is there a way in MySQL to calculate new users sum amount per week?
So for this table it would be:
week 1 = 150
week 2 = 200
Yes, there is. You can use the value of MIN(week) to indicate a particular user's first transaction.
SELECT w, SUM(amount)
FROM(
SELECT user, amount, MIN(week) AS w
FROM `trans`
GROUP BY user) newUserTrans
GROUP BY w
If you want to still show a week where there is no new user, then you can use this:
SELECT week, IFNULL(SUM(amount),0) AS total
FROM(
SELECT user, amount, MIN(week) AS w
FROM `trans`
GROUP BY user) newUserTrans RIGHT JOIN (SELECT DISTINCT week FROM trans) weeks ON newUserTrans.w = weeks.week
GROUP BY w
ORDER BY week
UPDATE:
Based on #skobaljic opinion, I also provide another alternative for the case when a user may have multiple records in the same week.
SELECT weeks.week AS Week, IFNULL(SUM(amount),0) AS Total
FROM(
SELECT trans.user, trans.amount, trans.week
FROM trans
JOIN (SELECT user, MIN(week) AS w
FROM trans
GROUP BY user) newWeek ON trans.user = newWeek.user
AND trans.week = newWeek.w) newUserTrans
RIGHT JOIN (SELECT DISTINCT week FROM trans) weeks ON newUserTrans.week = weeks.week
GROUP BY newUserTrans.week
ORDER BY weeks.week
Hope it helps.
I hope this could be helpful in any way:
select sum(A.`amount`) from `table` AS A WHERE A.`user` NOT IN (SELECT B.`user` FROM `table` AS B where A.`week` > B.`week') AND A.`week` = #pWeek
where #pWeek is a parameter with a particular number of a week
I think there is a better way of get the first week for a user.
First week is the minumum week for a user.
select
trans.week
,sum(amount) as amount
from trans
inner join
(select
user
,min(week) as firstweek
from
trans
group by
user
) as firstweek
on trans.user = firstweek.user
where
trans.week = firstweek.firstweek
group by
trans.week
$query = "SELECT * FROM [table] GROUP BY week";
this should work. I don't think you will need the sum(amount), but try it both ways.

MySQL query - get sum of counts depending on column

I have a mysql table with actions and question_ids. Each action comes with a score like this:
ACTION | SCORE
downvote_question | -1
upvote_question | +1
in_cardbox | +2
I want to query for the question with the highest score but I can't figure it out.
http://sqlfiddle.com/#!2/84e26/15
So far my query is:
SELECT count(*), l1.question_id, l1.action
FROM `log` l1
GROUP BY l1.question_id, l1.action
which gives me every question_id with all its accumulated actions.
What I want is this:
QUESTION_ID | SCORE
2 | 5
1 | 4
3 | 1
4 | 1
5 | 1
I can't figure it out - I probably need subqueries, JOINS or UNIONS...
Maybe you can try this one.
SELECT a.question_id, sum(b.score) totalScore
FROM `log` a INNER JOIN scoreValue b
on a.`action` = b.actionname
group by a.question_id
ORDER BY totalScore DESC
SQLFiddle Demo
You should replace count(*) with sum(l1.score) because sum will add all values based on group by statement
SELECT sum(l1.score), l1.question_id, l1.action
FROM `log` l1
GROUP BY l1.question_id, l1.action
With constant scores works on SQL Fiddle (with grouping by question):
SELECT
sum(
CASE WHEN l1.action = 'downvote_question' THEN -1
WHEN l1.action = 'upvote_question' THEN 1
ELSE 2 END
) score,
l1.question_id
FROM `log` l1
GROUP BY l1.question_id
SELECT sum(SCORE), l1.question_id, l1.action
FROM `log` l1
GROUP BY l1.question_id, l1.action
is it what you want to?
upd: in your code on fidel, there is no such column as score, but i think it wont be a problem to create a tabel with action | score and join it to sum(score)

MySQL - Complex COUNT Query

I have a table called user_scores as below:
id | af_id | uid | level | record_date
----------------------------------------
1 | 1.1 | 1 | 3 | 2012-01-01
2 | 1.1 | 1 | 4 | 2012-02-01
3 | 1.2 | 1 | 3 | 2012-01-01
4 | 1.2 | 1 | 5 | 2012-03-01
...
I have another table call user_info as below:
uid | forename | surname | gender
-----------------------------------
1 | Homer | Simpson | M
2 | Marge | Simpson | F
3 | Bart | Simpson | M
4 | Lisa | Simpson | F
...
In user scores uid is the user id of a registered user on the system, af_id identifies a particular test a user submits. A user scores a level between 1 - 5 for each test, which can be submitted every month.
My problem is I need to produce an analysis at the end of the year to COUNT the number of users that have achieved each level for a particular test. The analysis is to show a gender split for male and female.
So for example an administrator would select test 1.1 and the system would generate stats based that would COUNT of the total MAX level achieved by each user in the year, with a gender split.
Any help is much appreciated. Thank you in advance.
-
I think I need to clarify myself a bit. Because a user can complete the test multiple times throughout the year, there will be multiple scores for the same test. The query should take the highest level achieved and include this in the count. An example result would be:
Male Results:
level1 | level2 | level3 | level4 | level5
------------------------------------------
2 | 5 | 10 | 8 | 1
I am not certain I get exactly what you mean, but as always I'll have a go. As I understand it you want to know how many people from each gender reached each level in a certain year.
SELECT MaxLevel,
COUNT(CASE WHEN ui.Gender = 'M' THEN 1 END) AS Males,
COUNT(CASE WHEN ui.Gender = 'F' THEN 1 END) AS Females
FROM User_Info ui
INNER JOIN
( SELECT MAX(Level) AS MaxLevel,
UID
FROM User_Scores us
WHERE af_ID = '1.1'
AND YEAR(Record_Date) = 2012
GROUP BY UID
) AS MaxUs
ON MaxUs.uid = ui.UID
GROUP BY MaxLevel
I've put some sample data on SQL Fiddle so you see if it is what you were after.
EDIT
To transpose the data so levels are along the top and Gender in the rows the following will work:
SELECT Gender,
COUNT(CASE WHEN MaxLevel = 1 THEN 1 END) AS Level1,
COUNT(CASE WHEN MaxLevel = 2 THEN 1 END) AS Level2,
COUNT(CASE WHEN MaxLevel = 3 THEN 1 END) AS Level3,
COUNT(CASE WHEN MaxLevel = 4 THEN 1 END) AS Level4,
COUNT(CASE WHEN MaxLevel = 5 THEN 1 END) AS Level5
FROM User_Info ui
INNER JOIN
( SELECT MAX(Level) AS MaxLevel,
UID
FROM User_Scores us
WHERE af_ID = '1.1'
AND YEAR(Record_Date) = 2012
GROUP BY UID
) AS MaxUs
ON MaxUs.uid = ui.UID
GROUP BY Gender
Note, that if there are ever more than 5 levels you will need to add more to the select statement, or start building dynamic SQL.
Assuming record_date holds only dates (without time parts):
SELECT
s.maxlevel,
COUNT(NULLIF(gender, 'F')) AS M,
COUNT(NULLIF(gender, 'M')) AS F
FROM user_info u
INNER JOIN (
SELECT
uid,
MAX(level) AS maxlevel
FROM user_scores
WHERE record_date > DATE_SUB(CURDATE(), INTERVAL DAYOFYEAR(CURDATE()) DAY)
AND af_id = '1.1'
GROUP BY
uid
) s ON s.uid = u.uid
GROUP BY
s.maxlevel
That will show you only the maximum levels found in the user_scores table. If you have a Levels table where all possible levels (1 to 5) are listed, you could use that table to get a complete list of levels. If some levels are not present in the requested subset of data, the corresponding rows will show 0s in both columns.
Here's the above script with minor changes to show the complete chart of levels:
SELECT
l.level AS maxlevel,
COUNT(NULLIF(gender, 'F')) AS M,
COUNT(NULLIF(gender, 'M')) AS F
FROM user_info u
INNER JOIN (
SELECT
uid, MAX(level) AS maxlevel
FROM user_scores
WHERE record_date > DATE_SUB(CURDATE(), INTERVAL DAYOFYEAR(CURDATE()) DAY)
AND af_id = '1.1'
GROUP BY
uid
) s ON s.uid = u.uid
RIGHT JOIN Levels l ON s.maxlevel = l.level
GROUP BY
l.level
Hope this is what your looking for!
Show number of records group by userid and gender of the max score for af_id '1.1'.
select count(*), info.uid, info.gender, max(score.level)
from user_info as info
join user_scores as score
on info.uid = score.uid
where score.af_id = '1.1'
group by info.uid, info.gender;
EDITED based on your edit.
select sum(if(a.gender="M",1,0)) Male_users, sum(if(a.gender="F",1,0)) Female_users
from myTable a where
a.level = (select max(b.level) from myTable b where a.uid=b.uid)
group by af_id.
I typed this in a rush. But it should work or at least get you where you need to go. E.G. if you need to specify time frame, add that.
You need something like
SELECT
uid,
MAX(level)
WHERE
record_date BETWEEN '2012-01-01' AND '2012-12-31'
AND af_id='1.1'
GROUP BY uid
If you need the gender splits then depending on what stat you need per gender you can either add a JOIN on the user_info table into this query (to get the MAX per gender) to wrap this as a sub-query and JOIN on the whole thing.