MySQL Query and Join Tables - mysql

I'm working something with mysql and php and I'm trying to achieve some result for learning purposes.
So what I'm trying is to make conversation messages system and I have following:
I have 2 tables, first conversation and second conversation_messages
First table conversation looks like following:
c_id, user_one, user_two
Second table conversation_messages looks like this:
m_id, text, date, created_by, status, c_id
So in messages table I set Conversation ID and when user click conversation to open it, url change to messages.php?c_id=1 or something like that... And that's fine, becouse I get c_id from url and so on.
My question is following:
Lets say I wan't to get all messages for conversation c_id = 1. How do I query trough table and get all messages for that conversation id. Also I need to query so it return results only if logged user is involved into conversation... So logged in user can see conversation messages only if he is person/user A (user_one) or user B(user_two). How do I do that and do I need to join tables. So what is the best way to do this.
So when logged in user type manually into url messages.php?c_id=3 if he is not involved into that conversation I don't want him to see it.
Sorry I'm new here and don't know how to format code properly or anything.
Thanks in advance.

You need to get the logged user id from a session and put into the query like that
SELECT * FROM conversation_message, conversation
WHERE conversation.c_id = $ID_OF_CONVERSATION
AND (user_one = $ID_LOGGED_USER OR user_two = $ID_LOGGED_USER)
AND conversation_message.c_id = conversation.c_id

In the broad strokes, if you want to add security to a certain endpoint, you need to allow or deny access after validating user input. In the example you gave, your user input is the c_id value of 3. In a simple PHP example, you could do something like this:
$user_id = $_SESSION['user_id'];
$can_access = false;
$convo_id = $_GET['c_id'];
$safe_id = mysql_real_escape_string( $convo_id );
$rli = mysql_query( "SELECT * FROM conversation WHERE c_id = {$safe_id}" );
if( mysql_num_rows( $rli ) ) {
$convo = mysql_fetch_object( $rli );
$can_access = $convo->user_one == $user_id || $convo->user_two == $user_id;
}
Notice in this example that I pulled the "logged in" user's id from the session, which assumes that you are using sessions. There are many different ways to create "logged in" user views, and that is somewhat outside the scope of this answer. The end result here is a boolean value variable $can_access which indicates whether or not the user can access the page. Assuming they can access the page, you could pull all the messages from the now validated conversation like so:
$rli = mysql_query("SELECT * FROM conversation_messages WHERE c_id = {$safe_id}");
$messages = array();
while( $message = mysql_fetch_object( $rli ) ) {
$messages[ $message->m_id ] = $message;
}
The above gives you a PHP array containing all the messages associated with the conversation. Hope this is enough to get you started.

Related

SQL query for build inbox of messages

Help with an SQL Query:
I want to get the last message of every conversation between client and helper. Based on same clientId lets say clientId = 1 but different helperId. What I am trying to achieve is build an inbox where last only last message of the conversation is available.
Here is the table structure.
Note: The clientId is known in the table everyother field is not known.
Please have a look at the table structure below:
You can do it this way:
$table = // table name
$clientId = // client's id
$query = "select * from ".$table." where `clientId`= '".$clientId."' group by `helperId` order by `id` desc";
This would give you the last conversation for the same client but with different helper.

how do i achieve following condition in codeigniter sql?

I have a table with the name message_system.
and structure of the table is as follow:
message_system => message_id(primary key)->sender_id->receiver_id->
message->time(current time stamp)
lets consider a $user_id = 1 and I want to select the row from the table
something like
select * from message_system where sender_id = $user_id or receiver_id = $user_id
and from the yielded result I want select only single result which has recently stored according to time stamp.
actually I want to select recent message from the message thread between two user..
edit 1:
I want to get recent message from the table with respect to each
unique pair of (user1_id and user2_id). Either it is(user1_id,
user2_id) or (user2_id, user1_id) usually user id of logged in user
would be on of the id and another is id of user who have been
messaged to current logged in user.
edit 2:
example:
lets consider user1 is logged in and he has messaged to user2. and later messaged to user3 and user4.
and those all messages are stored in the table and now i want to query such that it retrieves only recent message with user2,user3,user4 in user1 wall. not all their history
edit 3 :
query should return recent message of logged in user with user1 and recent message of logged in user user2 and user3 so on.
thank you for your time.
select * from message_system where sender_id = $user_id or receiver_id = $user_id
would translate in CodeIgniter in:
$this->db->where('sender_id', $user_id);
$this->db->or_where('receiver_id', $user_id);
$query = $this->db->get('message_system');
$row = $query->row();
If you want the most recent input, you could order by time, and if you know you only need one row, limit to 1:
$this->db->where('sender_id', $user_id);
$this->db->or_where('receiver_id', $user_id);
$this->db->order_by('time', 'desc');//order by time
$this->db->limit('1');//limit the query to only one row
$query = $this->db->get('message_system');
$row = $query->row();// get only first
UPDATE:
The solution above works if you want to find one entry corresponding to one given user.
If you want to find last entry for a given pair of users, thing would look like this if unknown who would be the sender and who would be te receiver:
$this->db->where_in('sender_id', array($user_id1, $user_id2));
$this->db->where_in('receiver_id', array($user_id1, $user_id2));
$this->db->order_by('time', 'desc');//order by time
$this->db->limit('1');//limit the query to only one row
$query = $this->db->get('message_system');
$row = $query->row();// get only first
And like this if you know exactly who is who:
$this->db->where('sender_id', $user_id1);
$this->db->where('receiver_id', $user_id2);
$this->db->order_by('time', 'desc');//order by time
$this->db->limit('1');//limit the query to only one row
$query = $this->db->get('message_system');
$row = $query->row();// get only first
With little bit modification of the table I have come up with an idea.
earlier my table structure was:
message_system => message_id(primary key)->sender_id->receiver_id->
message->time(current time stamp)
after modification:
message_system => message_id(primary key)->sender_id->receiver_id->
message->time(current time stamp)->unique_pair_key
unique_pair_key is to identify the message between two user uniquely and value would be something like this ($sender_id^2 + $receiver_id^2)
which is unique for given pair.
all messages between two particular user would be identify by this unique_pair_key.
so taking logged_in user as the reference, with respect to him only recent message with different user (if there any) should be shown (similar to stack overflow recent inbox message )and on clicking that particular message it should direct to their message thread.
model/model.php
public function get_notification($user_id)
{
$this->db->where("receiver_id",$user_id);
$this->db->where("sender_id",$user_id);
//because recent message would either be sent or received by logged in user
$this->db->select_max('message_id');
// latest message would have greatest value of id
$this->db->group_by('unique_pair_key');
$query = $this->db->get('message_system ');
//returns latest message_id with unique pair in which on of them is $userid.
}
above query only generates message id with that we can further query to get message
If you have any better solution please share with us.

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();

Perl and MySQL - Return a field from last row of a table using DBI possibly with last_insert_id?

I am trying to return a members firstname field from the table users from the last row of users.
my $dbh = DBI->connect($dsn, $db_user_name, $db_password) or die "$DBI::errstr";
my $LastID = $dbh->last_insert_id(`firstname`); ##but I want from table users
print qq~$LastID~;
This error is returned from above:
DBI last_insert_id: invalid number of arguments: got handle + 0, expected handle + between 4 and 5
Usage: $h->last_insert_id($catalog, $schema, $table_name, $field_name [, \%attr ])
So, what would be the "best" way (best being best for server, fastest, least memory, load, least amount of overhead.. whatever) to get the field firstname from the last row in the table users?
Realize my example above is not to be taken seriously as I have no idea how to do this without just doing something like my crude, but functional:
(p.s. UserID is NOT assigned by auto increment but, is in numeric order and a new user gets a higher UserID. Just the way this was when I tackled the pre existing problem.)
my $dbh = DBI->connect($dsn, $db_user_name, $db_password) or die "$DBI::errstr";
my $dsn = $dbh->prepare(qq{SELECT `firstname` FROM `users` ORDER BY ABS(UserID) DESC LIMIT ?,?});
$dsn->execute('1','1') or die "$DBI::errstr";
while(#nrow = $dsn->fetchrow_array()) {
$firstname = $nrow[0];
}
I assumed since I was using DBI that may provide the best solution but, I am inexperienced obviously and need some advice and guidance to learn the proper way to do this. Thanks for any assistance.
You mention that UserID is not auto incrementing, so I'm not sure if last_insert_id will work in this situation. It may, but I'm just not sure. The document states:
Typically this would be a value assigned by the database server to a
column with an auto_increment or serial type.
I would look to solve this by just using a SQL statement:
SELECT
u.firstname
FROM
users u
JOIN
(
SELECT
MAX(UserID) AS UserID
FROM
users
) t ON u.UserID = t.UserID
The code with DBI would then look like this:
my $stmt = 'SELECT u.firstname FROM users u JOIN(SELECT MAX(UserID) AS UserID FROM users) t ON u.UserID = t.UserID';
my $first_name = ($dbh->selectrow_array($stmt))[0];
last_insert_id method takes 4 args. Use like this:
my $id = $connection->last_insert_id(undef, 'myschemaname', 'mytablename', 'mycolumnname');
See the DBI pod.

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.