How to join four tables in MySQL [PHP] - php

I have four tables and I want to join all of them. I have successfully joined 3 tables, but when I try to join the fourth one, it doesn't work. I have set error_reporting(E_ALL); and add or die(mysqli_error($con)); to the end of my query but it doesn't show any errors, just a white screen.
It stopped working when I tried to join the votes table.
My tables are votes users rings posts
Here is my query:
$sql = mysqli_query($con, "SELECT * FROM posts p
INNER JOIN rings r ON p.rid = r.id
INNER JOIN users u ON p.uid = u.id
INNER JOIN votes v ON p.pid = v.pid
WHERE p.rid IN ('$rja') AND p.uid != '$uid'
AND p.deleted = '0'
ORDER BY p.date_posted DESC"
) or die(mysqli_error($con));
$rja is an array.
Votes Table:
vid | pid | uid | vote_type
Users Table:
id | username | password | email
Posts Table:
pid | uid | rid | body | votes | deleted | date_posted
Rings Table:
id | title | category | rating | user_created
If you have any questions please comment

Try left join
SELECT * FROM posts p
INNER JOIN rings r ON p.rid = r.id
INNER JOIN users u ON p.uid = u.id
LEFT JOIN votes v ON p.pid = v.pid
WHERE p.rid IN ('$rja') AND p.uid != '$uid'
AND p.deleted = '0'
ORDER BY p.date_posted DESC"

Related

Stock with slow SQL query

I have been working with the same SQL query for a couple of hours and it is finally working. But, it is very slow.. I have been trying to optimize it, but no luck, any help. Here is the query (Lots of left joins...):
$sql ="SELECT u.id, u.display_name, IFNULL(SUM(r.total_rating)/COUNT(r.total_rating), 0) AS avg_rating, s.title AS study FROM users u
LEFT JOIN rating r ON u.id = r.user_id
LEFT JOIN usermeta m ON u.id = m.user_id
LEFT JOIN usermeta m1 ON u.id = m1.user_id
LEFT JOIN studies s ON m.meta_value = s.id
WHERE m.meta_key = 'study' AND m1.meta_key = 'subjects' AND m1.meta_value REGEXP '$subjectsvalues'
GROUP BY u.id, r.total_rating
ORDER BY avg_rating DESC
LIMIT 10";
Table structure for user table:
id | display_name | email
-------------------------
1 | Khar | ...
2 | SantaCruz | ...
Table structure for rating table:
id | rating_title | total_rating | user_id
-------------------------------------------
1 | dffd | 5 | 1
2 | fddfdffdd | 4 | 1
Table structure for usermeta table:
id | user_id | meta_key | meta_value
-------------------------------------
1 | 1 | study | 132
2 | 1 | subjects | 121,231
Table structure for studies table:
id | title
----------
1 | dsdsf
2 | sdfdf
Subject values are handled like so:
$subjectsvalues = '';
$subjects = explode(",", $subjects);
foreach($subjects as $val) {
$subjectsvalues = $subjectsvalues.",".$val.",|";
}
$subjectsvalues = $subjectsvalues."notdata";
First, left joins are unnecessary. So try this:
SELECT u.id, u.display_name, AVG(r.total_rating) AS avg_rating, s.title AS study
FROM users u JOIN
rating r
ON u.id = r.user_id JOIN
usermeta m
ON u.id = m.user_id JOIN
usermeta m1
ON u.id = m1.user_id JOIN
studies s
ON m.meta_value = s.id
WHERE m.meta_key = 'study' AND m1.meta_key = 'subjects' AND m1.meta_value REGEXP '$subjectsvalues'
GROUP BY u.id, r.total_rating
ORDER BY avg_rating DESC
LIMIT 10;
Then, I would be inclined to try indexes on usermeta(meta_key, user_id, meta_value). I assume the main ids in the tables are all primary keys.
I can't suggest an edit to Gordon Linoff's answer, so here's an improved version. You don't really need to join with usermeta twice unless usermeta is being compared with itself.
SELECT u.id, u.display_name, AVG(r.total_rating) AS avg_rating, s.title AS study
FROM users u JOIN
rating r
ON u.id = r.user_id JOIN
usermeta m
ON u.id = m.user_id JOIN
studies s
ON m.meta_value = s.id
WHERE m.meta_key = 'study' OR (m.meta_key = 'subjects' AND m.meta_value REGEXP '$subjectsvalues')
GROUP BY u.id, r.total_rating
ORDER BY avg_rating DESC
LIMIT 10;
Also, could you please explain the utility of your regular expression? It most likely won't match anything as $ indicating end of the pattern is placed at start of regex.

MYSQL : select count with union three table

I have three table content information about posts
and each table has Post_id it's foreign_key for Post table
first table = `likes`
second table = `Comment`
and last one = `Visitor`
each Table has some info about users like session or id and etc
i need to create new view table contain post id and the number of visitor , likes , comment
i tried this
SELECT *
from (
select id , count(id) as Comment
from Post left join Comment on id = Post_id
group by id
UNION
select id, count(id) as Visitor
from Post left join Visitor on id = Post_id
group by id
UNION
select id, count(id) as Likes
from Post left join Likes on id = Post_id
group by id
) CountsTable
GROUP BY CountsTable.id
but it didnt work .
i dont know why the result is only the first inner select
in my example the result is
| id | Comment|
|--------|------- |
| 1 | 55 |
| 2 | 25 |
| 3 | 12 |
i expect something like that
| id | Comment | Likes | Visitor |
|--------------|-------|---------|
| 1 | 55 | 100 | 2000 |
No need to use UNION. Count the records for each post in all three tables and Left Join result with Post table
Try something like this
SELECT id,
comments,
vistors,
likes
FROM Post p
LEFT JOIN (SELECT Count(Post_id) comments, Post_id
FROM Comment
GROUP BY Post_id) c
ON p.id = c.Post_id
LEFT JOIN (SELECT Count(Post_id) vistors, Post_id
FROM Visitor
GROUP BY Post_id) v
ON p.id = v.Post_id
LEFT JOIN (SELECT Count(Post_id) likes, Post_id
FROM Likes
GROUP BY Post_id) l
ON p.id = l.Post_id
You can do it with a Left Join query, e.g.:
select u.id, count(distinct l.id) as likes, count(distinct c.id) as comments
from user u left join likes l on u.id = l.user_id
left join comments c on u.id = c.user_id
group by u.id;
Here is SQL Fiddle.
SELECT id, SUM(Comment),SUM(Visitor),SUM(Likes) from (
select id , count(id) as Comment, 0 as Visitor, 0 as Likes
from Post left join Comment on id = Post_id
group by id
UNION ALL
select id, 0 as Comment, count(id) as Visitor , 0 as Likes
from Post left join Visitor on id = Post_id
group by id
UNION ALL
select id, 0 as Comment, 0 as Visitor, count(id) as Likes
from Post left join Likes on id = Post_id
group by id
) CountsTable
GROUP BY CountsTable.id

Slow query using "where in" and "order by select count"

I want to show post from users that specified user is followed and i have two tables at below. but its query is very slow.
table user
id | username
1 | name1
2 | name2
3 | name3
..
..
table post
id | poster_id | post_content
1 | 2
2 | 3
3 | 10
..
..
table follow
followerid | followtoid
1 | 2
1 | 3
2 | 10
..
..
Assume that all tables have more than 1000 rows.
This's SQL
SELECT *
FROM post
WHERE poster_id IN (
SELECT followtoid
WHERE followerid = $_SESSION['userid']
)
And this's the second cast is very slow too.
I want to list all member by order from their total posts.
SELECT *
FROM user
ORDER BY (
SELECT COUNT(id)
FROM post
WHERE post_id = user.id
) DESC;
Try indexing post.userid, post.poster_id, followtoid.followerid and user.user_id, using CREATE INDEX, and use LEFT JOIN clause on your queries instead:
SELECT *
FROM user u
LEFT JOIN SELECT poster_id, COUNT(*) as count FROM post p GROUP BY poster_id
ON (u.user_id = p.poster_id)
ORDER BY count DESC;
and:
SELECT * FROM post AS p
LEFT JOIN (SELECT followerid FROM followtoid) AS f
ON (p.userid=f.followerid)
WHERE p.userid = {$_SESSION['userid']}
Use a JOIN for the first query
SELECT p.*
FROM post p
JOIN follow f ON p.post_id = f.followtoid
WHERE f.followerid = $_SESSION['userid']
and a JOIN plus a GROUP BY for the second
SELECT u.*, tbl.postCount
FROM user u
JOIN (
SELECT poster_id, COUNT(*) AS postCount
FROM post p
GROUP BY posterID
) tbl ON tbl.poster_id = u.id
ORDER BY postCount DESC
You can accomplish the second query without a subquery:
SELECT u.*, COUNT(p.poster_id) as postCount
FROM user u
LEFT JOIN post p
ON (u.user_id = p.poster_id)
GROUP BY u.user_id
ORDER BY postCount DESC;

MYSQL QUERY to join multiple tables

I am looking for single query where I can connect multiple tables.
The Query is as follows
`
SELECT
a.name as module_name,
a.id,
b.id as subject_id,
b.SUBJECT_name,
c.id as course_id,
c.course_name,
d.id as cordinator_id,
d.cordinator_name
FROM module_table a
LEFT JOIN subject_table b ON b.id = a.subject_id
LEFT JOIN course_table c ON c.id = a.course_id
LEFT JOIN cordinator_table d ON d.id = a.cordinator_ids
WHERE a.id = $somevalue
ORDER BY a.id DESC
`
Above query is producing error and when I am connecting the two tables Its showing all right
SELECT
a.name as module_name,
a.id,
b.id as subject_id,
b.SUBJECT_name
FROM module_table a
LEFT JOIN subject_table b ON b.id = a.subject_id
WHERE a.id = $somevalue
ORDER BY a.id DESC`
The first Table has all foreign keys for subject and course table, further subject table is connected to coordinator table with common id column.. I want the corresponding names of the id given in the module table..
The last table is the result of query I want from where I can collect my required data
My table structures are below
MODULE CAN BE INCLUDED ONLY IN A SUBJECT AND SUBJECT CAN BE INCLUDED IN COURSE
EACH SUBJECT CAN HAVE ANY NUMBER OF CORDINATORS WHICH I AM KEEPING THEM AS JSON value
-----------------------------------------------------------------------
MODULE TABLE
_____________________________________________________
id | subject_id | course_id | cordinator_id | name
-----------------------------------------------------------------------
COURSE TABLE
__________________
id | course_name
-----------------------------------------------------------------------
SUBJECT TABLE
_________________________________________________
id | course_id | cordinator_id | SUBJECT_name
-----------------------------------------------------------------------
CORDINATOR TABLE
______________________________________
id | cordinator_ids | cordinator_name
Result TABLE
___________________________________________________________________________________________
id | module_name | subject_id | subject_name | course_id | course_name | cordinator_ids
I am able to join two tables successfully with LEFT Join but on third it is reporting an error.
The problem is that you have a few typos and forgot to JOIN course_table.
Here is the right query:
SELECT
a.name as module_name,
a.id,
b.id as subject_id,
b.SUBJECT_name,
c.id as course_id,
c.course_name,
d.id as cordinator_id,
d.cordinator_name
FROM module_table a
LEFT JOIN subject_table b ON b.id = a.subject_id
LEFT JOIN course_table c ON c.id = a.course_id
LEFT JOIN cordinator_table d ON d.id = a.cordinator_id
WHERE a.id = $somevalue
ORDER BY a.id DESC
On this line:
b.SUBJECT_name,
you had to put uppercase "SUBJECT_name" as in your table subject_table schema.
And your query was lacking this JOIN to allow you to select course_table fields:
LEFT JOIN course_table c ON c.id = a.course_id

mysql query within a query with privacy condition check

I have 3 tables.
myMembers
------------------------------------
id | username | privacy
------------------------------------
1 | userA | 0
2 | userB | 1
3 | userC | 0
4 | userD | 1
following
--------------------------------
id | user_id | follower_id
--------------------------------
1 | 2 | 1
posts
-------------------------------------
id | userID | username | statusMsg
--------------------------------------
1 | 4 | userD | Issac Newton is genius
2 | 2 | userB | Newton Saw apple
3 | 3 | userC | Newtonian Physics
4 | 1 | userA | Calculus came from Sir Newton
There is a search field. When a logged in user searches for 'keyword' in table 'posts', I want to omit results from those users who has set his privacy to '1' and WHERE searcher is not following user B.
The query should logically do this.
SELECT * from posts WHERE (match the keyword)
AND (
if (poster's privacy (which is set in myMembers)==1){
if (seacher is following poster){
select this post
}
}
else { select this post
}
)
LIMIT results to 5 rows
So for a keyword "Newton",
if userA is searching, rows 2,3,4 from 'posts' should be returned.
if userD is searching, only rows 1, 3 and 4 from 'posts' should be returned,
based on privacy and following
Edit: Tagging for future searches: IF condition within WHERE Clause in mySql
Please, try this query (also on SQL Fiddle):
SELECT p.id, p.user_id, m.username, m.privacy,
searcher.username "Searcher", p.status_msg
FROM posts p
JOIN members m ON m.id = p.user_id
LEFT JOIN following f ON p.user_id = f.user_id
JOIN members searcher ON searcher.username = 'userA'
WHERE (m.privacy = 0 OR (m.privacy = 1 AND f.follower_id = searcher.id)
OR m.id = searcher.id)
AND p.status_msg LIKE '%New%'
ORDER BY p.id
LIMIT 5;
I removed username field from posts table, as it is redundant. Also, I named tables and columns slightly different, so query might need cosmetic changes for your schema.
The first line in the WHERE clause is the one that you're looking for, it selects posts in the following order:
First posts from members without privacy;
Then posts from members that are followed by the current searcher;
Finally, posts of the member himself.
EDIT:
This query is using original identifiers:
SELECT p.id, p.`userID`, m.username, m.privacy,
searcher.username "Searcher", p.`statusMsg`
FROM posts p
JOIN `myMembers` m ON m.id = p.`userID`
LEFT JOIN following f ON p.`userID` = f.user_id
JOIN `myMembers` searcher ON searcher.username = 'userD'
WHERE (m.privacy = 0 OR f.follower_id = searcher.id OR m.id = searcher.id)
AND p.`statusMsg` LIKE '%New%'
ORDER BY p.id
LIMIT 5;
EDIT 2:
To avoid duplicates in case there're several followers for the user from the posts table, join and filtering conditions should be changed the following way (on SQL Fiddle):
SELECT p.id, p.user_id, m.username, m.privacy,
searcher.username "Searcher", p.status_msg
FROM posts p
JOIN members m ON m.id = p.user_id
JOIN members searcher ON searcher.username = 'userC'
LEFT JOIN following f ON p.user_id = f.user_id
AND follower_id = searcher.id
WHERE (m.privacy = 0 OR (m.privacy = 1 AND f.id IS NOT NULL)
OR m.id = searcher.id)
ORDER BY p.id
LIMIT 5;
Try the following:
SET #my_user_id= 1;
SELECT * FROM posts p
INNER JOIN myMembers m ON p.user_id= m.id
WHERE statusMsg LIKE '%'
AND privacy=0
AND user_id IN (SELECT follower_id FROM following f WHERE f.user_id=#my_user_id)
LIMIT 5
try this:
SELECT a.*
FROM posts a
LEFT JOIN (SELECT user_id
FROM following a1
INNER JOIN myMembers b1
ON a1.follower_id = b1.id
WHERE a1.follower_id = 1 AND
b1.privacy = 1
) b
ON a.userID = b.user_id AND
WHERE a.statusMsg LIKE '%search%' AND
b.user_id IS NULL
LIMIT 5;
or better approach without subquery:
SELECT a.*
FROM posts a
LEFT JOIN myMembers b
ON a.userID = b.id AND
b.privacy = 1
LEFT JOIN following c
ON a.userID = c.user_id AND
c.follower_id = 1
WHERE a.statusMsg LIKE '%search%' AND
b.id IS NULL AND
c.user_id IS NULL
LIMIT 5;
See: A Visual Explanation of SQL Joins

Categories