How to join tables based on the column value in a table - php

I have the table news_feed in which all of my different types of activities data will be stored like admin activities, user activities, company activities etc. The table format looks like:
news_id | type | admin_id | user_id | company_id
1 | admin | 2 | 3 | 0
2 | user | 3 | 4 | 1
3 | company | 0 | 1 | 2
Suppose a user with an id 1 has liked the company which has id 2 then the record will be inserted like
4 user 0 1 2
And I'm listing them in my module and the listing is perfect. But suppose if the company id 2 doesn't exist or if it is inactive, then the news_feed block in listing getting empty. What I want to do is:
If the type is company then JOIN the company table while select listing with condition for status as active
If the type is user then JOIN the user table while select listing with condition for status as active

Well you can use UNION for this issue
SELECT t.column1, t.column2, t.column3
FROM my_table t INNER JOIN company_table c
ON t.company_id = c.id
WHERE c.active=1 AND t.type = "company"
UNION
SELECT column1, column2, column3
FROM my_table t INNER JOIN user_table u
ON t.user_id = u.id
WHERE c.active=1 AND t.type = "user"
Just to add, to increase the efiiciency use UNION ALL rather than UNION (or UNION DISTINCT) as UNION requires internal temporary table with index (to skip duplicate rows) while UNION ALL will create table without such index, but keep in mind it will skip the repeated data.
But more optimized way to do a Conditional Join in MySQL by using a INNER JOIN
SELECT t.column1, t.column2, t.column3
FROM my_table t
INNER JOIN company_table c
ON (t.company_id = c.id AND t.type = "company" AND c.active=1)
INNER JOIN user_table u
ON (t.user_id = u.id AND t.type = "user" AND u.active=1);

Related

Join with condition with different column

I have following two tables :
user_profile
id | name | email | phone
1 | Rahul |r#gmail.com |1234567890
2 | Rohan |r1#gmail.com |1234567890
3 | Mohan |m#gmail.com |1234567890
user_request_table
id | from_user_id|to_user_id|status
1 | 1 | 2 | 2
2 | 3 | 1 | 2
Here status
0 = Request is cancel,
1 = request is pending,
2 = request accepted
I have following section to display :
Find a friend
Here I need to display suggetion for friend. I need to display all record from user_profile table but user should not be a friend(status should not be 2) or request should not be pending(status should not be 1).
So if I logged in as Rohan(id : 2) it should suggest me Mohan as friend suggestion.
Would this work:
SELECT
u.*
FROM
user_profile u
LEFT JOIN user_request_table r ON u.id=r.to_user_id AND r.from_user_id = <your_logged_in_user_id>
WHERE
r.status NOT IN (2,1) --filter pending and accepted here ;
So you want those records in user_profile table that do not have a record in user_request table where user is either from_user_id or to_user_id. The pattern is to do a left join to user_request and filter out those where user_request.id is null:
select
p.*
from
user_profile as p
left join user_request as r on
(
(p.id = r.from_user_id and r.to_user_id = {id})
or
(p.id = r.to_user_id and r.from_user_id = {id})
)
and r.status in (1,2)
where
r.id is null
and p.id <> {id}
;
with {id} a parameter for the user you want to suggest friends for.
First get all possible pairs from the user_profile table. Next filter through all the pairs and eliminate the unqualified using data from the user_request_table. It will get all the eligible (from_user , to_user)pairs.
select u1_id as fromuser,u2_id as touser
from
(select distinct u1.id as u1_id,u2.id as u2_id from user_profile u1 cross join user_profile u2 where u1.id!=u2.id) all_pair
where not exists
(select from_user_id ,to_user_id from user_request_table where status>0 and (
((u1_id=from_user_id) and(u2_id=to_user_id)) or ((u1_id=to_user_id) and(u2_id=from_user_id))
)
)
;
The result set looks like this:
fromuser, touser
3 2
2 3
This solution is for MSSQL SERVER, you can modify as per your preferred server
Pass your logged in id to declared variable
DECLARE #LoginId AS INT
SET #LoginId=2 /*here I passed logged in id as 2*/
SELECT * FROM user_profile up
WHERE 1=1 and id<>#LoginId
AND id NOT IN (SELECT from_user_id FROM user_request_table WHERE to_user_id=#LoginId and STATUS in(1,2))
AND id NOT IN (SELECT to_user_id FROM user_request_table WHERE from_user_id=#LoginId and STATUS in(1,2))

MySQL joining tables and returning the latest row from the second table when the comparison is made between identical values

I have table 1: users
id | name |
1 | john |
2 | garry|
3 | sam |
And table two called posts:
id | user_id | title | posted
1 | 1 | Something | 1551128761
2 | 1 | Else | 1551128761
3 | 3 | Some Title | 1551122745
4 | 2 | Demo Title | 1551129777
5 | 3 | Something | 1551126793
user_id in the second table is the id of the user in the first table
I need to get the latest post out of the table and i'm doing that currently by using this query:
SELECT u.id, u.name, p.title
FROM users AS u
LEFT JOIN posts AS p
ON p.user_id= u.id
WHERE p.posted = ( SELECT MAX(posted) FROM posts WHERE user_id = u.id )
ORDER BY u.id
LIMIT 15
But the problem with this query is that if the timestamp is the same for the same user (in this example for user with user_id 1 the timestamp is the same) i'm getting both of those rows instead of just the latest one(the latest one has the highest id)
Try this MySQL query:
SELECT u.id,
u.name,
p.title
FROM users AS u
JOIN posts AS p
ON p.id = (SELECT pi.id
FROM posts AS pi
WHERE pi.user_id = u.id
ORDER BY pi.id DESC
LIMIT 1);
Tested and works fine. Here is a demo: DBFiddle
To speed up select query, consider adding indexes
ALTER TABLE posts ADD INDEX(user_id);
ALTER TABLE posts ADD INDEX(posted);
One option using id column from posts table as following. This is assuming id is going to be different for each post record is posts table. Demo here
SELECT u.id, u.name, p.title,p.posted
FROM users AS u
LEFT JOIN posts AS p
ON p.user_id= u.id
WHERE (p.posted,p.id) = ( SELECT MAX(posted),MAX(id) FROM posts WHERE user_id = u.id )
ORDER BY u.id
How about restructuring the query slightly?
SELECT posts.title, users.id, users.name
FROM posts, users
WHERE posts.user_id = users.id
ORDER BY posts.posted DESC, posts.id DESC
LIMIT 1
Essentially selecting from posts, ordering by the posted timestamp and secondly the id of the post in descending order in case timestamp is the same.

MYSQL joining three tables

I have a simple multiple school management system and I am trying to get total number of teachers, and total number of students for a specific school. My table structures are as follows:
teachers
--------------------------
id | schoolid | Name | etc...
--------------------------
1 | 1 | Bob |
2 | 1 | Sarah|
3 | 2 | John |
students
--------------------------
id | schoolid | Name | etc...
--------------------------
1 | 1 | Jack |
2 | 1 | David|
3 | 2 | Adam |
schools
--------------------------
id | Name | etc...
---------------------------
1 | River Park High |
2 | Stirling High |
I can count just all teachers with the following query:
SELECT COUNT(a.id) AS `totalteachers`
FROM teachers a
LEFT JOIN schools b ON a.schoolid = b.id WHERE b.id = '1'
and similarly I can count the number of teachers with the following query:
SELECT COUNT(a.id) AS `totalstudents`
FROM students a
LEFT JOIN schools b ON a.schoolid = b.id WHERE b.id = '1'
I am however struggling with trying to combine these two queries to get a simple result like this:
totalstudents | totalteachers
--------------------------------
2 | 2
I have tried the following:
SELECT COUNT(a.id) as `totalteachers`, COUNT(c.id) as `totalstudents`
FROM teachers a
LEFT JOIN schools b ON a.schoolid = b.id
LEFT JOIN students c ON c.schoolid=b.id WHERE b.id = '5'
You can do something like this
SELECT
id, name, s.total AS totalstudents, t.total AS totalteachers
FROM schools
JOIN (SELECT schoolid, COUNT(id) AS total FROM teachers GROUP BY schoolid)
AS t ON t.schoolid = id
JOIN (SELECT schoolid, COUNT(id) AS total FROM students GROUP BY schoolid)
AS s ON s.schoolid = id
then you can add where id = 2 or whatever to limit the school.
The problem with the multiple left joins is it generates additional records for each teacher to each student; artifically inflating your counts
There's four ways to solve this: (best imo is what Andrew bone did)
Simply select inline without the joins so the counts are not inflated. (most desirable in my mind as it's easy to maintain)
SELECT (SELECT COUNT(a.id) AS `totalteachers`
FROM teachers a
WHERE A.SchoolID = '1') as TotalTeachers
, (SELECT COUNT(a.id) AS `totalstudents`
FROM students a
WHERE a.SchoolID = '1') as TotalStudents
Use subqueries to get the counts first before the joins, then join. Since count will always be 1 a cross join works.
SELECT totalTeachers, totalStudents
FROM (SELECT COUNT(a.id) AS `totalteachers`
FROM teachers a
LEFT JOIN schools b
ON a.schoolid = b.id
WHERE b.id = '1')
CROSS JOIN (SELECT COUNT(a.id) AS `totalstudents`
FROM students a
LEFT JOIN schools b ON a.schoolid = b.id
WHERE b.id = '1')
Use key word distinct within the count so as not to replicate the counts and negate the artificial inflation (least desirable in my mind as this hides the artifical count increase)
SELECT COUNT(distinct a.id) as `totalteachers`, COUNT(distinct c.id) as `totalstudents`
FROM teachers a
LEFT JOIN schools b ON a.schoolid = b.id
LEFT JOIN students c ON c.schoolid=b.id WHERE b.id = '5'
Another way would be to use a window functions, however these are not available in mySQL.
SELECT COUNT(t.id) AS TotalTeachers, COUNT(st.id) AS TotalStudents
FROM schools s
INNER JOIN teachers t
ON s.id = t.schoolid
INNER JOIN students st
ON s.id = st.schoolid
Try this SQL. I havn't try it but it should work.

Or condition in INNER JOIN

I have a table structured as follows (points):
id1 | id2 | p1 | p2
1 3 5 7
3 1 2 4
1 2 1 7
And another table strucuted as follows (users):
id | name
1 User1
2 User2
3 User3
So now, I need a query that specifing an ID (for example 3), the query check that the ID appears in the column id1 and id2, and if it appears in one of the two columns, it gives me back the user name with id1 and id2 from the rows selected. So, for example if I specific the ID 3, the query give me back:
name1 | name2 | p1 | p2
User1 User3 5 7
User3 User1 2 4
I tried various solutions but no way to do it, I think that I need an OR condition in the INNER JOIN but I don't know if it's possible and if it's the solution.. I didn't find nothing here.
I mean something like:
INNER JOIN users ON (users.id = points.id1) || (users.id = points.id2)
Any solution for that? Thanks
Join the user table twice:
SELECT u1.name, u2.name, p.p1, p.p2
FROM points p
JOIN users u1 ON u1.id = p.id1
JOIN users u2 ON u2.id = p.id2
WHERE u1.id = 3 OR u2.id = 3
Use case statement it will give you all matching value not need restricted for one or two values
CREATE TABLE points (id1 int(2), id2 int(2), p1 int(2), p2 int(2));
INSERT INTO points VALUES(1,3,5,7);
INSERT INTO points VALUES(3,1,2,4);
INSERT INTO points VALUES(1,2,1,7);
CREATE TABLE users (id int(2), name char(23));
INSERT INTO users VALUES(1,'user1');
INSERT INTO users VALUES(2,'user2');
INSERT INTO users VALUES(3,'user3');
SELECT (CASE WHEN u.id = p.id1 THEN u.name END) AS name1,
(CASE WHEN u1.id = p.id2 THEN u1.name END) AS name2,
p1, p2
FROM points p
INNER JOIN users u ON (u.id = p.id1)
INNER JOIN users u1 ON (u1.id = p.id2);

Save selected in variable and then use that variable for next select

I have multiple tables in my database. Let's say the table users looks like this:
Users:
|id|name|gender|access|id_ext|
|1 | a | m | 1 | 32 |
|3 | b | m | 3 | 33 |
|4 | c | m | 1 | 34 |
|5 | d | f | 1 | 35 |
I would like to select the user with for example id_ext = 32 and then run another select statement using that selected users fields.
I can solve this by first getting the user with a query and then create another query with users info, but there must be a way to do this in the same query?
This is the query i use now:
SELECT * FROM users NATURAL JOIN
(SELECT id FROM ages WHERE age BETWEEN
(SELECT limit_age_l FROM users WHERE id=17)
AND (SELECT limit_age_h FROM users WHERE id=17)) as a
WHERE NOT id = 17
AND locale = 'en_US'
AND limit_gender = 1
AND visible = 0
AND NOT EXISTS (SELECT view_id FROM matches WHERE user_id = 17 AND view_id = a.id)
LIMIT 1
Problem is that the values id=17, limit_gender=1 and locale = 'en_US' in the query are not known. These are taken from the user with id_ext = '32'.
SELECT * FROM Users WHERE id in (SELECT id FROM Users WHERE id_ext='32');
Yes - assuming your subsequnt query is of the form:
select field1, field2, ...
from Table1
join Table2 on ...
where ...
and Table1.id = N /* previously selected id from users */
Then either by using the first query as a subquery:
select field1, field2, ...
from Table1
join Table2 on ...
where ...
and Table1.id = (select id from users where id_ext ='32')
/* replace = with IN if more than one id will be returned */
Or by joining to the results of the first query as part of the subsequent query:
select field1, field2, ...
from users
join Table1 on Table1.id = users.id
join Table2 on ...
where ...
and users.id_ext ='32'
(Note that both of these forms assume that users is not already being joined in the existing query - if it is, just add the users.id_ext ='32' condition to the existing query.)
EDIT: If I have understood the requirements correctly, the required query could be written as:
SELECT u.*
FROM users u
join ages a on u.id = a.id and
u.age between limit_age_l and limit_age_h
join users ul on ul.id = 17 and
ul.id <> u.id and
ul.locale = u.locale and
ul.limit_gender = u.limit_gender and
ul.visible = u.visible
AND NOT EXISTS (SELECT NULL
FROM matches m
WHERE m.user_id = ul.user_id AND m.view_id = a.id)
LIMIT 1
SELECT * FROM users WHERE id = (SELECT id FROM users WHERE id_ext = '32');
Select * from users as user inner join userinfo as usinfo on usinfo.id=user.id_ext where user.id_ext='32'

Categories