Go here https://www.mysqltutorial.org/?s=left+join
Click the tutorial links and really study the differences in a right join vs a left join.
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
I've been running into this problem for a while now. I've dramatically reduced the loading time of this query from 30s to 3s but it's still taking to long in my opinion. This query is used in our chatbot, to get a response for a customer.
It's all about this Eloquent query:
/**
* Only find sentences that are allowed to say back.
* 1. WHERE NOT EXISTS sentences said by this bot said in the past 2 weeks
* 2. WHERE NOT EXISTS sentences said in the last 8 minutes by any bots
* 3. WHERE NOT EXISTS sentences said to this customer
* 4. WHERE EXIST sentences with [$translation] translation
*/
$sentences = DB::table('sentences')
->where('keyword_id', '=', $keyword)
->whereNotExists(function ($query) use ($bot, $customer, $twoWeeksAgo) {
$query->selectRaw('id')
->from('customer_sentences')
->whereRaw('sentences.id = customer_sentences.sentence_id')
->whereRaw('customer_sentences.bot_id = ?', $bot->id)
->whereRaw('customer_sentences.created_at >= "' . $twoWeeksAgo .'"');
})
->whereNotExists(function ($query) use ($eightMinutesago) {
$query->selectRaw('id')
->from('customer_sentences')
->whereRaw('sentences.id = customer_sentences.sentence_id')
->whereRaw('customer_sentences.created_at >= "' . $eightMinutesago . '"');
})
->whereNotExists(function ($query) use ($customer) {
$query->selectRaw('id')
->from('customer_sentences')
->whereRaw('sentences.id = customer_sentences.sentence_id')
->whereRaw('customer_sentences.customer_id = ?', $customer->id);
})
->whereExists(function ($query) use ($translation) {
$query->selectRaw('id')
->from('sentence_translations')
->whereRaw('sentence_translations.sentence_id = sentences.id')
->whereRaw('sentence_translations.language_id = ?', $translation);
})
->get();
I've stripped the query down, and found out it is this line which is taking long to execute:
->whereNotExists(function ($query) use ($eightMinutesago) {
$query->selectRaw('id')
->from('customer_sentences')
->whereRaw('sentences.id = customer_sentences.sentence_id')
->whereRaw('customer_sentences.created_at >= "' . $eightMinutesago . '"');
})
->
I was then thinking to use a left join (i've very less experience with this):
$sentences = DB::table('sentences')
->where('keyword_id', '=', $keyword)
->whereNotExists(function ($query) use ($bot, $customer, $twoWeeksAgo) {
$query->selectRaw('id')
->from('customer_sentences')
->whereRaw('sentences.id = customer_sentences.sentence_id')
->whereRaw('customer_sentences.bot_id = ?', $bot->id)
->whereRaw('customer_sentences.created_at >= "' . $twoWeeksAgo .'"');
})
->leftJoin('customer_sentences', function ($query) use ($eightMinutesago) {
$query->on('sentences.id', '=', 'customer_sentences.sentence_id')
->where('customer_sentences.created_at', '=', $eightMinutesago);
})
->whereNull('customer_sentences.id')
->whereNotExists(function ($query) use ($customer) {
$query->selectRaw('id')
->from('customer_sentences')
->whereRaw('sentences.id = customer_sentences.sentence_id')
->whereRaw('customer_sentences.customer_id = ?', $customer->id);
})
->whereExists(function ($query) use ($translation) {
$query->selectRaw('id')
->from('sentence_translations')
->whereRaw('sentence_translations.sentence_id = sentences.id')
->whereRaw('sentence_translations.language_id = ?', $translation);
})
->get();
The above query is running much faster, but I'm unsure this is the way to go.
I am already having the following indexes:
$table->index(['created_at', 'bot_id']);
$table->index(['bot_id']);
$table->index(['customer_id']);
$table->index(['bot_id', 'created_at', 'sentence_id']);
I am really wondering how to solve this problem. I am stuck on this for a long time now.
Please or to participate in this conversation.