mysql select related items based on current item - mysql

i have a scenario, lets say 3 users review a business of id 10, how can i get all the unique id of the user who review that business and and use that unique id to find another business review which is not equal to 10 ?
sample table user_review:
review_id | user_id | business_id | rating | review_date
1 2 10 3 20121030124001
2 2 9 3 20121022120627
3 2 10 4 20121023120627
4 3 10 4 20121024120627
5 3 6 3 20121022140627
6 4 10 2 20121025120627
7 4 10 5 20121030120627
8 3 10 2 20121010120627
9 4 8 5 20121028120627
i should get result of these
review_id | user_id | business_id | rating | review_date
2 2 9 3 20121022120627
5 3 6 3 20121022140627
9 4 8 5 20121028120627
In a above result if there is 2 reviews for a same user_id and same business_id the latest one should be return.Thanks

Try this query:
Here is link to sqlfidle with running results http://sqlfiddle.com/#!2/cd4ea/1
SELECT
tblreview.*,tblusers.user_name
FROM
(
SELECT
MAX(tblreview.review_id) review_id
, tblreview.user_id
FROM
(
SELECT
DISTINCT user_id
FROM
tblreview
WHERE business_id = 10
) reviewsb10
INNER JOIN
tblreview
ON
tblreview.user_id = reviewsb10.user_id
AND
tblreview.business_id <> 10
GROUP BY
user_id
) tblLastReviewPerUser
INNER JOIN
tblreview
ON
tblreview.review_id = tblLastReviewPerUser.review_id
INNER JOIN
tblusers
ON
tblusers.user_id = tblLastReviewPerUser.user_id

SELECT t1.*
FROM TableName t1
INNER JOIN
(
SELECT user_id, business_id, MAX(review_date) review_date
FROM TableName
WHERE business_id <> 10
GROUP BY user_id, business_id
) t2 ON t1.user_id = t2.user_id
AND t1.review_date = t2.review_date
AND t1.business_id = t2.business_id

Try this query -
SELECT * FROM (
SELECT * FROM user_review
ORDER BY IF(business_id = 10, 1, 0), review_date DESC
) t
GROUP BY user_id
HAVING
COUNT(IF(business_id = 10, 1, NULL)) > 0
AND COUNT(IF(business_id <> 10, 1, NULL)) > 0
+-----------+---------+-------------+--------+----------------+
| review_id | user_id | business_id | rating | review_date |
+-----------+---------+-------------+--------+----------------+
| 2 | 2 | 9 | 3 | 20121022120627 |
| 5 | 3 | 6 | 3 | 20121022140627 |
| 9 | 4 | 8 | 5 | 20121028120627 |
+-----------+---------+-------------+--------+----------------+

Related

IN MYSQL how to pick up the largest count each row?

In mysql. I want to get some the largest counted review_id and mention.
below table is raw data
>> table Named: topic_id_review_id
id topic_id review_id
+-------+-------------+-------------+
1 1 1
2 1 1
3 1 1
4 2 1
5 4 1
6 3 2
7 5 2
8 2 3
9 2 3
10 3 3
11 3 1
12 5 4
13 5 1
14 6 5
16 7 7
17 7 7
18 7 7
...
....
**I want to get the top of four(4) topics about each of review_id.
>> wanted!!
topic_id review_id
+-------------+-------------+
1 1
2 1
3 1
4 1
3 2
5 2
2 3
3 3
5 4
...
....
like above table, I want to top 4 topics in Every review_id.
I have already tried some code. but all of thing is not good results...
SELECT
tr1.topic_id, tr1.review_id
FROM topic_id_review_id tr1
WHERE review_id IN (1,2,3)AND(
SELECT COUNT(*)FROM topic_id_review_id tr2
WHERE tr2.review_id = tr1.review_id) < 4;
SELECT
COUNT(*) AS `count`
FROM topic_id_review_id tr2
WHERE tr2.review_id = 1
GROUP BY topic_id, review_id
order by `count` DESC LIMIT 4;
maybe your guy, this not difficult... but as beginner to me...
it is not simple. so please let me know to to solve it! thanks
One way that works in all versions of MySql is the use of a correlated subquery in the WHERE clause which counts the number of topic_ids for each review_id:
select
t.topic_id, t.review_id
from (
select min(id) id, topic_id, review_id
from topic_id_review_id
group by topic_id, review_id
) t
where (
select count(*) from topic_id_review_id
where review_id = t.review_id and topic_id <= t.topic_id and id < t.id
) <= 4
order by t.review_id, t.topic_id
See the demo.
For MySql 8.0+:
with cte as (
select
t.topic_id, t.review_id,
row_number() over (partition by t.review_id order by t.topic_id, t.id) rn
from (
select min(id) id, topic_id, review_id
from topic_id_review_id
group by topic_id, review_id
) t
)
select
c.topic_id, c.review_id
from cte c
where c.rn <= 4
order by c.review_id, c.topic_id
See the demo.
Results:
| topic_id | review_id |
| -------- | --------- |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 3 | 2 |
| 5 | 2 |
| 2 | 3 |
| 3 | 3 |
| 5 | 4 |
| 6 | 5 |
| 7 | 7 |

How to select rows, using group by with minimum field values?

Today I have posted a question and got a good answer: Stuck in building mysql query.
I though it helped me, but I've discovered that it returns wrong data. So I'm reposting the question here, with an answer I received, as well I will explain the problem why it is not working for me.
Example of data:
id | item_id | user_id | bid_price
----------------------------------
1 | 1 | 11 | 1
2 | 1 | 12 | 2
3 | 1 | 13 | 3
4 | 1 | 14 | 1
5 | 1 | 15 | 4
6 | 2 | 16 | 2
7 | 2 | 17 | 1
8 | 3 | 18 | 2
9 | 3 | 19 | 3
10 | 3 | 18 | 2
Expected result:
id | item_id | user_id | bid_price
----------------------------------
1 | 1 | 11 | 1
7 | 2 | 17 | 1
8 | 3 | 18 | 2
Offered solution:
select m.id, m.item_id, m.user_id, m.bid_price
from my_table m
inner join (
select item_id, min(id) min_id, min(bid_price) min_price
from my_table
where item_id IN (1,2,3)
group by item_id
) t on t.item_id = m.item_id
and t.min_price= m.bid_price
and t.min_id = m.id
The problem:
In the sub query the minimum ID is selected entire the group by (item_id) statement and doesn't reflects according to minimum bid_price.
In other words, the minimum id is selected not depending on the price field at all. So, in the result I will get minimum price and minimum id of the group, but this will not be the same row! The id can be related to the row with another bet_price value.
How this query can be adjusted? Thank you in advance!
SELECT min(m.id) AS id, m.item_id, m.user_id, m.bid_price
FROM my_table m
INNER JOIN (
SELECT item_id, min(bid_price) AS min_price
FROM my_table
GROUP BY item_id
) t ON t.item_id = m.item_id
AND t.min_price= m.bid_price
GROUP BY item_id
Output
id item_id user_id bid_price
1 1 11 1
7 2 17 1
8 3 18 2
Live Demo
http://sqlfiddle.com/#!9/a52dc6/13
SELECT DISTINCT
t1.item_id,
t1.bid_price
FROM tab1 t1
WHERE NOT exists(SELECT 1
FROM tab1 t2
WHERE t2.item_id = t1.item_id
AND t2.bid_price < t1.bid_price)
AND t1.item_id IN (1, 2, 3);
http://sqlfiddle.com/#!9/615e0a/5

Count started conversations in MYSQL

I have this table:
+--------+------+----------+
| fromId | toId | messages |
+--------+------+----------+
| 10 | 2 | 'text' |
| 10 | 4 | 'text' |
| 4 | 10 | 'text' |
| 5 | 10 | 'text' |
| 5 | 10 | 'text' |
| 10 | 3 | 'text' |
| 6 | 10 | 'text' |
+--------+------+----------+
Every row is a message from fromId to toId.
As you can see I have 5 conversations in which user whose ID is 10 is involved:
10 with 2
10 with 4
10 with 5
10 with 3
10 with 6
How can I count them in a Select query??
I have tried this one:
SELECT * FROM messaggi WHERE fromId = 10 OR toID = 10 GROUP BY (fromId)
but something tells me that I have to try a SELECT from another SELECT, I think it's called a subquery.
The answer is equal to the number of rows returned by this query:
SELECT DISTINCT from_id x FROM my_table WHERE to_id = 10
UNION
SELECT DISTINCT to_id x FROM my_table WHERE from_id = 10;
If I understand 10, 5 is the same as 5, 10 so doesnt matter who start the conversation, so:
SQL DEMO
SELECT COUNT(*)
FROM (SELECT DISTINCT
LEAST(`fromId`, `toId`) AS a,
GREATEST(`fromId`, `toId`) AS b
FROM Table1
WHERE 10 in (`fromId`, `toId`)
) T
So we need to find the total number of distinct conversations in any direction. What we can do is query the results and union them together but switch the columns around for one of the queries. This gives us all results for conversations in any direction. We then can count the distinct results to find out how many conversations there are for an individual.
SELECT COUNT(DISTINCT *) FROM
(SELECT fromId as col1, toId as col2 FROM conversations WHERE fromId = 10
UNION
SELECT toId as col1, fromId as col2 FROM conversations WHERE toId = 10)
I would suggest you to change the way it is constructed. Have one table for threads:
TID
---
1
2
3
4
5
6
7
And now, each thread can have any (right now 2) number of participants. So, let's have participants table.
TID | UID
----+----
1 | 10
1 | 2
2 | 10
2 | 4
3 | 4
3 | 10
4 | 5
4 | 10
5 | 5
5 | 10
6 | 10
6 | 3
7 | 6
7 | 10
Now, if you find the count of the participated ones, in a distinct way:
SELECT DISTINCT(GROUP_CONCAT(`UID` ORDER BY `UID` ASC)) FROM participants GROUP BY `TID`
You get this:
And finally, wrapping it with COUNT gives you 5:
SELECT COUNT(*) FROM (SELECT DISTINCT(GROUP_CONCAT(`UID` ORDER BY `UID` ASC)) FROM `participants` GROUP BY `TID`) AS T

How to select a row before a specific row on MySQL, if the table is ordered by date?

I have a select result like this:
ID | DATE
----------------
10 | 2014-07-23
7 | 2014-07-24
8 | 2014-07-24
9 | 2014-07-24
1 | 2014-07-25
2 | 2014-07-25
6 | 2014-07-25
3 | 2014-07-26
4 | 2014-07-27
5 | 2014-07-28
The result above is ordered by date. Now, I want to select the one previous row before:
2 | 2014-07-25
Which is:
1 | 2014-07-25
In case I don't know the exact ID and the conditional code must be compatible with if I want to select a previous row of:
3 | 2014-07-26
Which is:
6 | 2014-07-25
What condition should I use?
UPDATE
Tried this:
SET #rank=0;
SELECT #rank:=#rank+1 AS rank, t1.*
FROM table t1
Then I got this:
RANK | ID | DATE
----------------
1 | 10 | 2014-07-23
2 | 7 | 2014-07-24
3 | 8 | 2014-07-24
4 | 9 | 2014-07-24
5 | 1 | 2014-07-25
6 | 2 | 2014-07-25
7 | 6 | 2014-07-25
8 | 3 | 2014-07-26
9 | 4 | 2014-07-27
10 | 5 | 2014-07-28
Then I tried this:
SET #rank=0;
SELECT #rank:=#rank+1 AS rank, t1.*
FROM table t1
WHERE rank < 3;
I got this error: Unknown column 'rank' in 'where clause'.
Here's one way...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(ID INT NOT NULL PRIMARY KEY
,DATE DATE NOT NULL
);
INSERT INTO my_table VALUES
(10 ,'2014-07-23'),
(7 ,'2014-07-24'),
(8 ,'2014-07-24'),
(9 ,'2014-07-24'),
(1 ,'2014-07-25'),
(2 ,'2014-07-25'),
(6 ,'2014-07-25'),
(3 ,'2014-07-26'),
(4 ,'2014-07-27'),
(5 ,'2014-07-28');
SELECT a.id
, a.date
, b.id b_id
, b.date b_date
FROM
( SELECT x.*
, COUNT(*) rank
FROM my_table x
JOIN my_table y
ON (y.date < x.date)
OR (y.date = x.date AND y.id <= x.id)
GROUP
BY x.date
, x.id
) a
LEFT
JOIN
( SELECT x.*
, COUNT(*) rank
FROM my_table x
JOIN my_table y
ON (y.date < x.date)
OR (y.date = x.date AND y.id <= x.id)
GROUP
BY x.date
, x.id
) b
ON b.rank = a.rank - 1;
+----+------------+------+------------+
| id | date | b_id | b_date |
+----+------------+------+------------+
| 10 | 2014-07-23 | NULL | NULL |
| 7 | 2014-07-24 | 10 | 2014-07-23 |
| 8 | 2014-07-24 | 7 | 2014-07-24 |
| 9 | 2014-07-24 | 8 | 2014-07-24 |
| 1 | 2014-07-25 | 9 | 2014-07-24 |
| 2 | 2014-07-25 | 1 | 2014-07-25 |
| 6 | 2014-07-25 | 2 | 2014-07-25 |
| 3 | 2014-07-26 | 6 | 2014-07-25 |
| 4 | 2014-07-27 | 3 | 2014-07-26 |
| 5 | 2014-07-28 | 4 | 2014-07-27 |
+----+------------+------+------------+
... but you can also do this (quicker) with variables.
You can add a row id to the select like this
SELECT #rowid:=#rowid+1 as rowid,
t1.* FROM yourdatabase.tablename t1, (SELECT #rowid:=0) as rowids;
Then you can run a simple query to get the lower rowid from the input.
This uses a sub query that joins the table against itself, where on one side it is the date you are checking and matching against smaller dates. It uses MAX to get the highest smaller date.
This is then joined against another sub query that gets the highest ID for each date, which also joins against the table itself to get the other details from that row.
SELECT table.*
FROM table
INNER JOIN
(
SELECT MAX(a.date) AS latest_prev_date
FROM table1 a
INNER JOIN table1 b
ON a.date > b.date
WHERE a.date = '2014-07-26'
) sub0
ON table.date = sub0.latest_prev_date
INNER JOIN
(
SELECT date, MAX(ID) AS latest_prev_id
FROM table1
GROUP BY date
) sub1
ON table.ID = sub1.latest_prev_id
AND sub1.date = sub0.latest_prev_date
if you want to use a user_defined_variable this is a way to do it.
SELECT
tab.id, temp.id, temp.date
FROM
(
SELECT
#A:=#A + 1 AS rank_col, t.date, t.id
FROM
myTable t
CROSS JOIN (SELECT #A:=0) join_table
) AS tab
LEFT JOIN
(
SELECT
#B:=#B + 1 AS rank_col, t2 . *
FROM myTable t2
CROSS JOIN (SELECT #B:=0) join_table1
) temp ON temp.rank_col = tab.rank_col - 1;
DEMO

Select most popular users given by likes, posts and comments

I have a forum and I would like to find the most popular users. The most popular users definded by the most likes on posts and comments and also defined by the most total posts and comments. A user with the most likes (order 1), most posts (order 2) and most comments (order 3) is the most popular. Same logic applies for the next (second) most popular user.
So I have 3 tables:
posts table
id user_id likes
1 1 0
2 1 0
3 1 0
4 1 0
5 1 0
6 1 1
7 1 0
8 2 0
9 2 2
10 2 0
11 2 0
12 3 0
13 3 0
14 4 0
15 4 10
comments table
id user_id likes
1 1 0
2 1 1
3 1 1
4 1 0
5 2 0
6 2 2
7 2 1
8 4 1
9 4 0
users table
id name
1 John
2 Adam
3 Maggie
4 Steve
The likes column contains the likes given by other users on the respective post (row).
I tried:
SELECT DISTINCT c.id, c.name,
SUM(a.likes), SUM(b.likes), (SUM(a.likes) + SUM(b.likes)) as popular,
COUNT(a.id) as mostp, COUNT(b.id) as mostc
FROM posts as a, comments as b, users as c
WHERE a.user_id=b.user_id AND a.user_id=c.id AND b.user_id=c.id
GROUP BY a.user_id, b.user_id ORDER BY popular DESC, mostp DESC, mostc DESC
Obviously, this does not work, because if you test the query it gives more likes(sum) than expected.
Here is the live query
http://sqlfiddle.com/#!2/08900/3
The problem with your query is with users that have more than one post and more than one comment, resulting in a cartesian product and producing the wrong sums.
The query below (example on SQL Fiddle) should work, since the subqueries already group by user_id:
SELECT
u.name,
COALESCE(p.likes,0) + COALESCE(c.likes,0) AS likes,
COALESCE(p.cnt,0) AS post_count,
COALESCE(c.cnt,0) AS comment_count
FROM users u
LEFT JOIN (
SELECT user_id, COUNT(1) AS cnt, SUM(likes) AS likes
FROM posts
GROUP BY user_id
) p ON ( p.user_id = u.id )
LEFT JOIN (
SELECT user_id, COUNT(1) AS cnt, SUM(likes) AS likes
FROM comments
GROUP BY user_id
) c ON ( c.user_id = u.id )
ORDER BY likes DESC, post_count DESC, comment_count DESC;
Result:
| NAME | LIKES | POST_COUNT | COMMENT_COUNT |
-----------------------------------------------
| Steve | 11 | 2 | 2 |
| Adam | 5 | 4 | 3 |
| John | 3 | 7 | 4 |
| Maggie | 0 | 2 | 0 |
Here is how you can do this
SELECT
u.id,
u.name,
(l.likes + r.likes) As TotalLikes,
IFNULL(posts,0) AS TotalPosts,
IFNULL(comments,0) AS TotalComments
FROM users AS u
LEFT JOIN (SELECT
user_id,
IFNULL(SUM(likes),0) as likes,
COUNT(likes) as posts
FROM posts
GROUP BY user_id) AS l
on l.user_id = u.id
LEFT JOIN (SELECT
user_id,
IFNULL(SUM(likes),0) as likes,
COUNT(likes) AS comments
FROM comments
GROUP BY user_id) AS r
on r.user_id = u.id
ORDER BY TotalLikes DESc
SQL Fiddle Demo
Output
| ID | NAME | TOTALLIKES | TOTALPOSTS | TOTALCOMMENTS |
---------------------------------------------------------
| 4 | Steve | 11 | 2 | 2 |
| 2 | Adam | 5 | 4 | 3 |
| 1 | John | 3 | 7 | 4 |
| 3 | Maggie | 0 | 2 | 0 |