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

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

Related

Big Query: Join single latest row from second table

I have two tables. One is a list of Orders, and one is a list of Events.
For each Order, I want to join the single last Event that happened (using clicked_at) before the created_at of the Order.
I have tried numerous ways to get this to work and tried several other answers on Stack Overflow but I am struggling to return the correct data.
The sudo logic for the subquery in my mind is something like:
SELECT campaign, user_id, created_at
FROM `Events`
WHERE order.user_id = user_id AND clicked_at < order.created_at
ORDER created_at DESC
LIMIT 1
Please see the example data below:
# Orders
| order_id | user_id | created_at |
-----------------------------------
| 123 | abc | 2020-07-04 |
| 456 | abc | 2020-05-01 |
# Events
| campaign | keyword | user_id | clicked_at |
----------------------------------------------
| facebook | shoes | abc | 2020-07-03 |
| google | hair | abc | 2020-07-01 |
My desired result
# Orders with campaign attribution
| order_id | user_id | created_at | campaign | keyword |
---------------------------------------------------------
| 123 | abc | 2020-07-04 | facebook | shoes |
| 456 | abc | 2020-06-04 | null | null |
Thanks!
Alex
Below is for BigQuery Standard SQL
#standardSQL
SELECT a.*, campaign, keyword
FROM `project.dataset.orders` a
LEFT JOIN (
SELECT
ANY_VALUE(o).*,
ARRAY_AGG(STRUCT(campaign, keyword) ORDER BY clicked_at DESC LIMIT 1)[OFFSET(0)].*
FROM `project.dataset.orders` o
JOIN `project.dataset.events` e
ON o.user_id = e.user_id
AND clicked_at < created_at
GROUP BY FORMAT('%t', o)
)
USING(order_id)
if applied to sample data from our question - result is
Row order_id user_id created_at campaign keyword
1 123 abc 2020-07-04 facebook shoes
2 456 abc 2020-05-01 null null
with orders as (
select 123 as order_id, 'abc' as user_id, cast('2020-07-04' as date) as created_at union all
select 456, 'abc', '2020-05-01'
),
events as (
select 'facebook' as campaign, 'shoes' as keyword, 'abc' as user_id, cast('2020-07-03' as date) as clicked_at union all
select 'google', 'hair', 'abc', '2020-07-01'
),
logic as (
select
orders.order_id,
orders.user_id,
orders.created_at,
events.clicked_at,
events.campaign,
events.keyword,
row_number() over (partition by orders.order_id order by events.clicked_at desc) as rn
from orders
left join events
on orders.user_id = events.user_id and events.clicked_at < orders.created_at
)
select * except(rn)
from logic
where rn = 1

Mysql query the most visited url per user

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

Laravel / MySQL query raw

Not sure on how to query this, but let's say I've got two tables as such
Table 1
| id | userid | points |
|:-----------|------------:|:------------:|
| 1 | 1 | 30
| 2 | 3 | 40
| 3 | 1 | 30
| 4 | 3 | 40
| 5 | 1 | 30
| 6 | 3 | 40
Table 2
| id | userid | productid |
|:-----------|------------:|:------------:|
| 1 | 1 | 4
| 2 | 3 | 4
| 3 | 1 | 3
| 4 | 3 | 3
| 5 | 1 | 3
| 6 | 3 | 3
I need to get all rows with s from table 1 where points are above 30 and where table2 has a productid of 4
At the moment I have a raw query like this:
SELECT userid, SUM(points) as points FROM table1 GROUP BY userid HAVING SUM(points) >= 30 ORDER BY SUM(points) DESC, userid
Through DB::select
How can I make sure that all of the results only have a product id of 4 via table2 connected via the userid? Is this where join is applicable and then I see leftjoin and others so I'm not too sure how to go about this, any suggestions appreciated.
EDIT:
I just got this working:
SELECT userid, SUM(points) as points FROM table1 LEFTJOIN table2 on table1.userid = table2.userid WHERE table2.productid = '4' GROUP BY userid HAVING SUM(points) >= 30 ORDER BY SUM(points) DESC, userid
It is giving me back to correct results, but not 100%sure on join/leftjoin, any feedback if that is OK?
If you use inner join you get only the related row that match between productid =4 and sum only this
SELECT userid, SUM(points) as points
FROM table1
inner join table2 on table1.id = table2.userid and productid=4
GROUP BY userid
HAVING SUM(points) >= 30
RDER BY SUM(points) DESC, userid
or if you are looking for the user that have on of the product = 4 then you can use
SELECT userid, SUM(points) as points
FROM table1
inner join (
select distinct userid
from table2 where productid =4
) t on table1.id = t.userid
GROUP BY userid
HAVING SUM(points) >= 30
RDER BY SUM(points) DESC, userid

Mysql query with group by and having clause

I am constructing query to get the maximum price for same product.
I have table as
-----------------------------------------
| prod_id | price | user_id |
-----------------------------------------
| 4 | 25 | 1 |
| 4 | 38 | 2 |
| 4 | 41 | 3 |
| 7 | 100 | 1 |
| 7 | 95 | 2 |
-----------------------------------------
I am trying to get following output:
-----------------------------------------
| 4 | 41 | 3 |
-----------------------------------------
| 7 | 100 | 1 |
-----------------------------------------
I have constructed following query which is not right.
select * from user_bid group by prod_id having max(price);
Can someone guide me to get the query for desired results.
SELECT *
FROM user_bid
WHERE (prodid, price) IN
(
SELECT prodid, MAX(price)
FROM user_bid
GROUP BY prod_id
)
Here is an option using a join:
SELECT p1.*
FROM prod_id
INNER JOIN
(
SELECT prod_id, MAX(price) AS price -- this inner subquery finds the maximum
FROM user_bid -- price for each product group
GROUP BY prod_id
) p2
ON p1.prod_id = p2.prod_id AND -- and this join condition retains
p1.price = p2.price -- only records which have this maximum
-- price for each product group
And here is an option which uses a subquery:
SELECT p1.*
FROM prod_id p1
WHERE price = (SELECT MAX(p2.price) FROM prod_id p2 WHERE p1.prod_id = p2.prod_id)
For SQL Server
;WITH cte AS
(
SELECT prod_id,price,user_id,
ROW_NUMBER() OVER(PARTITION BY prod_id ORDER BY price desc) as rank
FROM table Name
)
SELECT prod_id,price,user_id
FROM cte
WHERE rank=1
Use the below query:
SELECT * FROM (SELECT * FROM user_bid ORDER BY price DESC) AS Temp
GROUP BY prod_id
Output:

MySQL Inner Join two tables on the maximum values of the second table

I have two tables "listings" and "bids". I have a query that will return all the listings that a particular user has bid on, where the end_date is past. What I need to do though is limit the results even further to find only the listings where the user has bid, and is the last bidder. Here is the query I have so far...
SELECT listings.end_date, listings.user_id, listings.title, listings.auc_fp, listings.id, listings.auc_image1
FROM listings INNER JOIN bids ON listings.id = bids.listing_id
WHERE bids.user_id=$userid
AND listings.end_date < NOW()
ORDER BY list_ts DESC"
I'm not good with subqueries and I'm assuming I'm going to need one here to find all the users bids in the "bids" table where the bid_ts (bid timestamp) is the lastest timestamp for that corresponding listing_id in the bids table. The columns in my bids table are: listing_id, user_id, bid, bid_ts.
+------------+---------+------+---------------------+
| listing_id | user_id | bids | bid_ts |
+------------+---------+------+---------------------+
| 1 | 10 | 100 | 2012-11-16 00:54:03 |
| 1 | 11 | 101 | 2012-11-16 00:54:04 |
| 2 | 10 | 33 | 2012-11-16 00:54:03 |
| 2 | 11 | 34 | 2012-11-16 00:54:04 |
| 2 | 12 | 35 | 2012-11-16 00:54:05 |
+------------+---------+------+---------------------+
Thanks for any help
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT listing_ID, user_ID, MAX(bid_ts) maxDate
FROM tableName
GROUP BY listing_ID, user_ID
) b ON a.listing_ID = b.listing_ID AND
a.user_ID = b.user_ID AND
a.bid_ts = b.maxDate
SQLFiddle Demo