Mysql query the most visited url per user - mysql

I need to count the urls with the most visits per user.
Table name:visit_actions: mysql version:5.7
+----+--------+---------+---------------------+
| id | url_id | user_id | server_time |
+----+--------+---------+---------------------+
| 1 | 265338 | 4 | 2019-11-07 08:54:47 |
| 2 | 265405 | 1 | 2019-11-07 08:55:21 |
| 3 | 265391 | 4 | 2019-11-07 08:56:03 |
| 4 | 265338 | 1 | 2019-11-07 08:57:36 |
| 5 | 265338 | 1 | 2019-11-07 10:02:46 |
| 21 | 265207 | 5 | 2019-11-08 02:17:30 |
| 22 | 265207 | 5 | 2019-11-08 02:17:30 |
+----+--------+---------+---------------------+
I have tried this sql:
SELECT
url_id,
user_id,
count( * ) AS visit_times
FROM
visit_actions
GROUP BY
user_id,
url_id
ORDER BY
visit_times DESC
I expect the output :
+--------+---------+-------------+
| url_id | user_id | visit_times |
+--------+---------+-------------+
| 265338 | 4 | 1 |
| 265207 | 5 | 2 |
| 265338 | 1 | 2 |
+--------+---------+-------------+
Each user only finds the one with the most url_id.
Click Here Online Demo . Thanks folks!

On MySQL 8+ a fairly clean solution uses ROW_NUMBER with aggregation:
WITH cte AS (
SELECT url_id, user_id, COUNT(*) AS cnt,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY COUNT(*) DESC) rn
FROM visit_actions
GROUP BY url_id, user_id
)
SELECT
url_id,
user_id,
cnt AS visit_times
FROM cte
WHERE rn = 1;
If you had to do this on MySQL 5.7, here is one way:
SELECT
t1.url_id,
t1.user_id,
t1.cnt AS visit_times
FROM
(
SELECT url_id, user_id, COUNT(*) AS cnt
FROM visit_actions
GROUP BY url_id, user_id
) t1
INNER JOIN
(
SELECT user_id, MAX(cnt) AS max_cnt
FROM
(
SELECT url_id, user_id, COUNT(*) AS cnt
FROM visit_actions
GROUP BY url_id, user_id
) t
GROUP BY user_id
) t2
ON t1.user_id = t2.user_id AND
t1.cnt = t2.max_cnt;

Here's a MySQL 5.7 solution. Basically you have to find the maximum number of visits per user and then join the count of visits per user to that table to give the user and urls they have visited most. Note in your sample that yields 6 rows for user 1 as they have visited 6 sites twice.
SELECT c.url_id, c.user_id, c.visit_times
FROM (SELECT url_id, user_id, count( * ) AS visit_times
FROM visit_actions
GROUP BY user_id, url_id) c
JOIN (SELECT user_id, MAX(visit_times) AS max_visits
FROM (SELECT url_id, user_id, count( * ) AS visit_times
FROM visit_actions
GROUP BY user_id, url_id ) c
GROUP BY user_id) m ON m.user_id = c.user_id AND m.max_visits = c.visit_times
Output:
url_id user_id visit_times
265207 0 2
265338 1 2
265391 1 2
265394 1 2
265396 1 2
265410 1 2
265431 1 2
265338 4 1
Demo on SQLFiddle

Related

How to calculate the minimum amount during the period?

I have a table named wallet, which is like this:
// wallet
+----+----------+----------+------------+
| id | user_id | amount | created_at |
+----+----------+----------+------------+
| 1 | 5 | 1000 | 2022-05-20 | -- 1000
| 2 | 5 | 500 | 2022-05-20 | -- 1500
| 3 | 5 | -1000 | 2022-05-21 | -- 500 <-- this
| 4 | 5 | 4000 | 2022-05-23 | -- 4500
| 5 | 5 | -2000 | 2022-05-23 | -- 2500
| 6 | 5 | 1000 | 2022-05-24 | -- 3500
+----+----------+----------+------------+
As you can see, (after all deposits and withdrawals), sum(amount) is 500 at the lower point (minimum calculated amount) in the period which is happened at 2022-05-21. So, selecting this row is the expected result:
| 3 | 5 | -1000 | 2022-05-21 |
Any idea how can I get that?
select t0.id, t0.user_id, t0.sum_amt, t0.rank_amt
from
(
select t.id, t.user_id, sum_amt, rank() over(partition by t.user_id order by t.sum_amt) rank_amt
from
(
select t1.id, t1.user_id, SUM(t2.amount) as sum_amt
from wallet t1
inner join wallet t2 on t1.id >= t2.id and t1.user_id = t2.user_id
group by t1.id, t1.user_id
) t
) t0
where t0.rank_amt = 1;
Fiddle
WITH
cte1 AS ( SELECT *,
SUM(amount) OVER (PARTITION BY user_id
ORDER BY created_at ASC) cumulative_sum
FROM wallet
),
cte2 AS ( SELECT *,
ROW_NUMBER() OVER (PARTITION BY user_id
ORDER BY cumulative_sum ASC,
created_at DESC) rn
FROM cte1
)
SELECT *
FROM cte2
WHERE rn = 1;

How to sum and count Id's of different tables from a Union

I have 3 tables councils, station_levy, market_levy, and the station table is joined to get councils in station_levy.
I need to get data by council sum the number of station_levy + market_levy and get the total amount tendered.
Tables are as follows
councils
---------------+----------+
| council_id | Name |
+--------------+----------+
| 1 | LSK |
---------------+----------+
| 2 | KBW |
---------------+----------+
station_levy
------------------+-------------+-----------------+
| station_levy_id | station_id | amount_tendered |
+-----------------+-------------+-----------------+
| 1 | 3 | 10.00 |
+-----------------+-------------+-----------------+
| 2 | 3 | 10.00 |
+-----------------+-------------+-----------------+
| 3 | 1 | 5.00 |
+-----------------+-------------+-----------------+
(station_id = 1 is found in the LSK council_id=1 And station_id = 3 is found in the KBW council_id=2)
market_levy
------------------+-------------+-----------------+
| market_levy_id | council_id | amount_tendered |
+-----------------+-------------+-----------------+
| 1 | 1 | 5.00 |
+-----------------+-------------+-----------------+
| 2 | 2 | 5.00 |
+-----------------+-------------+-----------------+
| 3 | 1 | 5.00 |
+-----------------+-------------+-----------------+
mysql
SELECT c.council_name, (COUNT(market_levy.market_levy_id)+ COUNT(st.station_levy_id )) count, SUM(amount_tendered) revenue
FROM councils c
JOIN (
(SELECT council_id, amount_tendered,market_levy_id FROM market_levy WHERE transaction_date >= CURDATE() )
UNION ALL
(SELECT station_levy_id , councils.council_id, amount_tendered
FROM station_levy st
JOIN stations ON stations.station_id = st.station_id
JOIN councils ON councils .council_id= stations .council_id
WHERE transaction_datetime >= CURDATE()
)) totalCouncilRevenue USING (council_id)
group by council_id, c.council_name ORDER BY SUM(amount_tendered) DESC
Expected result
------------------+-------------+-----------------+
| council_name | count | revenue |
+-----------------+-------------+-----------------+
| LSK | 3 | 15.00 |
+-----------------+-------------+-----------------+
| KBW | 3 | 25.00 |
+-----------------+-------------+-----------------+
You are confusing columns in your UNION ALL matching council_id with station_levy_id, amount_tendered with council_id, and market_levy_id with amount_tendered.
Then, in your main query you try to access market_levy.market_levy_id and st.station_levy_id, but these columns are not accessible, as you select from a subquery called totalCouncilRevenue, not from tables labelled market_levy and st there.
Your query fixed:
SELECT
c.council_name,
COUNT(*) AS transaction_count,
SUM(amount_tendered) AS revenue
FROM councils c
JOIN
(
SELECT council_id, amount_tendered
FROM market_levy
WHERE transaction_date >= CURDATE()
UNION ALL
SELECT s.council_id, st.amount_tendered
FROM station_levy st
JOIN stations s ON s.station_id = st.station_id
WHERE st.transaction_datetime >= CURDATE()
) totalCouncilRevenue USING (council_id)
GROUP BY council_id, c.council_name
ORDER BY SUM(amount_tendered) DESC;
I prefer aggregating before joining, though:
SELECT
c.council_name,
COALESCE(t1.cnt, 0) + COALESCE(t2.cnt, 0) AS transaction_count,
COALESCE(t1.total, 0) + COALESCE(t2.total, 0) AS revenue
FROM councils c
LEFT JOIN
(
SELECT council_id, SUM(amount_tendered) as total, COUNT(*) as cnt
FROM market_levy
WHERE transaction_date >= CURDATE()
GROUP BY council_id
) t1 USING (council_id)
LEFT JOIN
(
SELECT s.council_id, SUM(st.amount_tendered) as total, COUNT(*) as cnt
FROM station_levy st
JOIN stations s ON s.station_id = st.station_id
WHERE st.transaction_datetime >= CURDATE()
GROUP BY s.council_id
) t2 USING (council_id)
ORDER BY revenue DESC;
Such queries are usually less prone to errors and are sometimes faster, because they may be able to use indexes more efficiently.

MySQL: Average rating per item, using latest rating for user

I have the items_ratings table as follows:
items_ratings
+----+--------+---------+---------+---------------------+
| id | rating | user_id | item_id | created (DATETIME) |
+----+--------+---------+---------+---------------------+
| 1 | 20 | 1 | 12 | 2017-07-12 14:00:04 |
| 2 | 80 | 2 | 12 | 2017-07-12 15:32:12 |
| 3 | 50 | 1 | 15 | 2017-08-01 11:14:04 |
| 4 | 90 | 1 | 12 | 2017-08-02 19:23:19 |
| 5 | 60 | 2 | 15 | 2017-08-05 19:23:19 |
+----+--------+---------+---------+---------------------+
I need to retrieve the average value per item_id, using each user's most recent rating.
The following gives me the average rating for each item:
SELECT AVG(rating) FROM items_ratings
GROUP BY item_id
I have also identified that the following query gives me the most recent row for each user_id, by item_id.
SELECT MAX(created), user_id, item_id FROM items_ratings
GROUP BY user_id, item_id;
I am unsure of how I should combine these queries to yield my desired result.
You could use a select from join table with max created by user_id
select item_id, avg( rating) from (
select * from items_ratings a
inner join (
SELECT MAX(created) t_created, user_id
FROM items_ratings
GROUP BY user_id
) t on t.user_id = a.user_id and t.t_created = a.created
) t1
group by item_id
The inner select get the max created by user_id, the other get all the rows that macht and the outer buil the avg on this group by item_id
and with the your new condition on item_id you could use
select item_id, avg( rating) from (
select * from items_ratings a
inner join (
SELECT MAX(created) t_created, user_id, item_id
FROM items_ratings
GROUP BY user_id, item_id
) t on t.user_id = a.user_id and t.t_created = a.created and t.item_id = a.item_id
) t1
group by item_id

SQL - Find Highest Single Day of Sales From Date Range

I am using MySQL..
I have a simple sales table as follow:
o----o----------o-----------o
| id | store_id | logDate |
o----o----------o-----------o
| 1 | 1 | 2015-1-13 |
| 2 | 1 | 2015-1-14 |
| 3 | 2 | 2015-1-11 |
| 4 | 2 | 2015-1-18 |
o----o----------o-----------o
And sale product table
o----o----------o---------o------------o
| id | sale_id | qty | price |
o----o----------o---------o------------o
| 1 | 1 | 1 | 10 |
| 2 | 2 | 1 | 10 |
| 3 | 2 | 1 | 10 |
| 4 | 3 | 1 | 10 |
| 5 | 3 | 1 | 10 |
| 6 | 3 | 1 | 10 |
| 7 | 4 | 1 | 10 |
| 8 | 4 | 1 | 10 |
o----o----------o---------o------------o
Expected Result
o-- --------o----------------o---------------------o
| store_id | SUM(price*qty) | Highest Date On |
o-----------o----------------o---------------------o
| 1 | 20 | 2015-1-14 |
| 2 | 30 | 2015-1-11 |
O-----------o----------------o---------------------o
How to achieve my expected result?
I have tried as follow but it didn't work as expected:
SELECT store_id, MAX(total), highestSingleDateOn
FROM (
SELECT SUM(price * qty) AS total,
DATE(s.logDate) AS highestSingleDateOn, s.store_id AS store_id
FROM sale_product sp JOIN sales s ON s.id = sp.sales_id
GROUP BY DATE(s.logDate), s.store_id
ORDER BY DATE(s.logDate) ASC
) AS result_for_highest_single_day
GROUP BY highestSingleDateOn, store_id
SELECT store_id, MAX(total), highestSingleDateOn
FROM (
SELECT SUM(price * qty) AS total,
DATE(s.logDate) AS highestSingleDateOn, s.store_id AS store_id
FROM sale_product sp JOIN sales s ON s.id = sp.sales_id
GROUP BY DATE(s.logDate), s.store_id
ORDER BY total DESC
) AS result_for_highest_single_day
GROUP BY store_id
I just have modified the script ORDER BY DATE(s.logDate) ASC >> ORDER BY total DESC
and GROUP BY highestSingleDateOn, store_id >> GROUP BY store_id.
*Above sql script,it uses the unstable features about group by of MYSQL.
*Then according to Mysql standard,I write a other version sql script.
select table1.*
from
( SELECT SUM(price * qty) AS total,
DATE(s.logDate) AS highestSingleDateOn, s.store_id AS store_id
FROM sale_product sp JOIN sales s ON s.id = sp.sale_id
GROUP BY DATE(s.logDate), s.store_id) as table1
,
(select tmp.store_id,MAX(tmp.total) as max_total from
(SELECT SUM(price * qty) AS total,
DATE(s.logDate) AS highestSingleDateOn, s.store_id AS store_id
FROM sale_product sp JOIN sales s ON s.id = sp.sale_id
GROUP BY DATE(s.logDate), s.store_id ) as tmp group by tmp.store_id) as table2
where table1.store_id = table2.store_id and table1.total=table2.max_total
One way to do this in MySQL is with multiple an aggregations and then a join. Perhaps an easier way is to use variables:
SELECT sd.*
FROM (SELECT sd.*,
(#rn := if(#s = store_id, #rn + 1,
if(#s := store_id, 1, 1)
)
) as rn
FROM (SELECT DATE(s.logDate) AS date, s.store_id, SUM(price * qty) AS total
FROM sale_product sp JOIN sales s ON s.id = sp.sales_id
GROUP BY DATE(s.logDate), s.store_id
ORDER BY s.store_id, total desc
) sd cross join
(SELECT #rn := 0, #s := -1) params
) sd
WHERE rn = 1;

How to select only the latest rows for each user?

My table looks like this:
id | user_id | period_id | completed_on
----------------------------------------
1 | 1 | 1 | 2010-01-01
2 | 2 | 1 | 2010-01-10
3 | 3 | 1 | 2010-01-13
4 | 1 | 2 | 2011-01-01
5 | 2 | 2 | 2011-01-03
6 | 2 | 3 | 2012-01-13
... | ... | ... | ...
I want to select only the latest users periods entries, bearing in mind that users will not all have the same period entries.
Essentially (assuming all I have is the above table) I want to get this:
id | user_id | period_id | completed_on
----------------------------------------
3 | 3 | 1 | 2010-01-13
4 | 1 | 2 | 2011-01-01
6 | 2 | 3 | 2012-01-13
Both of the below queries always resulted with the first user_id occurance being selected, not the latest (because the ordering happens after the rows are selected from what I understand):
SELECT
DISTINCT user_id,
period_id,
completed_on
FROM my_table
ORDER BY
user_id ASC,
period_id DESC
SELECT *
FROM my_table
GROUP BY user_id
ORDER BY
user_id ASC,
period_id DESC
Seems like this should work using MAX and a subquery:
SELECT t.Id, t.User_Id, t.Period_Id, t.Completed_On
FROM my_table t
JOIN (SELECT Max(completed_on) Max_Completed_On, t.User_Id
FROM my_table
GROUP BY t.User_ID
) t2 ON
t.User_Id = t2.User_Id AND t.Completed_On = t2.Max_Completed_On
However, if you potentially have multiple records where the completed_on date is the same per user, then this could return multiple records. Depending on your needs, potentially adding a MAX(Id) in your subquery and joining on that would work.
try this:
SELECT t.Id, t.User_Id, t.Period_Id, t.Completed_On
FROM table1 t
JOIN (SELECT Max(completed_on) Max_Completed_On, t.User_Id
FROM table1 t
GROUP BY t.User_ID) t2 ON t.User_Id = t2.User_Id AND t.Completed_On = t2.Max_Completed_On
DEMO HERE