MySQL sum only new rows - mysql

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.

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

Getting percentage of total in SQL with two joins

So I'm trying to do something that I think should be fairly simple with SQL. But I'm having a hard time figuring it out. Here is the format of my data:
One table with user information, let's call it User:
ID name_user Drive_Type
1 Tim Stick shift
2 Jim Automatic
3 Bob Automatic
4 Lisa Stick shift
Then I have one table used for the join, let's call it Join_bridge:
user_ID car_has_ID
1 12
2 13
3 14
4 14
And one table with car information, let's call it Car:
car_ID name
12 Honda
13 Toyota
14 Ford
Then what I want is something that looks like this with the total number of Ford's that are stick shift and the percentage
name Total percentage
Ford 1 25%
I have tried the following, which gets the total right, but not the percentage:
select Drive_Type,
name,
count(Drive_Type) as Total,
(count(Drive_Type) / (select count(*)
from User
join Join_bridge
on User.ID = user_ID
join Car
on Car.car_ID = Join_bridge.car_has_ID
) * 100.0 as Percent
from User
join Join_bridge
on User.ID = Join_bridge.user_ID
join Car
on Car.car_ID = Join_bridge.car_has_ID
where name = 'Ford' and Drive_Type = "Automatic"
;
What am I missing? Thanks.
See this SQL Fiddle with the query - the trick is to SUM over CASE that returns 1 for rows you look for and 0 for the rest in order to calculate "Total" at the same time you can also count all rows to calculate percentage.
Here's the SQL query:
SELECT
'Ford' name,
SUM(a.ford_with_stack_flag) Total,
100.0 * SUM(a.ford_with_stack_flag) / COUNT(*) percentage
FROM (
SELECT
Car.name,
(CASE WHEN User.Drive_Type = 'Stick Shift' and Car.name = 'Ford' THEN 1 ELSE 0 END) ford_with_stack_flag
FROM User
JOIN Join_bridge on User.ID = Join_bridge.user_ID
JOIN Car ON Car.car_ID = Join_bridge.car_has_ID
) a
Compute percent and join to Car. Window functions are supported in MySql 8.0
select c.car_ID, c.name, p.cnt, p.Percent
from car c
join (
select car_has_ID, u.Drive_Type,
count(*) cnt,
count(*) / count(count(*)) over() Percent
from Join_bridge b
join user u on u.ID = b.user_ID
group by b.car_has_ID, u.Drive_Type
) p on p.car_has_ID = c.car_ID
where c.name = 'Ford' and p.Drive_Type='Stick shift';
db<>fiddle

Group by Member but Order by Count Product

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.

MySQL: Return user making over X requests in Y amount of time

If I had a list of users coming back from my DB, is it possible to filter out and return the user(s) that appear over X amount of times in Y amount of time?
Here's an example of what I'm trying to accomplish:
mysql> SELECT * FROM user_activity;
+----------+---------------------+
| username | created_at |
+----------+---------------------+
| User A | 2018-12-06 00:00:00 |
| User B | 2018-12-06 00:00:00 |
| User B | 2018-12-06 00:09:00 |
| User A | 2018-12-06 00:11:00 |
+----------+---------------------+
4 rows in set (0.00sec)
In MySQL, How would I return User B because this user appears more than once in a 10minute period? User A would be omitted from this result because they only appear once every 10minutes.
You need an aggregate query, with a HAVING clause to filters users based on their number of occurences. The filter on the date goes to the WHERE clause.
The folowing query selects all users having at least 2 entries within 10 minutes after the given #report_date :
set #report_date = "2018-12-06 00:00:00";
SELECT username
FROM user_activity
WHERE created_at
BETWEEN DATE_ADD(#report_date, INTERVAL 10 MINUTE)
AND #report_date
GROUP BY username
HAVING COUNT(*) > 1
SELECT
DISTINCT a.username
FROM
user_activity a
INNER JOIN user_activity b ON (
b.username = a.username
AND b.created_at > a.created_at
AND b.created_at <= DATE_ADD(a.created_at, INTERVAL 10 MINUTE)
)
I would think about using exists:
select ua.*
from user_activity ua
where (select count(*)
from user_activity ua2
where ua2.user_name = ua.user_name and
ua2.created_at <= ua.created_at and
ua2.created_at > ua.created_at - interval 10 minute
) > 1;
This method is flexible. Your question is generally about having n occurrences during a period. Normally, I advocate using exists when someone wants a single occurrence, but for the general question, count(*) is a respectable approach.

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)