MySQL: How do I retrieve n (number of) data from an array? - php

I have a messages table.
+----+---------+----------+
| id | conv_id | body |
+----+---------+----------+
| 1 | 1 | haha |
| 2 | 1 | blabl|
| ...| ... | ... |
| 25| 2 | hehe |
+----+---------+----------+
... = rest of messages with conv_id of 2's or 1's or 3's or n's.
Let's say I have conv_id = array(2,1) and I want to obtain 10 messages after matched with an array of ids in conv_id so I did
select * from `messages` where `conv_id` in (2, 1) order by `created_at` desc limit 10
The sql above gave me 10 messages after matching both conv_ids and getting all combined messages. However, this is NOT what I wanted. Instead, I wanted 10 messages of EACH conv_id matched.
How do I get 10 messages of EACH conv_id matched? No PHP for loop, please. Thank you!
NOTE : the array conv_id can easily be extended to include many other values unique to each other, not only 2s or 1s.
P.s., bonus points for Laravel Eloquent answer! Here are the details :
Two models, Conversations and Messages linked by Conversations hasMany Message and Message belongsTo a Conversation.
My sql above was translated from Messages::with('User')->whereIn('conv_id',$conv_id)->orderBy('created_at','desc')->take(10);

I think Jared is right but if you can add another column to the table, solution would be more efficient. Add a column which indicates message number for each conv_id (earliest will have 1 and the latest will have number of messages conversation have). After that, you can achieve your goal by scanning table twice with HAVING clause.
SELECT * FROM messages JOIN
(SELECT conv_id, MAX(msg_no) FROM messages WHERE conv_id IN (2,1) GROUP BY conv_id) as M
ON messages.conv_id=M.conv_id HAVING messages.msg_no > M.msg_no-10

Another possibility. Get last conv_ids and their 10th (message)id with a group_concat - substring_index trick and re-join of message-table.
SELECT `messages`.*
FROM (
SELECT
conv_id,
substring_index(
substring_index(
group_concat(`messages`.id),
',',
10
),
',',
-1
) AS lastMessageId
FROM `messages`
WHERE `conv_id` in (2, 1)
GROUP BY `conv_id`
) AS msub
INNER JOIN `messages` ON `messages`.conv_id = msub.conv_id AND `messages`.id <= msub.lastMessageId

Warning: This approach is potentially wrong. I'm trying to validate it and will update it once I reach a conclusion
Yesterday I just learnt something new about Eloquent relationship from an answer by deczo in Getting just the latest value on a joined table with Eloquent. And I think we can adapt it to your case as well.
What you're essentially trying to do, I put in my view as:
A Conversation model that has many Messages.
But I want only 10 latest messages per each conversation. All come eager-loaded.
In Eloquent, I would probably do something along the line of:
Conversation::with('messages')->whereIn('conv_id', [1, 2])->get();
But two things I note of your question.
I assume you don't have another table called conversations.
The code above does not limit to how many messages per conversation.
So I decided that here should be two models, one called Message, another called Conversation.
Both call to the same table, but we will tell Eloquent to use different columns as primary key.
So to get a Conversation model, I added a distinct() to my newQuery function, and select only conv_id out since that's the only information about a conversation.
So in the end I have this:
models/Message.php
class Message extends Eloquent
{
protected $table = 'messages';
protected $primaryKey = 'id';
public $timestamps = false;
}
models/Conversation.php
class Conversation extends Eloquent
{
protected $table = 'messages';
protected $primaryKey = 'conv_id';
public $timestamps = false;
public function newQuery($excludeDeleted = true)
{
return parent::newQuery()
->select('conv_id')
->distinct();
}
public function messages()
{
return $this->hasMany('Message', 'conv_id')
->orderBy('id', 'desc')
->take(10);
}
}
Now I can do:
Conversation::with('messages')->whereIn('conv_id', [1, 2])->get();
which gives me a list of conversations with conv_id 1 and 2, along with the 10 latest messages for each of them.
This is the first time I do something like this. So I code-tested and it works.
Update: The code in my answer perform these two queries only (retrieved from DB::getQueryLog()):
select distinct `conv_id` from `messages` where `conv_id` in (?, ?);
select * from `messages` where `messages`.`conv_id` in (?, ?) order by `id` desc limit 10;

Related

Count from selection and access both tables with joins

I stumbled upon a query that I have never done until now.
Before asking the question I looked for if another user had had the same need as me but nothing.
My goal is very simple:
having two tables:
collaboratori (collaborators)
invite (invitations)
I have to count how many invitations the collaborators have made.
table structure of collaboratori:
ID_Collaboratori | cod_manager
37 4675
150 6675
3 6575
table structure of inviti:
invite_id | invite_code_manager
37 6675
39 6575
40 4675
41 6675
if I execute the join obviously I access the two tables in this way:
$q_stats_prod_manager = $connessione->prepare("
SELECT * FROM invite
LEFT JOIN collaboratori
ON collaboratori.cod_manager = invite.invite_code_manager ");
$q_stats_prod_manager->execute();
$r_stats_prod_manager = $q_stats_prod_manager->get_result();
my need lies in showing in a table:
show me for each manager who has his cod_manager inside the inviti table, the number of times he sent them.
Name Surname Manager 1 | Number of invite: 200
Name Surname Manager 2 | Number of invite: 50
Name Surname Manager 3 | Number of invite: 10
not limiting myself to just one counter but also being able to access other table values ​​like any join
I take the liberty of putting the answer that was partially written by another user, adding a detail and explanation for future users. The resolution query for this case is the same:
$q_stats_prod_manager = $connessione->prepare("
SELECT count(invite.invite_id)
/*name of what you want to call the result you will see in the while*/
AS result_count, /*you can call this value whatever you want*/
/*Start | Values ​​of the tables you are interested in selecting*/
collaboratori.nome,
collaboratori.data_registrazione,
invite.invite_code_manager
/*End | Values ​​of the tables you are interested in selecting*/
FROM collaboratori
LEFT JOIN invite
ON invite.invite_code_manager = collaboratori.cod_manager group by invite.invite_code_manager
");
$q_stats_prod_manager->execute();
$r_stats_prod_manager = $q_stats_prod_manager->get_result();
$count_invite_manager=mysqli_fetch_array($r_stats_prod_manager);
$number_of_invite_manager = $count_invite_manager[0];
Select the id of the table you want to count
Give a name you wish you want to name the counted result
Select the values ​​of the tables on which you will perform the join you want to view
Join the tables
Show the result with while
Code while:
<?php while($rowstatspm = mysqli_fetch_assoc($r_stats_prod_manager)){ ?>
<!-- this is the fancy name you associated with your query when you wrote: AS nameofwhatyouwant -->
<?php echo $rowstatspm['result_count'] ;?>
<?php } ?>
You seem to want aggregation. I assume you want all rows for collaboratori, so that should be the first table for the LEFT JOIN:
SELECT c.cod_manager, COUNT(i.invite_code_manager)
FROM collaboratori c LEFT JOIN
invite i
ON c.cod_manager = i.invite_code_manager
GROUP BY c.cod_manager;
Your question doesn't describe where the name comes from. But those fields should be in both the SELECT and GROUP BY.
SELECT count(invite.invite_id),collaboratori.name FROM collaboratori
LEFT JOIN invite
ON invite.invite_code_manager = collaboratori.cod_manager group by invite.invite_code_manager

Mysql join three table/ Laravel query

I have three tables Beneficiary, Questions, Responses..Beneficiary
Benficiary
id name
1 user1
2 user2
3 user3
Questions
id question type
1 what is your hobby hobby
2 address contact
3 phone contact
..questions upto 500 and i need to select according to its type
Response
id beneficiary_id question_id response
1 1 1 football
2 1 3 5354353
3 2 1 basketball
4 3 2 cityname
5 3 3 432423
Beneficiary table and Question table are static but response table joins both of them. I tried to do this from laravel query but it gives me only responses
sql query
select response,questions.question, beneficiarys.name
from responses RIGHT join (questions,beneficiarys)
on beneficiarys.id = responses.beneficiary_id && questions.id = responses.question_id
where questions.sheet_type = 'SCDS' OR questions.sheet_type = 'SMM'
laravel query
$beneficiaries = Beneficiary::with('quests','response')->whereHas('response', function ($query)use($sheet_type) {
$query->where('beneficiary_id', '>', 0);
})
->whereHas('quests',function($query)use($sheet_type){
$query->where('questions.sheet_type','=',$sheet_type);
$query->orWhere('questions.sheet_type','=','SCDS');
})
->where('updated_at','>=',!empty($this->request->from_date)?$this->request->from_date:$from)->where('updated_at','<=',!empty($this->request->to_date)?$this->request->to_date:$date)->get();
Beneficiary Model
public function response()
{
return $this->hasMany('App\Model\Response');
}
public function quests(){
return $this->belongsToMany('App\Model\Question', 'responses','beneficiary_id','question_id');
}
Question Model
public function response(){
return $this->hasMany('App\Model\Response');
}
public function bens(){
return $this->belongsToMany('App\Model\Bebeficiary', 'responses','question_id','beneficiary_id');
}
Response Model
public function beneficiary(){
return $this->belongsTo('App\Model\Beneficiary', 'beneficiary_id');
}
public function question(){
return $this->belongsTo('App\Model\Question', 'question_id');
}
I need all the question according to its type(type is selected by user from view) and i need output like this
beneficary hobby address phono ....
user1 football null 4244 ....
user2 basketball null null ....
is there any way to achieve it with sql or laravel query*
this is the mysql select, maybe is a better way to it but this work
SELECT b.name,hobby.response as hobby,address.response as addres,phone.response as
phone FROM beneficiary as b
left join (select * from response where question_id=1 ) as hobby
on hobby.beneficiary_id = b.id
left join (select * from response where question_id=2 ) as address
on address.beneficiary_id = b.id
left join (select * from response where question_id=3 ) as phone
on phone.beneficiary_id = b.id
You Will get result from this query. Laravel Support customization as well.
$sSQL = "SELECT * FROM RESPONSE
INNER JOIN QUESTION ON QUESTION.id=RESPONSE.QUESTION_id
INNER JOIN BENEFICIARY ON BENEFICIARY.id=RESPONSE.BENEFICIARY_id";
DB::query($sSQL);
Try
$res = DB::table('responses')
->join('questions','responses.question_id','=','questions.id')
->join('beneficiarys','responses.beneficiary_id','=','beneficiarys.id')
->select('responses.*','questions.question','beneficiarys.name')
->where('questions.sheet_type', '=','SCDS')
->orWhere('questions.sheet_type','=','SMM')
->get();
Use the result as
foreach($res as $res)
{
echo $res->name;
}
If you want to do it Eloquent way you could try using whereHas on your nested relationships. Try something like :
Beneficiary::with('response.question')->whereHas('response.question')
For more references try this link Eloquent with nested relationships

Complex MySQL Conversation Group Query?

I have the following tables.
conversations
| id |
------
1
and
conversationMembers
| id | conversationId | userId | email
---------------------------------------
1 1 2 null
2 1 null test#test.com
3 1 7 null
Basically, I'm trying to construct a MySQL query that returns a row from the conversations table by an exact match of conversationMembers.
So, here's some examples of expected returns.
Let's say we want a a conversation id for a conversation between the exact following members: userId 2, userId 7, email test#test.com - This would see that in the conversationMembers table there's rows with the same conversation id and the exact match across all members of that conversation id that we're searching for. It would return conversations row with id 1.
Here's another example. We want a conversation id for a conversation between userId 2 and userId 7. This would see that there's not a conversation exclusively between userId 2 and userId 7, so it would not return anything.
And a final example. Let's say we want userId 7 and userId 9, this would also see there's no exclusive conversation between these 2 user id's and would return nothing.
What's the best way to go about doing it? I've played with subqueries but everything I've come up with doesn't seem to be able to handle the exact matching situation - I was having issues with selecting conversations for example - on userId 2 and 7 only (which should return nothing) and was getting conversationId 1 back, even though I didn't specify I wanted a conversation with test#test.com email as a part of it. I should only have gotten conversationId 1 back for if I searched on an exact match of all members in for conversationId.
One method is to use group by and having. This is nice because it is flexible with regards to what can be expressed. So, your first example is:
select conversionid
from conversationMembers
group by conversionid
having sum(userId = 2) > 0 and
sum(userId = 7) > 0 and
sum(email = 'test#test.com') > 0;
The condition being summed counts the number of members that match. The > 0 means there is at least one. For the second condition, the clause would be:
having sum(userId = 2) > 0 and
sum(userId = 7) > 0 and
sum(userId not in (2, 7)) = 0;
or alternatively:
select conversionid
from conversationMembers
group by conversionid
having sum(userId = 2) > 0 and
sum(userId = 7) > 0 and
count(distinct userId) = 2;

Grouping BY Same Fields and Showing As 1 Division

I have a collection returning messages, however, I want to group them by 'subjects' and 'sender' to create 1 div for each subject, and show the latest one's created_at.
So I have:
id | senderId | receiverId | subject | message | created_at
My Controller:
$messages = Message::where('receiverId', \Auth::user()->name)
->orderBy('created_at','desc')
->paginate(10);
return view('chat')->with(compact('messages'));
So this works: dd($messages[0]->subject). Also, in my view, I can use #foreach to use it as $message->created_at. Currently it seems like this (subjects are not listed):
*yIZgT0oqH - is name
How can I group them by subject and senderId, and show them as 1 group(1 div) by using the latest message (created_at)? What is the best way?
You can modify your query to do something like this, using groupBy / aggregation.
$messages = Message::where('receiverId', \Auth::user()->name)
->groupBy('subject')
->orderBy('created_at','desc')
->paginate(10);
Check the documentation for the same - http://laravel.com/docs/5.1/queries#ordering-grouping-limit-and-offset

Count Relational Table Rows

I'm trying to count the number of rows in a relational table many to many, but always returns the wrong value. When it is 1, always returns 2.
PS: All models and foreign keys in mysql are configured correctly.
Comments Table:
id | name
10 Comment Test
Users Table:
id | name
20 User Test
Likes (Comment/User) Many to Many:
user_id | comment_id
20 10
Code:
$criteria = new CDbCriteria;
$criteria->select='*, COUNT(likes.id) AS count_likes'; // I believe the error is in the use of COUNT (likes.id).
$criteria->with=array('likes'=>array('on'=>'user_id=20'));
$model = Comments::model()->findByPk($_GET['id'], $criteria);
// Return Wrong Value
echo $model->count_likes; // Return 2 where should be 1. (I need to use this case)
echo count($model->likes); // Return right value 1.
You should use Statistical Query, e.g. :
In your Comments model :
public function relations()
{
return array(
// ...
// I assume your relation table's name is "likes"
'likes'=>array(self::MANY_MANY, 'Users', 'likes(comment_id, user_id)'),
'likesCount'=>array(self::STAT, 'Users', 'likes(comment_id, user_id)'),
// ...
);
}
Are you planning on selecting from the LIKES table and group by them by their userid and comment id?
If so, you can use GROUP BY
Please take a look at the SQLFiddle here
http://sqlfiddle.com/#!2/bc29b8/1/0
SELECT uid, cid, COUNT(likes.cid) FROM likes GROUP BY uid, cid

Categories