Codeigniter active record join with 3 tables - mysql

I have 3 tables,
user - contains user_id | username | fullname | email etcc
user_profile - contains entry_id |user_id| profile_image_id| user_location etcc
user_profile_images - contains image_id| user_id | file_path | file_thumb | date etcc
When a user sign's up, all the details go to the users table, the user_id is also added to the user_profile table with the rest set default as NULL till the user add's some profile info.
I have a query in model_users to get all user's data from these three table and it goes like this;
$this->db->where('users.user_id', $user_id)->select('*')->from('users');
$this->db->join('user_profile', 'user_profile.user_id = users.user_id', 'left');
$this->db->join('user_profile_images','user_profile_images.image_id = user_profile.profile_image_id','left');
It work's fine only when the profile_image_id field in user_profile is not null, that is when the user has uploaded a pic. In the case where the field is null, the user user_id is not returned even when all the other data are returned.
I can see why this is the case, as my join query requires the field profile_image_id but currently my way around it was to set profile_image_id in user_profile as 1(default), which is the default image and has image_id as 1 and user_id as 0 as it's the general default image. But i still can't get over the fact that i need to update that query to make it less of a hack.
Do you guys have any ideas?

Try
$this->db->select('*')->select('users.user_id')->from('users')
->join('user_profile', 'user_profile.user_id = users.user_id', 'LEFT')
->join('user_profile_images', 'user_profile_images.image_id = user_profile.profile_image_id', 'LEFT')
->group_by('users.user_id')
->where('users.user_id', $user_id);
$query = $this->db->get();

When using join for tables you need your columns in all joined tables to have unique names. Either you add column aliases by AS syntax or just retrieve the columns you need. In your case the error is most probably due to the fact that user_id exists in all three tables. Hence the result is like you experience. user_id is retrieved only for the last occurrence and that is if your user has uploaded an image. I would alter the query someting like:
$this->db->where('users.user_id', $user_id);
$this->db->select('users.*,user_profile.user_location,user_profile_images.filepath'); // and other fields of interest...
$this->db->from('users');
$this->db->group_by('users.user_id'); // very useful for not getting multiple rows/user
$this->db->join('user_profile', 'user_profile.user_id = users.user_id', 'left');
$this->db->join('user_profile_images','user_profile_images.image_id = user_profile.profile_image_id','left');

Try This one.
$this->db->select("*");
$this->db->from('table1');
$this->db->join('table2','table2.id1=table1.id1');
$this->db->join('table3','table3.id1=table1.id1');
$query = $this->db->get();
return $query->result();

Related

Multiple Joins using Codeigniter

I have this query made in codeigniter using active record
$this->db->select('products_main.*, images_main.*, items_main.*, product_tags.*');
$this->db->from('products_main');
$this->db->join('images_main','products_main.product_tag = images_main.img_tag AND products_main.product_id = images_main.pro_img_id');
$this->db->join('items_main','products_main.product_id = items_main.pro_items_id');
$this->db->join('product_tags','products_main.product_id = product_tags.pro_tag_id');
$this->db->where('products_main.product_id',$p_id);
$this->db->where('products_main.status','active');
$this->db->group_by('products_main.product_id');
$query = $this->db->get();
My goal is to:
Grab one product from products_main that matches the input from $p_id
Grab all the photos for that one product from the images_main table
Grab all the items that have the product_id of the product I have found
Grab the row in the product_tags table that match the product_id of the product i have found.
I started a test fiddle here but have not filled out the query part yet as I do not know what to put. fiddle
You don't have any problem within your query, but as you group by product id, you may only get one result, but you can group by image_id instead, i've created the same in my localhost with the database you've created a fiddle and tried your solution, your only problem i see was as you don't join on left, the query can't find any exact matches, so try to join on left, also i've changed your query a little bit to prevent the database error with the given structure in your fiddle, so change it if you have them like that in your database ( commented them ) :
$this->db->select('p.*, im.*, i.*, pt.*');
$this->db->from('products_main as p');
// img_tag -> image_tag
$this->db->join('images_main as im',
'p.product_tag = im.image_tag
AND p.product_id = im.product_id','left'); // pro_img_id -> product_id
$this->db->join('items_main as i',
'p.product_id = i.product_id','left'); // pro_items_id -> product_id
$this->db->join('product_tags as pt',
'p.product_id = pt.product_id','left'); // pro_tag_id -> product_id
$this->db->where('p.product_id',"23"); // i have given example id as 23
$this->db->where('p.status','active');
$query = $this->db->get();
return $query->result_array();

SQL merging rows with least amount of queries

My database system is pretty easy. I have a table with the users, and a table with 'guesses'. My site has logos, and a user can guess the name of each logo. There is a unique relation between the logo_id and the user_id. Here's the guesses table-structure:
|| id | user_id | logo_id | guess | guess_count | guessed | level | time ||
Now, when a user visits the page and isn't logged in, a user is made with the session_id and all the guesses and stuff are stored in the same structure. So the only difference is in the user-table.
Now I want the user to be able to login and keep whatever he/she just did. Of course, if the logged in account already has the logo as guessed, this shouldn't be altered. But when the logged in account has a logo as not-guessed, the guess_count of the session user should be added to the logged in user guess_count. Also the guessed should be updated. It should only do this if the time of the edit was more recent, but I think it's safe to assume that the session-guesses are more recent.
Now how I would do this, is loop through all the logos from the logged in id first where guessed = 0, then for each result do a query again to add the guess_count and store the guessed, then remove all the ones found from the session-id, then loop through all the old ones with the session-id and change the user_id to the one of the logged-in-user. Now, this is a ton of queries, so there must be a more efficiƫnt way.
Any thoughts? Sorry for the wall of text & bad explanation, databases are not my best thing.
Sorry havn't used mySQL in donkey's years, but here's an example of the stored procedure in SQL Server: Hopefully someone can help with the MySQL syntax, or you can infer it from the SQL below
CREATE PROC MergeGuesses
#UserSessionId INT,
#UserId INT
AS
--where the userId has already a guess for the logo update it
UPDATE gusr
SET gusr.guess_count = gusr.guess_count + gses.guess_count,
gusr.guessed = gses.guessed
FROM Guesses gusr
JOIN Guesses gses ON gusr.logo_id = gses.logo_id
AND gusr.time > gses.time -- more recent
WHERE gusr.user_id = #UserId
AND gses.user_id = #UserSessionId
AND gses.guessed = 0 --not yet guessed
--where there is no guess for the user yet - just update the userId
UPDATE gses
SET gses.user_id = #UserId
FROM Guesses gses
LEFT JOIN Guesses gusr ON gusr.logo_id = gses.logo_id
AND gusr.user_id = #UserId
WHERE gses.user_id = #UserSessionId
AND gusr.user_id = NULL -- there is no guess for the userId
--finally delete any rows for the sessionId that are left
DELETE FROM Guesses
WHERE user_id = #UserSessionId
Since I have no idea how to start stored procedures, I've just written it out in a few queries, but it's okay I guess.
$STH = $DBH->prepare("SELECT logo_id, guess_count, guessed, guess FROM guess WHERE user_id=:id GROUP BY logo_id");
$STH->bindParam(":id",$loginuser['id']);
$STH->execute();
while($row = $STH->fetch()){
if($row['guessed'] == 0){
$STH2 = $DBH->prepare("SELECT guess, guess_count, guessed FROM guess WHERE logo_id=:logo_id AND user_id=:id");
$STH2->bindParam(":logo_id",$row['logo_id'],PDO::PARAM_STR,20);
$STH2->bindParam(':id',$_SESSION['login'],PDO::PARAM_INT);
$STH2->execute();
$row2 = $STH2->fetch(PDO::FETCH_ASSOC);
if($row2){
$STH3 = $DBH->prepare("UPDATE guess SET guess_count=guess_count+:guess_count, guessed=:guessed, guess=:guess WHERE logo_id=:logo_id AND user_id=:id");
$data = array('guess_count'=>$row2['guess_count'],'guessed'=>$row2['guessed'],'guess'=>$row2['guess'],'logo_id'=>$row['logo_id'],'id'=>$loginuser['id']);
$STH3->execute($data);
$STH3 = $DBH->prepare("DELETE FROM guess WHERE logo_id=:logo_id AND user_id=:id");
$STH3->bindParam(":logo_id",$row['logo_id']);
$STH3->bindParam(':id',$_SESSION['login']);
$STH3->execute();
}
}else{
$STH2 = $DBH->prepare("DELETE FROM guess WHERE logo_id=:logo_id AND user_id=:id");
$STH2->bindParam(":logo_id",$row['logo_id']);
$STH2->bindParam(':id',$_SESSION['login']);
$STH2->execute();
}
}
$STH = $DBH->prepare("UPDATE guess SET user_id=:login WHERE user_id=:session"); // update all entries that are new from session
$STH->bindParam(':login',$loginuser['id']);
$STH->bindParam(':session',$_SESSION['login']);
$STH->execute();

select values from multiple tables where value = X

I'm trying to perform what I assume is a very simple query on a MySQL DB. Here's my table setup;
Table 1 - CMS_AccessLevels
accessLevel
titleColor
Table 2 - CMS_Users
userID
username
userEmail
userAvatar
userSignature
accessLevel
I've already got this query;
SELECT `titleColor` FROM `CMS_AccessLevels` WHERE `accessLevel` = (SELECT `accessLevel` FROM `CMS_Users` WHERE `userID` = 3)
This works correctly and returns the correct titleColor value based on the accessLevel matching across both tables.
Now, what I want to do is also grab some of the values from CMS_Users as well. For the sake of simplicity, let's assume I want to grab only a few of the values, so my result set might look something like this;
userID|username|userAvatar|accessLevel|titleColor
-------------------------------------------------
0 |Scott |image.png | 6 |#FFFFFF
or as a PHP Array (shown just so you can see the logical layout if the above table didn't make sense);
array('userID' => $result['userID'],
'username' => $result['username'],
'userAvatar' => $result['userAvatar'],
'accessLevel' => $result['accessLevel'],
'titleColor' => $result['titleColor'];
Let's say I want to get userID, userName, userAvatar and accessLevel from CMS_Users, and titleColor from CMS_AccessLevels where CMS_Users.userID is equal to '3', remembering that CMS_AccessLevels.accessLevel and CMS_Users.accessLevel MUST match.
Realistically, the only piece of data I know before running the query is userID.
Is it possible to do this with a single query?
Try this:
SELECT u.userID, u.username, u.userAvatar, u.accessLevel, al.titleColor
FROM CMS_AccessLevels al
INNER JOIN CMS_Users u
ON u.accessLevel = al.accessLevel
WHERE u.userID = 3
You are using subqueries whereas joins will be the right choice. You might try something like
SELECT a.titleColor AS titleColor, u.username AS username FROM CMS_users u INNER JOIN CMS_AccessLevels a ON u.accessLevel = a.accessLevel WHERE u.userID = '3'

Query Multiple Rows in the same Table for one Variable

I have a table setup like this:
session_id | event_id | moderator | speaker_1 | speaker_2 | speaker_3 | ...keeps going to speaker_10
What I am trying to do is setup a query that searches for 1 variable "speakerid = 13245" and check rows
'moderator', 'speaker_1', 'speaker_2', 'speaker_3', 'speaker_4', 'speaker_5', 'speaker_6', 'speaker_7', 'speaker_8', 'speaker_9', 'speaker_10'
Then return every 'session_id' corresponding to any row that contains speakerid = 12345 in any of the 11 speaker rows.
I know it has something to do with an INNER JOIN but after a lot of searching I can't find anything specific enough. I've been following stackoverflow for years now and this is my first ever post.
It really sounds like you need to normalize this table and have a table of sessions/events and a table of speakers related to it through a third sesssions_speaker table. That way you don't need to change your table schema when you have an event with 12+ speakers.
That being said, you can query like this to get the result you need
SELECT session_id
FROM table
WHERE
moderator = ?
OR speaker_1 = ?
OR speaker_2 = ?
...
OR speaker_11 = ?
I think you just need to use LIKE with OR to return the rows where any field contains "speakerid = 12345":
SELECT Session_Id
FROM YourTable
WHERE Moderator Like '%speakerid = 13245%'
OR speaker_1 Like '%speakerid = 13245%'
OR ...
You should read up on database normalization as speaker_n columns are a bad sign. You probably want a Speakers table amd a "Session-Speakers" mapping table. This would certainly make your query easier, but for now you have no choice but to search all columns:
SELECT sesion_id FROM t1 WHERE
moderator = '12345'
OR speaker_1 = 12345
etc.
You can do this using in in the where clause:
select session_id
from t
where 13245 in (moderator, speaker_1, speaker_2, speaker_3, speaker_4,
speaker_5, speaker_6, speaker_7, speaker_8, speaker_9,
speaker_10)

Brain boiling from MySQL - How to do a complicated select from multiple tables?

I have two tables: Users and Groups
In my table "Users", there is a column called "ID" for all the user ids.
In my table "Groups" there is a column called "Participants", fields in this column are filled with all the user ids like this "PID_134,PID_489,PID_4784," - And there is a column "ID" that identifies a specific group.
Now what i want to do, i want to create a menu that shows all the users that are not yet in this particular group.
So i need to get all the user ids, that are not yet in the Participants column of a group with a particular ID.
It would be cool if there was a single mysql query for that - But any PHP + MySQL solutions are okay, too.
How does that work? Any guesses?
UPDATE:
i know, that's not code, but is there a way I could do something like this that would return me a list of all the users?
SELECT *
FROM users, groups
WHERE groups.participants NOT LIKE '%PID_'users.id'%' AND groups.id = 1;
Something like this. You just get rid of "PID_" part of ID.
SELECT * FROM [users] WHERE [id] NOT IN
(SELECT replace(id,'PID_','') FROM groups WHERE group_name='group1')
Group1 would be your variable - group id/name of menu that you've opened.
You can select from multiple tables as shown below:
SELECT * from users, groups WHERE users.id != groups.participants AND groups.id = 1;
This will list all users who are not in group id 1; A more elegant solution can be found by using joins, but this is simple and will do the trick.
I believe something like that should help:
SELECT * FROM users WHERE users.id NOT IN (SELECT groups.participants FROM groups)
But this works only if your DB is normalized. So for your case I see only PHP + MySQL solution. Not very elegant, but it does the job.
<?php
$participants_array = mysql_query("SELECT participants FROM groups");
$ids = array();
while ($participant = mysql_fetch_assoc($participants_array))
{
$id = explode(',', $participant['participant']);
foreach ($id as $instance)
{
if (!in_array($instance, $ids)) $ids[] = $instance;
}
}
$participants = implode(',', $ids);
$result = mysql_query("SELECT * FROM users WHERE id NOT IN ( $participants )");
But I highly recommend normalizing the database.