How to Get number of post done by user - mysql

Here is my question:
I have 4 tables which hold post done by user(say CatA,CatB,CatC and CatD, each table hold created_by column). My requirement is to get all the user available from table USER with post count (sum of post).
I am struggling to find answer from past 2 days and i'm still clueless.
Any idea is much appreciated.
SELECT u.username,
a.cnt + b.cnt + c.cnt + d.cnt AS total_posts
FROM users u
LEFT JOIN (SELECT created_by, COUNT(*) AS cnt FROM CatA GROUP BY created_by) a
ON u.id = a.created_by
LEFT JOIN (SELECT created_by, COUNT(*) AS cnt FROM CatB GROUP BY created_by) b
ON u.id = b.created_by
LEFT JOIN (SELECT created_by, COUNT(*) AS cnt FROM CatC GROUP BY created_by) c
ON u.id = c.created_by
LEFT JOIN (SELECT created_by, COUNT(*) AS cnt FROM CatD GROUP BY created_by) d
ON u.id = d.created_by
ORDER BY total_posts DESC
This return me total post count on top (irrespective of user post count) like below instead of post count for each user
username | user1 | user2 | user3
total_posts | 11020 (Total post count) | NULL | NULL | and so on
More Info:
SELECT created_by, COUNT(*) AS cnt FROM CatA GROUP BY created_by
This returns me:
created_by | 22 | 26 | 88 | 90
cnt | 6 | 20 | 15 | 8

So it seems like you have a separate table for each category (which is actually not the most optimal design by the way). What you can do to get the count of posts for all users is:
SELECT u.*,
a.cnt + b.cnt + c.cnt + d.cnt AS total_posts
FROM users u
LEFT JOIN (SELECT user_id, COUNT(*) AS cnt FROM CatA GROUP BY user_id) a
ON u.user_id = a.user_id
LEFT JOIN (SELECT user_id, COUNT(*) AS cnt FROM CatB GROUP BY user_id) b
ON u.user_id = b.user_id
LEFT JOIN (SELECT user_id, COUNT(*) AS cnt FROM CatC GROUP BY user_id) c
ON u.user_id = c.user_id
LEFT JOIN (SELECT user_id, COUNT(*) AS cnt FROM CatD GROUP BY user_id) d
ON u.user_id = d.user_id

Related

How to Select max date in this query?

I would like to write a query which retrieves name, id, and last modified date for each User. The below query gives the name, id, and last modified date from tables UserDetails1 and UserDetails2.
How could I modify this query to return a single date value, the max date for a given user_id in either of the details tables?
SELECT
id,
name,
MAX(userdetails1.date_modified),
MAX(userdetails2.date_modified)
FROM User user
INNER JOIN UserDetails1 userdetails1
ON userdetails1.user_id = user.id
INNER JOIN UserDetails2 userdetails2
ON userdetails2.user_id = user.id
User
id | name
---------
1 | name1
2 | name2
3 | name3
UserDetails1
user_id | date_modified
---------------------
1 | 2016-11-28 16:28:26
....
UserDetails2
user_id | date_modified
---------------------
1 | 2016-11-29 16:29:26
....
Try this, although I think there can be a more optimized way to write it.
SELECT
id,
name,
(CASE
WHEN MAX(userdetails1.date_modified) > MAX(userdetails2.date_modified)
THEN MAX(userdetails1.date_modified)
ELSE MAX(userdetails2.date_modified)
END)
FROM User user
INNER JOIN UserDetails1 userdetails1
ON userdetails1.user_id = user.id
INNER JOIN UserDetails2 userdetails2
ON userdetails2.user_id = user.id
GROUP BY id, name
One option is to UNION your two date tables together. This can be done before or after you JOIN. I personally would UNION before JOINING as it is simpler in my mind and to write.
Please excuse the SQL Server-esque syntax.
Before JOINing:
SELECT
u.id,
u.name,
MAX(d.date_modified) last_modified
FROM [User] u
INNER JOIN
(
SELECT user_id, date_modified
FROM UserDetails1
UNION ALL
SELECT user_id, date_modified
FROM UserDetails2
) d
ON u.id = d.user_id
GROUP BY u.id, u.name
After JOINing:
SELECT
id,
name,
max(date_modified) last_modified
FROM
(
SELECT
u.id, u.name, d.date_modified
FROM [User] u
INNER JOIN UserDetails1 d
ON d.user_id = user.id
UNION ALL
SELECT
u.id, u.name, d.date_modified
FROM [User] u
INNER JOIN UserDetails2 d
ON d.user_id = u.id
)
GROUP BY id, name

Complex Joins (Joining Joins)

I have 4 tables: booking, address, search_address & search_address_log
Tables: (relevant cols)
booking: (pickup_address_id, dropoff_address_id)
address: (address_id, postcode)
search_address: (address_id, postcode)
search_address_log: (id, from_id, to_id)
What I need to do is have a count from both booking and search_address_log
grouped by the pickup/dropoff & from/to postcodes.
I can do this individually for each i.e.:
booking:
SELECT
a1.postcode b_From,
a2.postcode b_to,
COUNT(*) b_count
FROM booking b
INNER JOIN address a1 ON b.pickup_address_id = a1.address_id
INNER JOIN address a2 ON b.destination_address_id = a2.address_id
GROUP BY b_From, b_To
ORDER BY COUNT(*) DESC
LIMIT 10
search_address_log:
SELECT
sa1.postcode s_From,
sa2.postcode s_To,
COUNT(*) s_count
FROM search_address_log sal
INNER JOIN search_address sa1 ON sal.from_id=sa1.address_id
INNER JOIN search_address sa2 ON sal.to_id=sa2.address_id
GROUP BY s_From, s_To
ORDER BY COUNT(*) DESC
LIMIT 10
Returning tables like:
| b_To b_From b_count || s_To s_From s_count |
| x y 10 || x y 50 |
| a b 5 || a b 60 |
WHAT I NEED:
| To From b_count s_count |
| x y 10 50 |
| a b 5 60 |
Thanks,
George
Technically, what you want is a full outer join, but MySQL doesn't support that. However, the following should do what you want -- getting summaries for each from and to value for the two columns:
SELECT b_from, b_to, sum(b_count) as b_count, sum(s_count) as s_count
FROM ((SELECT a1.postcode as b_From, a2.postcode as b_to, COUNT(*) as b_count, 0 as s_count
FROM booking b INNER JOIN
address a1
ON b.pickup_address_id = a1.address_id INNER JOIN
address a2
ON b.destination_address_id = a2.address_id
GROUP BY b_From, b_To
) UNION ALL
(SELECT sa1.postcode as s_From, sa2.postcode as s_To, 0, COUNT(*) as s_count
FROM search_address_log sal INNER JOIN
search_address sa1
ON sal.from_id = sa1.address_id INNER JOIN
search_address sa2
ON sal.to_id = sa2.address_id
GROUP BY b_From, b_To
)
) ft
GROUP BY s_From, s_to;
Proposal: select on the addresses -- get counts of the to/from pairs, and then add them up by postcode
SELECT t.postcode, f.postcode, SUM(sal.count), SUM(b.count)
FROM search_address t, search_address f
LEFT JOIN ( SELECT from_id, to_id, COUNT(*) count
FROM search_address_log GROUP BY from_id, to_id ) sal
ON sal.from_id=f.address_id AND sal.to_id=t.address_id
LEFT JOIN ( SELECT pickup_address from_id, destination_address_id to_id, COUNT(*) count
FROM booking GROUP BY from_id, to_id) b
ON b.from_id=f.address_id AND b.to_id=t.address_id
WHERE sal.count > 0 OR b.count > 0
GROUP BY t.postcode, f.postcode;
This will scale based on number of addresses squared, which may end up worse than the "generate independent summaries and then union them" scheme outlined in another answer. It's a bit more concise, however.
If every booking has a pickup_address and a destination_address and every search_address_log from_id an to_id has a search address and also that the codes in booking are the same as those in the search address log then you can do a join as in
select t1.b_from, t1.b_to, t1.b_count, t2.s_count from
(SELECT
a1.postcode b_From,
a2.postcode b_to,
COUNT(*) b_count
FROM booking b
INNER JOIN address a1 ON b.pickup_address_id = a1.address_id
INNER JOIN address a2 ON b.destination_address_id = a2.address_id
GROUP BY b_From, b_To) t1
inner join
(SELECT
sa1.postcode s_From,
sa2.postcode s_To,
COUNT(*) s_count
FROM search_address_log sal
INNER JOIN search_address sa1 ON sal.from_id=sa1.address_id
INNER JOIN search_address sa2 ON sal.to_id=sa2.address_id
GROUP BY s_From, s_To) t2 on (t1.b_from = t2.s_from and t1.b_to = t2.s_to)

How to mention two aggregation functions in the same query?

I have an sql query to get information from three different tables as following:
select users.username, users.id, users.avatar, users.daily_tahmin, users.alert, f1.comments_no, f2.tahmins_no, f3.monthly_tahmins_no from users LEFT join
(SELECT count(comments) AS comments_no, user_id
FROM comments
Where user_id = 12
) AS f1 on users.id = f1.user_id left join
(
SELECT count(tahmin) AS tahmins_no, user_id
FROM tahminler
Where user_id = 12
) AS f2 on users.id = f2.user_id left join
(
SELECT count(tahmin) AS monthly_tahmins_no, user_id, matches_of_comments.match_id
FROM tahminler
INNER JOIN matches_of_comments on tahminler.match_id = matches_of_comments.match_id
Where user_id = 12 AND (MONTH( STR_TO_DATE( matches_of_comments.match_date, '%d.%m.%Y' ) ) = '01' AND YEAR( STR_TO_DATE( matches_of_comments.match_date, '%d.%m.%Y' ) ) = '2014')
) AS f3 on users.id = f3.user_id
where users.id = 12
and it gives the following result :
+------------+----+----------------+--------------+-------+-------------+------------+--------------------+
| username | id | avatar | daily_tahmin | alert | comments_no | tahmins_no | monthly_tahmins_no |
+------------+----+----------------+--------------+-------+-------------+------------+--------------------+
| cold heart | 12 | 1389002263.jpg | 0 | 0 | 65 | 258 | 10 |
+------------+----+----------------+--------------+-------+-------------+------------+--------------------+
The previous code was not optimized after i do some EXPLIAN and I tried to optimized it and I got the following query:
SELECT m.*,count(comments.id)
FROM comments
JOIN
(SELECT users.username, users.id, users.avatar, users.daily_tahmin, users.alert
FROM users
WHERE id=12)as m ON m.id = comments.user_id
My problem is that I can not get (tahmins_no,monthly_tahmins_no) every time i add them to the query it gives wrong result I can not find a way to add them correctly to the query to be in optimized way?? can I have any advice from anybody here?
Your simplified query is:
select m.*, count(c.id)
from comments c join
users m
on m.id = c.user_id
where m.id = 12
group by m.id;
You should be able to add in the monthly number:
select m.*, count(c.id), f3.*
from comments c join
users m
on m.id = c.user_id join
(select count(tahmin) AS monthly_tahmins_no, user_id, moc.match_id
from tahminler t join
matches_of_comments moc
on t.match_id = moc.match_id
Where user_id = 12 AND
MONTH( STR_TO_DATE( moc.match_date, '%d.%m.%Y' ) ) = 1 AND
YEAR( STR_TO_DATE( moc.match_date, '%d.%m.%Y' ) ) = 2014
) f3
on f3.user_id = m.id
where m.id = 12
group by m.id;
The month() and year() function return numbers, not strings. I don't understand why the field match_date would be stored as a string -- seems like a silly choice for a column whose name contains date.

how to write the sql statement?

I have 3 tables as follows in my database. this is used to a application just like foursqure. i need help with the problem of writing the sql statement i have asked in the bottom of this.
thank you
user_details
user_id | fname
----------------
1 | Losh
8 | Dush
9 | Rosh
10 | NELL
friends
user_idf |user_idff
----------------
1 | 8
8 | 9
10 | 1
Check_in
check_in_id |user_id | place | date
--------------------------------------------
1 | 8 | Hotel | 01/01/2012
2 | 9 | Home | 05/01/2012
3 | 1 | Junction | 08/01/2012
4 | 1 | Rest | 11/01/2012
5 | 9 | Hotel | 15/01/2012
6 | 8 | Home | 15/01/2012
i get the user's who are friends with 8 and user 8 details AND the check in places as follows
SELECT a.`user_id`, a.`fname` , b.*
FROM `user_details` a, `check_in` b
WHERE (b.user_id = 8
OR b.user_id in (select user_idf from friends where user_idff = '8' union select user_idff from friends where user_idf = '8')) AND b.user_id = a.user_id
how do i write the sql to select who are friends with 8 and user 8 details AND the last check in place of those users
explanation::
i seeks for a answer such as
user id name place date
1 LOSH Rest 11/01/2012
8 DUSH HOME 15/01/2012
9 ROSH HOTEL 15/01/2012
Join it to the table returned by:
(SELECT `user_id`, `place` FROM Check_in GROUP BY user_id ORDER BY `date` DESC)
That should give you one entry per user, and since it's sorted in reverse by date, that entry should be the most recent.
But when i group by it gives me the first dates not the latest date
How about this:
(SELECT user_id, place
FROM (SELECT * FROM Check_in ORDER BY `date` DESC) tmp
GROUP BY user_id)
SELECT user_id, fname, c.place
FROM user_details u
INNER JOIN (SELECT IF(user_idff = 8, user_idf, user_idff) AS user_id
FROM friends
WHERE (user_idff = 8 OR user_idf = 8)
) f
ON u.user_id = f.user_id
LEFT JOIN (SELECT c1.user_id, place
FROM Check_in c1
LEFT JOIN Check_in c2
ON c1.user_id = c2.user_id AND
c1.date < c2.date
WHERE c2.date IS NULL
) c
ON u.user_id = c.user_id;
This doesn't break ties but it's a straighforward way of answering your question.
EDIT
I just re-read you question and I see that you want user 8 info also. It's not clear whether you want user 8 as a separate row or with info in line with the friends' rows.
select *
from
friends as f inner join check_in as ci on ci.user_id = f.user_idff
inner join user_details as ud on ud.user_id = f.user_idff
inner join user_details as ud8 on ud8.user_id = f.user_idf
where
f.user_idf = 8
and date = (
select max(date)
from friends as f2 inner join check_in as ci on ci.user_id = f2.user_idff
where f2.user_idf = f.user_idf
)
EDIT 2
You request may be a small bit unclear about determining which check-in location to return. Use this option if you want the latest location of each friend individually. The first query finds the most recent location among all friends. Obviously these are two variations on an identical theme.
select *
from
friends as f inner join check_in as ci on ci.user_id = f.user_idff
inner join user_details as ud on ud.user_id = f.user_idff
inner join user_details as ud8 on ud8.user_id = f.user_idf
where
f.user_idf = 8
and date = (
select max(date)
from check_in as ci
where ci.user_id = f.user_idff
)
(SELECT a.user_id, a.place, b.fname, a.date, a.time, a.check_in_id
FROM (SELECT * FROM check_in ORDER BY date DESC) as a, user_details as b
WHERE a.user_id = b.user_id AND (a.user_id in (select user_idf from friends where user_idff = '8' union select user_idff from friends where user_idf = '8') OR a.user_id = 8)
GROUP BY a.user_id)
above query gave me the required answer.
thank you all for the help given

Followers of a user and ids of last 2 comments liked by each follower(if any), also the count of follower's followers, using mysql

I have social application let's say like twitter, where user can follow other users and can likes some comments.
What I need to fetch is followers of a user and ids of last 2 comments liked by each follower(if any), also the count of follower's followers, using MySQL.
Here are the tables
Table user_follower
User_id follower_id
1 2
2 3
1 5
1 6
1 7
Table user_likes
comment_id User_id date
41 2 some_date
42 2 some_date
41 5 some_date
42 5 some_date
43 5 some_date
43 2 some_date
43 6 some_date
how can we do this in a single mysql query?
so far i am able to get the followers and count of follower's follower and following both.
select uf.follower_id,
(select count(*) from user_followers uf1 where uf1.follower_id = uf.follower_id) as following_count,
(select count(*) from user_followers uf2 where uf2.user_id = uf.follower_id) as follower_count,
from user_followers uf
join users u on u.id = uf.follower_id
where uf.user_id = 1
what i want is now to get the 2 latest comment_ids for each follower, i.e. uf.follower_id here.
if not possible in the same query,
i am fine even with another query as will passing the follower_ids as in parameter, but it should give me 2 latest comment for each passed id..
I think this will work. I have not tested it so it may have some syntax errors. Given the level of nesting used in this query I suspect it will perform badly with a very large dataset.
SELECT follower_id, num_followers, GROUP_CONCAT(comment_id)
FROM (
SELECT t.*,
#r := IF(#g = t.follower_id, #r+1, 1) RowNum,
#g := t.follower_id
FROM (select #g:=null) AS initvars
INNER JOIN (
SELECT followers.*, ul.comment_id
FROM (
SELECT
uf1.user_id,
uf1.follower_id,
COUNT(uf2.follower_id) AS num_followers
FROM user_follower uf1
LEFT JOIN user_follower uf2
ON uf1.follower_id = uf2.user_id
WHERE uf1.user_id = 1
GROUP BY uf1.user_id, uf1.follower_id
) AS followers
LEFT JOIN user_likes ul
ON followers.follower_id = ul.user_id
ORDER BY followers.follower_id ASC, comment_id DESC
) AS t
) AS final
WHERE RowNum < 3
GROUP BY follower_id, num_followers;
UPDATE Here is the other query using the inequality join -
SELECT tmp.follower_id, COUNT(uf2.follower_id) AS num_followers, tmp.comments
FROM (
SELECT follower_id, GROUP_CONCAT(comment_id ORDER BY comment_id DESC) AS comments
FROM (
SELECT uf.follower_id, ul1.*
FROM user_follower uf
LEFT JOIN user_likes ul1
ON uf.follower_id = ul1.user_id
LEFT JOIN user_likes ul2
ON uf.follower_id = ul2.user_id
AND ul1.comment_id < ul2.comment_id
WHERE uf.user_id = 1
GROUP BY ul1.user_id, ul1.comment_id
HAVING COUNT(ul2.comment_id) < 2
) AS tmp
GROUP BY tmp.follower_id
) AS tmp
LEFT JOIN user_follower uf2
ON tmp.follower_id = uf2.user_id
GROUP BY tmp.follower_id