MySQL query to pull only the latest record - mysql

I am creating a simple chat module where there are two levels.
Inbox
Conversation
in the Inbox i want to list the conversation between me and other users and display the latest chat message from that particular user.
messages(id, user_id, receiver_id, message, timestamp)
Dummy data
1, 1, 2, "hi there", today
2, 1, 4, "Hey Tommy", yesterday
3, 1, 2, "Wanna meet tonite?", now
Now when i display the list i want to just display two messages
one with user id - 4 and user id 2. But the latest message of userid 2 should be displayed only.
How to do a query where i can get only the latest row from a particular user ?

SELECT *
FROM (SELECT *
FROM `messages`
WHERE `user_id` = 1
ORDER BY `timestamp` DESC) TT
GROUP BY TT.`receiver_id`;
Hope the above helps!

This is beleive is an efficient way to acheive this:
SELECT M1.* FROM Messages M1 LEFT JOIN Messages M2
ON M1.ReceiverID = M2.ReceiverID AND M1.Timestamp < M2.Timestamp
WHERE M2.ID IS NULL

I would try it like this:
$user_id = 2;
$latestmessage = $this->Message->find('first', array(
'conditions' => array(
'user_id' => $user_id
),
'order' => 'id DESC'
));

This might work:
$latest_per_user_id = $this->Message->find('all', array(
'fields'=>'DISTINCT user_id',
'order' => 'timestamp DESC'
));
EDIT: If you just need info for one user you can do this:
$latest_msg_by_user = $this->Message->find('first', array(
'conditions'=>array('user_id'=>$user_id),
'order' => 'timestamp DESC'
));
You could put this in a loop to get it for each user.

Related

Not equal condition with findAll()

I am trying to pull record from a table using the following code
$userId = Yii::$app->user->id;
$lists = PromoLists::findAll(['user_id' => $userId, 'list_type' => 'custom']);
which outputs a query like below
select * from promo_lists where user_id ='$userId' and list_type='custom'
But i am unable to find any thing in the documentation that would help me achieve it with the following condition.
select * from promo_lists where user_id ='$userId' and list_type='custom' and status!='deleted'
as the status is an ENUM field and there are 4 different status
'active','pending','rejected','deleted'
currently i used the following approach
PromoLists::findAll(['user_id' => $userId, 'list_type' => 'custom', 'status'=>['active','pending','rejected']]);
which outputsthe following query
select * from promo_lists where user_id ='$userId' and list_type='custom' and status in ('active','pending','rejected')
which somehow achieves the same thing but this query would need to be edited every time when there is a new status type added to the table column status.
i know i can do this by using PromoLists::find()->where()->andWhere()->all()
but how to check with != / <> operator using findAll().
Simply like this:
PromoLists::find()->where(['and',
[
'user_id' => $userId,
'list_type' => 'custom',
],
['<>', 'status', 'deleted'],
])->all();
Using operator format in condition
http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html#operator-format
PromoLists::find()
->andWhere([
'user_id' => $userId,
'list_type' => 'custom',
['!=', 'status', 'deleted']
])
->all();

Cakephp find joining two columns

I have a BD that contains people with day and month fields, like so:
person1 (day = 5; month = 3)
person2 (day = 2; month = 12)
I have to perform a find between 2 dates, for example, I need all people between 01/03 and 01/06 (day/month) but I don't know how to perform that.
I tried using separate conditions, like this:
$conditions['People.day >='] = dayA;
$conditions['People.month >='] = monthA;
$conditions['People.day <='] = dayB;
$conditions['People.month <='] = monthB;
But, that's not correct because it finds day and month, I mean, it finds People between monthA and monthB and People between dayA and dayB, instead, what I need is people between dayA/monthA and dayB/monthB
I suppose I must do some kind of a JOIN, but I'm lost here, I looked some information but I don't know where to start.
updated info:
ok, I'm using this
Array
(
[People.month_day BETWEEN ? AND ?] => Array
(
[0] => 01/02
[1] => 28/02
)
)
but I get people like this:
1/1
2/1
10/1
11/1
12/1
13/1
20/1
21/1
1/2
what's wrong? do you need more code? I'm using this to retrieve results:
A paginate:
public $paginate = array('People'=>array(
'limit' => 16,
'order' => 'People.month, People.day ASC'
));
In your People model
public $virtualFields = array(
'month_day' => 'CONCAT(People.month, "-", People.day)'
// 'month_day' => 'CONCAT(People.day, "/", People.month)'
);
then add in your controller
$options = array(
'conditions' => array(
'Post.month_day BETWEEN ? AND ?' => array('01-03','01-06')
// 'Post.month_day BETWEEN ? AND ?' => array('03/01','06/01')
)
);
$posts = $this->Post->find('all',$options);
It would be best to alter your table. Use DATE field and your SQL query would be:
SELECT *
FROM dbname.People
WHERE People.date BETWEEN '1999-03-01' AND '1999-06-01';

How to write MySQL query in CakePHP format?

i have faced in a problem.
Can you please write this query in cakephp format
Table
id from_id to_id is_active message
--- ------- ----- --------- -------
1 1 8 1 Hello
2 8 1 1 Yes
3 1 8 1 How are you?
4 1 8 1 Are you with me?
Code :
<?php
$qry = mysql_query("SELECT * FROM messages WHERE (from_id='8' OR to_id='1') AND (from_id='1' OR to_id='8') AND id_active='1'"));
$res = mysql_fetch_array($qry);
?>
Edit
I have written but getting null value
my code:
$condition = array('OR'=>array('Message.from_id'=>1,'Message.to_id'=>8),'OR'=>array('Message.from_id'=>8,'Message.to_id'=>1),'Message.is_active'=>1);
$message=$this->Message->find("all",array('conditions'=>$condition));
pr($message);
Output:
array
(
)
Hi if you would like to list the conversation try out this:
$this->Message->find('all' ,
array(
'conditions'=>array(
'AND'=>array(
'OR'=>array(
array('AND'=>array('from_id'=>1, 'to_id'=>'8')),
array('AND'=>array('from_id'=>8, 'to_id'=>'1'))
),
'is_active'=>1
)
)
)
);
The good query is:
SELECT Message.id, Message.from_id, Message.to_id, Message.is_active, Message.message FROM c23dev1.messagess AS Message WHERE ((((((from_id = 1) AND (to_id = 8))) OR (((from_id = 8) AND (to_id = 1))))) AND (is_active = '1'))
I can recommend using Dogmatic69's converter. That gave this result for your query:
'conditions' => array(
array(
'or' => array(
'from_id' => '8',
'to_id' => '1',
),
),
'id_active' => '1',
),
I'm not sure what your original SQL query is actually trying to return, but if you want all messages between user 7 and user 8, your query should be something like:
SELECT *
FROM messages
WHERE ((from_id='7' AND to_id='8') OR (from_id='8' AND to_id='7'))
AND is_active='1';
Here's a SQLFiddle example.

Message list: order by and group by query on cakephp and mysql

I want to change my message list page/inbox. Currently it shows all the messages from the DB like if someone send me a message 3 times, it will all appear in my inbox. Now i want to make it simple like every sender will appear only once in the list and will show the latest message that he/she sent.
This is my code in cakephp:
$this->paginate = array(
'Message'=> array(
'conditions'=>array('Message.recipient_id'=>$this->user_id),
'group' => array('Message.sender_id'),
'order'=>'message_sent_date DESC',
'limit'=> 20,
)
);
The problem with this code is it shows the oldest message content. I think it's because group by always first before order by.
I was able to do it on mysql query but i don't know how to make it on cakephp.
Here's my query:
SELECT *
FROM(
SELECT *
FROM messages WHERE recipient_id = 114
ORDER BY message_viewed_date DESC
) AS messages
GROUP BY sender_id
ORDER BY message_viewed_date DESC
Please help me with this.. Thanks.
This should do the trick (make sure your models are set up correctly):
$results = $this->Message->find('all', array(
'conditions' => array(
'recipient_id' => 114
),
'group' => 'sender_id',
'order' => 'message_viewed_date DESC'
));
No need for the sub query there, it's not doing anything fancy. The manual will help you with queries like this.
I've solve this problem using the Model::query() http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-query
here's my code:
$messages = $this->Message->query("SELECT id FROM(SELECT * FROM messages WHERE recepient_id = ". $this->user_id ." ORDER BY message_sent_date DESC) AS Message GROUP BY sender_id ORDER BY message_sent_date DESC");
for($i=0; $i<count($messages); $i++){
$message_id[$i] = $messages[$i]['Message']['id'];
}
$this->paginate = array(
'Message'=> array(
'conditions'=>array('Message.id' => $message_id, 'Message.recepient_id'=>$this->user_id, 'Message.hidden_on_to'=>false),
'limit'=>20,
'order'=>array('Message.message_sent_date' => 'DESC'),
'recursive'=>0,
'fields'=>array('Message.id', 'Message.message_content', 'Message.message_sent_date', 'Message.message_viewed_date', 'Sender.id', 'Sender.username')
)
);

CakePHP has and belongs to many conditions with NOT EXISTS

$conditions = Array
(
[table] => products_pages
[alias] => ProductsPage
[type] => inner
[foreignKey] =>
[conditions] => Array
(
[0] => ProductsPage.product_id = Product.id
)
)
I'm trying to set up NOT EXISTS conditions, like the following SQL statement:
SELECT * FROM products_pages,products
WHERE NOT EXISTS (SELECT id
from products_pages
where products_pages.product_id = products.id)
So basically select any product that doesn't exist in the products_pages table.
What is the proper way to format that SQL statement for CakePHP and replace it here:
[conditions] => Array
(
[0] => (What's the proper way to insert above SQL here?
)
Would really appreciate your help guys, I've been trying to figure this out for about 5 hours with no luck. Thanks!
You can always use query if you don't find the way to do it with CakePHP:
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-query
In this case security wouldn't be compromised as you are not using any input.
Anyway, something simple would be just to do it in more than one step:
//selecting the products in the productcs_pages table
$productsWithPages = /* query to get them*/
//getting an array of IDs
$productsWidthPagesIds = Hash::extract($productsWithPages, '{n}.Product.id');
//doing the NOT IN to select products without pages
$productsWithoutPages= $this->Product->find('all',
array('conditions' =>
array( 'NOT' => array('Product.id' => $productsWidthPagesIds )
)
);