How to mention two aggregation functions in the same query? - mysql

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.

Related

Split table row into two fields and count each

So far I have wrote the following query:
SELECT forum_topics.*, users.id as userid, users.username, users.avatar, forum_categories.name as cat_name
FROM forum_topics
INNER JOIN users
ON users.id = forum_topics.author_id
INNER JOIN forum_categories
ON forum_categories.id = forum_topics.category_id
WHERE forum_topics.id = 64
But I also want to add another table votes that has the following structure:
___________________________________________________________
| id | object_type | object_id | receiver | giver | type |
___________________________________________________________
| 128| topic | 64 | 21 | 22 | like |
| 129| topic_reply | 55 | 21 | 22 | dislike |
___________________________________________________________
Basically the relation between the two tables is forum_topics.id from Table 1 and object_id from Table 2 (the bottom one). This is for a forum and I want to display likes/dislikes for each topic and reply. type could be like and dislike. receiver is the user that made the post, giver is the user that voted. I want to INNER JOIN the votes table in the first query and count all likes and dislikes into two separate fields. Something like:
Select votes.count(*) as likes WHERE type = 'like and votes.count(*) as dislikes WHERE type = 'dislike'
The query got so complicated and I am so confused.
Edit: So I figured it out for forum_topics. Here is how I did it:
SELECT forum_topics.*, users.id as userid, users.username, users.avatar, forum_categories.name as cat_name,
count(CASE WHEN votes.type = 'like' AND votes.object_type = 'topic' then 1 else null end) as votes_likes,
count(CASE WHEN votes.type = 'dislike' AND votes.object_type = 'topic' then 1 else null end) as votes_dislikes
FROM forum_topics
INNER JOIN users
ON users.id = forum_topics.author_id
INNER JOIN forum_categories
ON forum_categories.id = forum_topics.category_id
INNER JOIN votes
ON votes.object_id = forum_topics.id
WHERE forum_topics.id = ?
Now for forum_posts It is not working..
SELECT forum_posts.*, users.id as userid, users.username, users.avatar,
count(CASE WHEN votes.type = 'like' AND votes.object_type = 'topic_post' then 1 else null end) as votes_likes,
count(CASE WHEN votes.type = 'dislike' AND votes.object_type = 'topic_post' then 1 else null end) as votes_dislikes
FROM forum_posts
INNER JOIN users
ON users.id = forum_posts.author_id
LEFT JOIN votes
ON votes.object_id = forum_posts.id
WHERE forum_posts.topic_id = 64
ORDER BY forum_posts.id
Any ideas how to fix it? In HeidiSQL it returns one row with everything NULL.
You need a GROUP BY
SELECT forum_posts.id
, forum_posts.author_id
, forum_posts.editor_id
, forum_posts.topic_id
, forum_posts.content
, forum_posts.date_created
, forum_posts.updated
, users.id as userid
, users.username
, users.avatar
, count(CASE WHEN votes.type = 'like' AND votes.object_type = 'topic_post' THEN 1 ELSE NULL END) AS votes_likes
, count(CASE WHEN votes.type = 'dislike' AND votes.object_type = 'topic_post' THEN 1 ELSE NULL END) AS votes_dislikes
FROM forum_posts
INNER JOIN users ON users.id = forum_posts.author_id
LEFT JOIN votes ON votes.object_id = forum_posts.id
WHERE forum_posts.topic_id = 64
GROUP BY forum_posts.id
, forum_posts.author_id
, forum_posts.editor_id
, forum_posts.topic_id
, forum_posts.content
, forum_posts.date_created
, forum_posts.updated
, users.id
, users.username
, users.avatar
try using group by;
SELECT type, COUNT(*)
FROM votes
GROUP BY type;

Query: Select and display next highest value as new column

I have this query which lists out IDs from "pages" on our site.
SELECT mdl_page.id
FROM mdl_page, mdl_log, mdl_user
WHERE mdl_log.module = "page"
AND mdl_log.action = "view"
AND mdl_user.id = mdl_log.userid
AND mdl_log.info = mdl_page.id
AND mdl_log.course = 178
The result is simple:
| ID |
|-----|
| 3 |
| 4 |
| 7 |
| 11 |
Notice the jumps in the count. I'm trying to get something like this:
| ID | NEXT ID |
|-----|---------|
| 3 | 4 |
| 4 | 7 |
| 7 | 11 |
| 11 | 12 |
Can anyone point in me in the right direction for this?
UPDATE
One twist, the system (not my own) I have to run the query through only allows queries that begin with 'SELECT'.
Two ways i can think of use a co-related subquery,in your sub query compare the value from main query and sorts it in ascending manner and limit the result to one
SELECT
p.id ,
(SELECT
p1.id
FROM mdl_page p1
JOIN mdl_log l1 ON (l1.info = p1.id)
JOIN mdl_user u1 ON (u1.id = l1.userid)
WHERE l1.module = "page"
AND l1.action = "view"
AND l1.course = 178
AND p1.id > p.id
ORDER BY p1.id ASC LIMIT 1) NEXT_ID
FROM mdl_page p
JOIN mdl_log l ON (l.info = p.id)
JOIN mdl_user u ON (u.id = l.userid)
WHERE l.module = "page" AND l.action = "view" AND l.course = 178
ORDER BY p.id
and use a rank query, in rank query i am left joining the same query with the less than condition ON (t.id< t1.id) so it will result in multiple rows like (3,4),(3,7),(3,11) so i need to pick the first combination of 3,4 for this i have used a rank query to give the rank to the items that belong to same group, in parent where i am just restricting the result set to show the first pair for each group
SELECT t3.id,t3.NEXT_ID FROM (
SELECT t.id id, t1.id NEXT_ID ,
#r:= CASE WHEN #g = t.id THEN #r +1 ELSE 1 END rownum,
#g:= t.id
FROM
(SELECT
p.id
FROM
mdl_page p
JOIN mdl_log l ON (l.info = p.id)
JOIN mdl_user u ON (u.id = l.userid)
WHERE l.module = "page"
AND l.action = "view"
AND l.course = 178
ORDER BY p.id
) t
LEFT JOIN
(SELECT
p.id
FROM
mdl_page p
JOIN mdl_log l ON (l.info = p.id)
JOIN mdl_user u ON (u.id = l.userid)
WHERE l.module = "page"
AND l.action = "view"
AND l.course = 178
ORDER BY p.id ) t1 ON (t.`id` < t1.id)
CROSS JOIN (SELECT #g:=0,#r:=0) t2
ORDER BY t.`ID` , t1.ID
) t3
WHERE t3.rownum = 1
resutset you will get as null for 11 if there is no more record exist which have an id greater than 11 ,or in other words the last record will have a null in next_id column
ID NEXT_ID
3 4
4 7
7 11
11 NULL
Perhaps you should create a temporary table, which is pretty much the same as the query you're running and erase the first line?
Then run your query and join it with the temporary table?

using a limit on one table when joining two tables in mysql

How do I correctly write an sql statement using a limit and join in this case?
SELECT u.userName, u.userFriends, u.userJoined, f.freindName (only 3)
FROM user u inner join friends f ON u.userId = f.addedUserId
WHERE u.userId=1
AND f.userId=1
I tried
SELECT u.userName, u.userFriends, u.userJoined, group_concat(f.freindName)
FROM user u inner join friends f ON u.userId = f.userId
WHERE u.userId = 1 and f.addedUserId = 1
GROUP by u.userName limit 5 /* but this does not work */
Can you help with the limiting the f.freindName to 3
'gid' | 'userName' | 'userId' | 'userFriends' |'userJoined'
-------|-------------|----------|-------------------|--------------
'1' | 'Jason' | '1' | '5' |'14-Aug-2014'
'gid' | 'friendName'| 'thisUserId' |'addedUserId'
-------|-------------|------------------|-----------------
'1' | 'James' | '2' |'1'
'2' | 'Lars' | '3' |'1'
'3' | 'Kirk' | '4' |'1'
'4' | 'Rob' | '5' |'1'
'5' | 'Dave' | '5' |'1'
I'm assuming that the tables users and friends are joined by the columns userId and addedUserId. And I assume further that you want a list of three friends of the user with the userId = 1.
You can use a derived table by using a subselect to get the desired result:
SELECT u.userName, u.userFriends, u.userJoined, group_concat(f.friendName)
FROM user u
INNER JOIN (
SELECT
userId,
friendName
FROM
friends
LIMIT 3
) f
ON u.userId = f.addedUserId
WHERE u.userId = 1
GROUP by u.userName
should work.
This is the query you are looking for. I think there is some problems with the field names (for instance f.freindName instead of f.friendname) but fixing that, This should work
SELECT u.userName, u.userFriends, u.userJoined, f.freindName
FROM user u inner join friends f ON u.userId = f.userId
(SELECT DISTINCT f.freindName
FROM user u2 inner join friends f2 ON u2.userId = f2.userId
WHERE u2.userId = u.userId
LIMIT 3) as f
WHERE u.userId=1

How to Get number of post done by user

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

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