Multiple tables join in SQL for this scenario - mysql

This is my table structure
I have 3 tables:
member table
comments table
comments like table
The tables structures can be found in the following images:
Table: member
--------------------------------------------------------------------
user_id |full_name |email | password | image |join_date |
| | | | | |
---------------------------------------------------------------------
Table: album_comments
--------------------------------------------------------------------
id |album_id |comment_text | comment_userid | post_date |active_bit |
| | | | | |
---------------------------------------------------------------------
Table: comment_likes
--------------------------------------------------------------------
id |user_id |comment_id | post_date | | like_bit |
| | | | | |
---------------------------------------------------------------------
I want to join three tables and retrieve the result. Here is what I need:
I want to return latest 20 comments and check if the member who is currently logged in has liked any of the comment in these 20 comments. If yes then return status bit as 1 for those comments and if not then return status bit 0.
Can anyone tell what would be the SQL query for this?

The following query should work for you. Select required columns from album_comments join it with comment_likes based on comment_id and check if the comment_likes user_id is equal to the user_id you sent from UI. ORDER BY DESC will return latest comments with LIMIT of 20.
Select |ac.Column1, ac.Column2...ac.Column-n|, cl.like_bit
FROM album_comments ac INNER JOIN comment_likes cl
ON ac.id = cl.comment_id AND cl.user_id = |screen user_id|
ORDER BY ac.id DESC LIMIT 20;

Related

How to use JOIN instead of comma?

I have this query:
INSERT INTO Votes (id_post,id_user)
SELECT ?,?
FROM Posts p, Users u
WHERE p.id_user = :id_author
AND u.id = $_SESSION['id']
AND u.active = 1
limit 1;
Now I want to use JOIN instead of ,. But there isn't any common column between those two tables. So what should I write in ON clause?
What I'm trying to do:
I have three tables:
// Posts
+----+----------+---------------+-----------+
| id | title | content | id_author |
+----+----------+---------------+-----------+
| 1 | title1 | content1 | 1234 |
| 2 | title2 | content2 | 5678 |
+----+----------+---------------+-----------+
// ^ the id of post's author
// Users
+----+--------+--------+
| id | name | active |
+----+--------+--------+
| 1 | jack | 1 |
| 2 | peter | 0 |
| 3 | John | 1 |
+----+--------+--------+
// Votes
+----+---------+---------+
| id | id_post | id_user |
+----+---------+---------+
| 1 | 32 | 1234 |
| 2 | 634 | 5678 |
| 3 | 352 | 1234 |
+----+---------+---------+
// ^ the id of current user
Now I need to check two conditions before inserting a new vote into Votes table:
Is the id of author the same as what I pass as id_author? Posts.id_user = :id_author (I know I can do that by a FK, but I don't want)
The account of current user is active? Users.active = 1
Sum Up: I'm trying to don't let people be able to vote who are inactive (active = 0). For example if Stackoverflow bans you, then you cannot vote to posts anymore, because you (current user) are banned. So I'm pretty sure $_SESSION['id'] should be used in the query to determine current user.
I suggest using exists instead of join:
INSERT INTO Votes (id_post, id_user)
SELECT id_post, id_user FROM (SELECT ? id_post, ? id_user) a
WHERE EXISTS (
SELECT 1 FROM Users
WHERE id = ?
AND active = 1
) AND EXISTS (
SELECT 1 FROM posts
WHERE id_user = :id_author
)
You already have a join here! This is an implicit join.
INNER JOIN and , (comma) are semantically equivalent in the absence of
a join condition: both produce a Cartesian product between the
specified tables (that is, each and every row in the first table is
joined to each and every row in the second table).
So there isn't a need for you to 'introduce' a join here.

Getting "subquery returns more than one row" error whenever trying to join a column from another table as alias?

I am stuck with this problem for a whole 2 days. I have a users table and it contains:
+--------+--------+----------+--------------------------------------------+
| userId | name | username | profile_pic |
+--------+--------+----------+--------------------------------------------+
| 1 | john | john123 | http://localhost/profile_pic/user1pic.jpg |
| 2 | andrew | andrew | http://localhost/profile_pi/user2pic.jpg |
| 3 | doe | doe | http://localhost/profile_pic/user3pic.jpg |
+--------+--------+----------+--------------------------------------------+
I have another table called userpost which contains:
+--------+--------+-------------+----------------------------+
| postId | userId | postMessage | postImage |
+--------+--------+-------------+----------------------------+
| 1 | 1 | "Hey" | http://localhost/post1.jpg |
| 2 | 3 | "Add me" | http://localhost/post2.jpg |
| 3 | 2 | "boring" | http://localhost/post3.jpg |
+--------+--------+-------------+----------------------------+
userId is refrenced to users.userId. I am trying to join profile_pic to userpost but mysql is returning error. Here is what I am doing:
SELECT *, (SELECT profile_pic FROM users
INNER JOIN userpost on users.userId = userpost.userId) as profile_pic FROM userpost
But getting Subquery returns more than 1 row error
I know I am doing something stupid with the query. I just want something like this:
+--------+--------+-------------+----------------------------+--------------------------------------------+
| postId | userId | postMessage | postImage | profile_pic |
+--------+--------+-------------+----------------------------+--------------------------------------------+
| 1 | 1 | "Hey" | http://localhost/post1.jpg | http://localhost/profile_pic/user1pic.jpg |
| 2 | 3 | "Add me" | http://localhost/post2.jpg | http://localhost/profile_pic/user3pic.jpg |
| 3 | 2 | "boring" | http://localhost/post3.jpg | http://localhost/profile_pi/user2pic.jpg |
+--------+--------+-------------+----------------------------+--------------------------------------------+
I am having a meeting tomorrow to showcase my prototype app. Help will be appreciated.
You are using a sub query not a join. When using subquery in the select, you have to make sure it returns exacly one row like
SELECT COL1,COL2,(SELECT 1) from YourTable
Or by using a correlated query, which I assume was your purpose but is not required since its from the same table as you select, so just use a simple join:
SELECT s.*, t.profile_pic
FROM users t
INNER JOIN userpost s on t.userId = s.userId
Try this...
SELECT *,
(SELECT top 1 profile_pic FROM users a
where a.userId = b.userId order by b.postId desc) as profile_pic
FROM userpost b
But i am not sure, the above query returns the desired profile picture.
You are not using the sub-query correctly.you should make sure that sub-query will return only one row.
Below is the Query that you require.
Select up.postId,u.userId,up.postMessage,up.postImage,u.profile_pic from user u
inner join userpost up on u.userId=up.userId

Select a MYSQL result where tables one and two match and two and three do not match

I have a two tables with users in an old format and a new format. I want to match the users with the old format to a separate table, then exclude all users who also show up in the new user format table. My data is like this:
Table newUsers:
+----+-------+-------+----------+
| id | oldid | first | last |
+----+-------+-------+----------+
| 1 | 10 | John | Kennedy |
| 2 | 66 | Mitch | Kupchak |
+----+-------+-------+----------+
Table posts:
+----+---------+
| id | user_id |
+----+---------+
| 1 | 10 |
| 1 | 66 |
| 1 | 88 |
| 2 | 88 |
| 2 | 28 |
| 3 | 10 |
+----+---------+
Table oldUsers:
+----+----------+-------+----------+
| id | username | first | last |
+----+----------+-------+----------+
| 10 | A | John | Kennedy |
| 66 | B | Mitch | Kupchak |
| 88 | C | Dale | Earnhardt|
+----+----------+-------+----------+
Result wantend:
+----+----------+-------+----------+
| id | username | first | last |
+----+----------+-------+----------+
| 88 | C | Dale | Earnhardt|
+----+----------+-------+----------+
I want to select my result by specifying: posts.id = 1 and posts.user_id = oldUsers.id and newUsers.oldid != oldUsers.id so that I only receive oldUser.id equaling 88 because he wasn't in the newUsers list.
I have tried all kinds of JOINS and SUBQUERIES. I keep getting all of the results and not the results minus corresponding entries in the newUsers table.
select * from oldusers where id in
select * from
(select id from oldusers where id in
select distinct userid from posts where id=1)
where id not in (select oldid from newusers);
Here is a way to do it
select
o.* from oldUsers o
left join newUsers n on o.id = n.oldid
left join posts p on n.oldid = p.user_id or o.id = p.user_id
where n.id is null and p.id= 1;
For better performance add the following indexes
alter table newUsers add index oldid_idx(oldid);
alter table posts add index user_post_idx (id,user_id);
I ended up finding my answer on my own and then came here to find others tried. Abhik's code did work, but was too inefficient to use. I ended up playing with my own code and IS NULL until I found something that was much more efficient.
select o.* from posts p, oldUsers o
LEFT JOIN newUsers n ON o.id = n.oldid
WHERE p.user_id = o.id AND p.id = 1 AND n.id IS NULL
Executes in .0044 seconds. Something I can use on a production site.
With indexes added from previous answer it now executes in .001x seconds so definately going with my own code.

Mysql count records grouped by ID in multiple tables

I'm developing an application integrated with facebook. This application can be embedded in FB page as tab app.
Using FB SDK feeds of page will be stored in Feeds table.
Page fans will may have liked and commented on feeds posted by page.
Users' likes store in Like Table and users' comments store in Comment table
I want to get total count ( Likes count + comment count) of each users'.
SQL Fiddle : http://sqlfiddle.com/#!2/ecb37/10/0
Table : Feeds
| ID | POST_ID |
|----|---------------------------------|
| 56 | 150348635024244_795407097185058 |
| 55 | 150348635024244_795410940518007 |
| 54 | 150348635024244_795414953850939 |
| 53 | 150348635024244_797424133650021 |
| 52 | 150348635024244_797455793646855 |
| 51 | 150348635024244_798997120159389 |
| 50 | 150348635024244_798997946825973 |
Table : Likes
SELECT user_id, COUNT(*) FROM likes GROUP by user_id
| USER_ID | LIKECOUNT |
|------------------|-----------|
| 913403225356462 | 4 |
| 150348635024244 | 3 |
| 356139014550882 | 2 |
| 753274941400012 | 2 |
| 1559751687580867 | 1 |
Table : Comments
SELECT user_id, COUNT(*) FROM comments GROUP by user_id
| USER_ID | COMMENTSCOUNT |
|-----------------|---------------|
| 150348635024244 | 2 |
| 356139014550882 | 2 |
| 913403225356462 | 2 |
Result should be like this
| POINTS | LIKESCOUNT | COMMENTSCOUNT | USER_ID |
|--------|------------|---------------|-----------------|
| 6 | 4 | 2 | 913403225356462 |
| 5 | 3 | 2 | 150348635024244 |
| 4 | 2 | 2 | 356139014550882 |
| 2 | 2 | 0 | 753274941400012 |
| 1 | 1 | 0 |1559751687580867 |
I tried this query. but count of each user's is wrong
SELECT COUNT(likes.user_id)+COUNT(comments.user_id) as points, likes.user_id FROM `likes`
LEFT JOIN comments ON likes.user_id = comments.user_id
LEFT JOIN feeds ON likes.post_id = feeds.post_id
WHERE likes.post_id LIKE '153548635024244%'
GROUP BY likes.user_id
ORDER BY points DESC
The two queries are unrelated and a join is useless. Use a UNION ALL:
SELECT user_id, sum(n) from (
SELECT user_id, COUNT(*) n FROM likes GROUP by user_id
UNION ALL
SELECT user_id, COUNT(*) FROM comments GROUP by user_id
) x
GROUP BY user_id
UNION ALL is needed instead of just UNION, because UNION removes duplicates and would cause incorrect results for the edge case of the two subqueries yielding the same counts.
The simple way to get what you want is to use count(distinct). But that will likely have lousy performance. Instead, use correlated subqueries:
SELECT COUNT(*) +
(select COUNT(c.user_id) from comments c where c.user_id = l.user_id)
) as points, l.user_id
FROM likes l
WHERE l.post_id LIKE '153548635024244%'
GROUP BY l.user_id
ORDER BY points DESC;
I'm not sure what the feeds table is for. However, you version of the query creates a cartesian product between the different tables. If you have a lot of activity for a given user, that would be very bad for performance.

Mysql include column with no rows returned for specific dates

I would like to ask a quick question regarding a mysql query.
I have a table named trans :
+----+---------------------+------+-------+----------+----------+
| ID | Date | User | PCNum | Customer | trans_In |
+----+---------------------+------+-------+----------+----------+
| 8 | 2013-01-23 16:24:10 | test | PC2 | George | 10 |
| 9 | 2013-01-23 16:27:22 | test | PC2 | Nick | 0 |
| 10 | 2013-01-24 16:28:48 | test | PC2 | Ted | 10 |
| 11 | 2013-01-25 16:36:40 | test | PC2 | Danny | 10 |
+----+---------------------+------+-------+----------+----------+
and another named customers :
+----+---------+-----------+
| ID | Name | Surname |
+----+---------+-----------+
| 1 | George | |
| 2 | Nick | |
| 3 | Ted | |
| 4 | Danny | |
| 5 | Alex | |
| 6 | Mike | |
.
.
.
.
+----+---------+-----------+
I want to view the sum of trans_in column for specific customers in a date range BUT ALSO include in the result set, those customers that haven't got any records in the selected date range. Their sum of trans_in could appear as NULL or 0 it doesn't matter...
I have the following query :
SELECT
`Date`,
Customer,
SUM(trans_in) AS 'input'
FROM trans
WHERE Customer IN('George','Nick','Ted','Danny')
AND `Date` >= '2013-01-24'
GROUP BY Customer
ORDER BY input DESC;
But this will only return the sum for 'Ted' and 'Danny' because they only have transactions after the 24th of January...
How can i include all the customers that are inside the WHERE IN (...) function, even those who have no transactions in the selected date range??
I suppose i'll have to join them somehow with the customers table but i cannot figure out how.
Thanks in advance!!
:)
In order to include all records from one table without matching records in another, you have to use a LEFT JOIN.
SELECT
t.`Date`,
c.name,
SUM(t.trans_in) AS 'input'
FROM customers c LEFT JOIN trans t ON (c.name = t.Customer AND t.`Date` >= '2013-01-24')
WHERE c.name IN('George','Nick','Ted','Danny')
GROUP BY c.name
ORDER BY input DESC;
Of course, I would mention that you should be referencing customer by ID, and not by name in your related table. Your current setup leads to information duplication. If the customer changes their name, you now have to update all related records in the trans table instead of just in the customer table.
try this
SELECT
`Date`,
Customer,
SUM(trans_in) AS 'input'
FROM trans
inner join customers
on customers.Name = trans.Customer
WHERE Customer IN('George','Nick','Ted','Danny')
GROUP BY Customer
ORDER BY input DESC;