MySQL syntax issue, multiple SELECT statements - mysql

I am working of a project that has 2 tables as follows: users_fb and posts
I spent 3 hours playing with the code and then I gave up.
table: posts
+-----+---------+---------+---------+---------+-----------+
| id | by_user | by_page | votes | status | time |
+-----+---------+---------+---------+---------+-----------+
| 1 | 1 | 0 | 20 | 1 | 372041014 |
+-----+---------+---------+---------+---------+-----------+
table: users_fb
+-----+-----------+-------+---------+--------+-------+
| id | username | name | gender | fb_id | email |
+-----+-----------+-------+---------+--------+-------+
SELECT username,
(
SELECT COUNT(b.by_user)
FROM users_fb a LEFT JOIN posts b ON a.id = b.by_user
WHERE b.by_page = '0'
GROUP BY a.username
) AS totalCount ,
(
SELECT IFNULL(SUM(b.votes),0)
FROM users_fb a LEFT JOIN posts b ON a.id = b.by_user
GROUP BY users_fb.id
) AS total_votes
FROM users_fb ORDER BY total_votes DESC
The desired output
+-------------------+-------------+-------------+
| username | totalCount | total_votes |
+-------------------+-------------+-------------+
| user4 | 1 | 25 |
| user1 | 0 | 0 |
| user2 | 0 | 0 |
| user3 | 0 | 0 |
+-------------------+-------------+-------------+
UNFORTUNATELY: This is what I am getting
+-------------------+-------------+-------------+
| username | totalCount | total_votes |
+-------------------+-------------+-------------+
| user4 | 1 | 25 |
| user1 | 1 | 25 |
| user2 | 1 | 25 |
| user3 | 1 | 25 |
+-------------------+-------------+-------------+
If you need any further information, let me know. Thanks for your help.

You don't appear to have anything to join your tables together to match up with posts / votes go with which user.
Something like this should do it
SELECT users_fb.username, Sub1.postcount, Sub2.votecount
FROM users_fb
LEFT OUTER JOIN(
SELECT a.username, COUNT(*) AS postcount
FROM users_fb a
INNER JOIN posts b
ON a.id = b.by_user
WHERE b.by_page = '0'
GROUP BY a.username
) Sub1
ON users_fb.username = Sub1.username
LEFT OUTER JOIN(
SELECT users_fb.id, IFNULL(SUM(b.votes),0) AS votecount
FROM users_fb a
LEFT JOIN posts b
ON a.id = b.by_user
GROUP BY users_fb.id
) Sub2
ON users_fb.id = Sub2.id
Possibly simplified to
SELECT a.username, SUM(IF(b.by_page = '0', 1, 0)) AS postcount, IFNULL(SUM(b.votes),0) AS votecount
FROM users_fb a
LEFT JOIN posts b
ON a.id = b.by_user
GROUP BY a.username

Since you are doing no matching of the selects (i.e. there is no binding WHERE between them), MySQL has no wait to make them show together.
You should do something like this:
SELECT username, totalCount.count, total_votesGROUPED.sum
FROM users_fb
LEFT JOIN (
SELECT COUNT(b.by_user) as count, a.username
FROM users_fb a LEFT JOIN posts b ON a.id = b.by_user
WHERE b.by_page = '0'
GROUP BY a.username
) AS totalCount ON totalCount.username = users_fb.username,
(
SELECT IFNULL(SUM(b.votes),0) as sum, id
FROM users_fb a LEFT JOIN posts b ON a.id = b.by_user
GROUP BY users_fb.id
) AS total_votesGROUPED ON total_votesGROUPED.id = users_fb.id
ORDER BY total_votes DESC
If I had a bit more information, I could test it

Quite a few problems, the biggest is that you don't make any join between your "main query" and your "subqueries".
So finally, something like that should be better.
SELECT
a.username,
SUM (CASE WHEN b.by_page IS NOT NULL and b.by_page = '0' then 1 else 0 end) as cnt,
SUM (IFNULL(b.votes),0) as nbVotes,
FROM users_fb a
LEFT JOIN posts b ON a.id = b.by_user
GROUP BY users_fb.id

SELECT a.username,
COUNT(b.by_user) totalCount,
SUM(IFNULL(b.votes,0)) total_votes
FROM users_fb a
LEFT JOIN posts b
ON a.id = b.by_user
WHERE b.by_page = '0'
GROUP BY a.id,a.username

SELECT username, IFNULL(COUNT(b.by_user), 0) totalCount, IFNULL(SUM(c.votes), 0) total_votes
FROM users_fb a
LEFT JOIN posts b
ON a.id = b.by_user AND b.by_page = 0
LEFT JOIN posts c
ON a.id = b.by_user
GROUP BY username
SQLFIDDLE

Related

SQL get aggregate breakdown by joined table

I have the following entities and relationships in a MySQL database:
Each Post has N Review
Each Review has 1 Comment and a state (is_accepted)
Each Comment has 1 User
Desired result:
I'm trying to get an aggregate report of reviews on a specific post, grouped by user:
+---------+--------------+-----------------------+---------------------+
| user_id | review_count | review_accepted_count | review_denied_count |
+---------+--------------+-----------------------+---------------------+
| 1 | 3 | 2 | 1 |
| 2 | 5 | 1 | 4 |
| 3 | 1 | 1 | 0 |
+---------+--------------+-----------------------+---------------------+
What I've tried:
SELECT
C.user_id,
COUNT(C.user_id) AS review_count,
(SELECT COUNT(*) FROM reviews WHERE `post_id` = R.post_id AND `user_id` = C.user_id AND `is_accepted` = 1) review_accepted_count,
(SELECT COUNT(*) FROM reviews WHERE `post_id` = R.post_id AND `user_id` = C.user_id AND `is_accepted` = 0) review_denied_count
FROM reviews R
INNER JOIN comments C ON C.id = R.comment_id
WHERE post_id = 1234
GROUP BY C.user_id
Actual result:
The returned review_accepted_count and review_denied_count columns are the total across all reviews, not grouped per user
Try this:
SELECT
C.user_id,
COUNT(C.user_id) AS review_count,
SUM(CASE WHEN `is_accepted` = 1 THEN 1 ELSE 0 END) AS review_accepted_count,
SUM(CASE WHEN `is_accepted` = 0 THEN 1 ELSE 0 END) AS review_denied_count
FROM reviews R
INNER JOIN comments C ON C.id = R.comment_id
WHERE post_id = 1234
GROUP BY C.user_id
In your subqueries review_accepted_count and review_denied_count you should join (in WHERE clause) by the review primary key. You don't need to make subqueries to get the result. This way is faster.
If you only have 1s and 0s in column is_accepted you can do:
SUM(`is_accepted`) AS review_accepted_count

SUM two columns of two different tables in one result based group by id of another table

I have 3 tables:
Users:
id | account_name
-------------------|----------------------|
18 | panic |
Deposits:
id | user_id | amount
-------------------|---------------------------|
1 | 18 | 100
2 | 18 | 100
Withdrawals:
id | user_id | amount
------------------------|--------------------------------|
1 | 18 | 200
2 | 18 | 200
and i'm trying to get a result like:
id | totalDeposits | totalWithdraws
------------------------|---------------------------|
18 | 200 | 400
Now when i try to get the totals for some reason they are cross adding themselves up, of course if there are no rows it should return 0.
SELECT t0.id,IFNULL(SUM(t1.amount),0) AS totalWithdrawals,
IFNULL(SUM(t2.amount),0) AS totalDeposits
FROM users t0
LEFT OUTER JOIN withdrawals t1 ON (t0.id = t1.user_id)
LEFT OUTER JOIN deposits t2 ON (t0.id = t2.user_id)
GROUP BY t0.id
Any idea how to do this cross join or where am i summing them wrong?
Try this-
SELECT A.id,
(SELECT SUM(amount) FROM Deposits WHERE user_id = A.id) totalDeposits,
(SELECT SUM(amount) FROM Withdrawals WHERE user_id = A.id) totalWithdraws
FROM users A
WHERE A.id = 18 -- WHERE can be removed to get all users details
You can try something along the lines of
SELECT u.id,
COALESCE(d.amount, 0) totalDeposits,
COALESCE(w.amount, 0) totalWithdrawals
FROM users u
LEFT JOIN (
SELECT user_id, SUM(amount) amount
FROM deposits
GROUP BY user_id
) d ON u.id = d.user_id
LEFT JOIN (
SELECT user_id, SUM(amount) amount
FROM withdrawals
GROUP BY user_id
) w ON u.id = w.user_id
SQLFiddle
Result:
| id | totalDeposits | totalWithdrawals |
|----|---------------|------------------|
| 18 | 200 | 400 |
The problem is that you are generating a Cartesian product. One solution is to aggregate first. Another method is to use UNION ALL and GROUP BY. I would structure this as:
SELECT u.id,
SUM(deposit) as deposits,
SUM(withdrawal) as withdrawal
FROM users u LEFT JOIN
((SELECT d.user_id, d.amount as deposit, 0 as withdrawal
FROM deposits d
) UNION ALL
(SELECT w.user_id, 0, w.amount
FROM withdrawals w
)
) dw
ON u.id = dw.user_id
GROUP BY u.id;

how to join 3 tables, topic, comment and user

I have 3 tables, tbl_topic, tbl_comment, tbl_user.
I want to select all the topics created by the user and also the topics that he commented on even if he is not the creator. Here is my database:
tbl_topic
----------
topic_id
topic_title
user_id
tbl_comment
----------
comment_id
comment_message
user_id
topic_id
tbl_user
----------
user_id
user_name
Need it so badly. Thanks!
So far i got this
select * from tbl_topic T inner join tbl_comment C on T.topic_id = C.topic_id inner join tbl_user U on T.user_id = U.user_id GROUP BY T.topic_id
My problem is it only returns the topics that has comments on it. I want to include the topics created by the user even if it has 0 comments.
I want the result to be like this:
+-----------+-----------+----------+-------------+----------------+----------+-------
| topic_id | topic_title | user_id | comment_id | comment_message | user_id | topic_id |
+-----------+-----------+----------+-------------+----------------+----------+--------
| 1 | my topic | 1 | 1 | comment me | 1 | 1
| 2 | others | 2 | 2 | comment me | 1 | 2
| 3 | my nocoment| 1 | NULL | NULL | NULL | NULL
+-----------+---------+--------+-------------+----------+----------+---------+--------
----------+-----------+
user_id | user_name |
-----------+-----------
1 | me |
2 | someone |
1 | me
-----------+---------+--
I messed up with my fields in my tables, the user_id beside comment_message should be comment_user_id but i already created my database that way. Can you help make this possible?
The query below uses UNION in the subquery. The first SELECT gets all topics created by user. The second SELECT statement gets all comments of the user and joins it to table tbl_topic so we can get the topic_title.
SELECT topic_ID, topic_title
FROM
(
SELECT a.user_ID, b.topic_ID, b.topic_title
FROM tbl_user a
INNER JOIN tbl_topic b
ON a.user_ID = b.user_ID
UNION
SELECT a.user_ID, c.topic_ID, c.topic_title
FROM tbl_user a
INNER JOIN tbl_comment b
ON a.user_ID = b.user_ID
INNER JOIN tbl_topic c
ON b.topic_ID = c.topic_ID
) x
WHERE x.user_ID = ?
Try the query below. This will show all the fields.
SELECT tt.*, tc.*, tbl_user.*
FROM tbl_topic AS tt INNER JOIN tbl_comment AS tc ON tt.topic_id = tc.topic_id INNER JOIN tbl_user as tc ON tc.user_id = tu.user_id;
WHERE tu.user_id = x
If you have to filter add to the WHERE clause to the query.
Go with a left join. But there is still a Problem left, you will only get ONE table-comment and ONE user. To get more, you can use the GROUP_CONCAT function, like here: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
select * from tbl_topic T
LEFT JOIN tbl_comment C on T.topic_id = C.topic_id
LEFT join tbl_user U on T.user_id = U.user_id
WHERE T.user_id = x
GROUP BY T.topic_id
EDIT: the where clause was missing

Mysql count from a second table

I have a query which grabs all users from the wait table based on $oneid. This works just fine but I would like to count how many messages a user leaves. These are in another table one_msg.
function waiting_users($oneid)
{
$query_str ="SELECT a.user_id, b.email, b.username, b.fbook, c.user_id as saved_user, d.type as user_type,
FROM wait a
LEFT JOIN users b ON a.user_id=b.id
JOIN user_profiles d ON a.user_id=d.user_id
LEFT JOIN save_one c ON a.user_id=c.user_id AND c.one_id=?
WHERE a.post_id = ?
ORDER BY a.date ASC";
}
$query = $this->db->query($query_str, array( $oneid, $oneid ) );
one_msg table
+----+--------+---------+---------+---------------------+
| id | one_id | host_id | user_id | date |
+----+--------+---------+---------+---------------------+
| 3 | 127 | 268 | 270 | 2012-06-11 18:57:58 |
| 4 | 127 | 268 | 270 | 2012-06-11 21:45:11 |
| 5 | 127 | 268 | 270 | 2012-06-12 09:10:01 |
+----+--------+---------+---------+---------------------+
So I am trying to count the messages from one_msg like this but it's returning the same value for all users.
function waiting_users($oneid)
{
$query_str ="SELECT a.user_id, b.email, b.username, b.fbook, c.user_id as saved_user, d.type as user_type,
(SELECT COUNT(id) FROM one_msg WHERE post_id = ?) AS count
FROM wait a
LEFT JOIN users b ON a.user_id=b.id
JOIN user_profiles d ON a.user_id=d.user_id
LEFT JOIN save_one c ON a.user_id=c.user_id AND c.one_id=?
WHERE a.post_id = ?
ORDER BY a.date ASC";
}
$query = $this->db->query($query_str, array( $oneid, $oneid, $oneid ) );
Your sub-query should check for the user_id instead of post_id (which does not exist in one_msg, so it is taken from wait:
(SELECT COUNT(id) FROM one_msg m WHERE m.user_id = a.user_id) AS count
instead of
(SELECT COUNT(id) FROM one_msg WHERE post_id = ?) AS count

mysql query to count

i am doing sql query to get values from db, db structure as follows...
fcats
| id | title | section |
+----+--------+---------+
| 1 | test | gd |
+----+--------+---------+
ftopics
| id | title | cat_id |
+----+--------+---------+
| 1 | test1 | 1 |
+----+--------+---------+
fposts
| id | post | topic_id |
+----+-------+----------+
| 1 | post | 1 |
+----+-------+----------+
I want to get total number of topics and posts for given category and using following query...
SELECT id, title (SELECT count(id) FROM ftopics WHERE cat_id = id) AS total_topics FROM fcats WHERE section = "gd"
and it gives correct results for total topics but how can i get total number of posts for all categories, i am abit confused about, please help me to write this query. thanks.
try this:
SELECT
t.id,
t.title,
COUNT(f.id) + COUNT(p.id) AS total
FROM fcats t
LEFT OUTER JOIN ftopics f
ON f.cat_id = t.id
LEFT OUTER JOIN fposts p
ON p.cat_id = t.id
WHERE t.section = "gd"
GROUP BY t.id, t.title
SELECT
t.id,
t.title,
COUNT(f.id) AS total_topics,
(SELECT COUNT(p.id) FROM fposts WHERE topic_id = t.id) total_posts
FROM fcats t
INNER JOIN ftopics f
ON f.cat_id = t.id
WHERE t.section = "gd"
GROUP BY t.id, t.title
not sure if this is the right syntax, updated the other answers code to try to answer your request for the two totals.