I am using laravel eloquent and have condition where thousands of records inside database so when using eloquent relationship my query is executing slow. Should i avoid using eloquent in these satuations or any other way?
here is mysql query
$leads=Lead::select($col)
->join("gc_od_leads_detail as ld", "gc_od_leads.leads_id", "=", "ld.ld_leads_id")
->join("gc_od_chat as c", "gc_od_leads.leads_chat_id", "=", "c.chat_id")
->join("gc_od_group as g", "c.chat_group_id", "=", "g.group_octachat_id")
->where('c.chat_tags','sales')
->whereIn('c.chat_group_id',$filter['groups']);
if(!empty($filter['keyword'])) {
$leads=$leads->where(function ($q) use ($filter) {
$q->where('ld_name','like', "%".$filter['keyword']."%")
->orWhere('ld_email','like', "%".$filter['keyword']."%")
->orWhere('ld_phoneno','like', "%".$filter['keyword']."%");
});
}
if(!empty($filter['startDate']) && !empty($filter['endDate'])){
$leads=$leads->whereBetween('leads_created_date', [$filter['startDate']." 00:00:00",$filter['endDate']." 23:59:59"]);
}
$leads=$leads->orderBy('leads_created_date','desc');
return $leads;
}
I have more than 500 000 recordings in side messages and chats table. i changed query in eloquent and debugged it
Query:
Lead::select('leads_id','leads_chat_id')->with(["detail"=>function($q){
$q->select("ld_leads_id");
}])->with(["chat"=>function($q){
$q->select("chat_id")->where(['chat_status'=>1]);
}])->where("leads_status",1)->get();
Debuging Ouput
array:3 [▼
0 => array:3 [▼
"query" => "select `leads_id`, `leads_chat_id` from `gc_od_leads` where `leads_status` = ?"
"bindings" => array:1 [▼
0 => 1
]
"time" => 14.85
]
1 => array:3 [▼
"query" => "select `ld_leads_id` from `gc_od_leads_detail` where `gc_od_leads_detail`.`ld_leads_id` in (2278918, 2278919, 2278920, 2278921, 2278922, 2278923, 2278924, 22789 ▶"
"bindings" => []
"time" => 0.59
]
2 => array:3 [▼
"query" => "select `chat_id` from `gc_od_chat` where `gc_od_chat`.`chat_id` in (3496457, 3496458, 3496459, 3496460, 3496461, 3496462, 3496463, 3496464, 3496465, 3496466, 34 ▶"
"bindings" => array:1 [▶]
"time" => 4.21
]
]
i above output you can see that it get all records of leads first then going to lead detail and chats table if i only want to find out the leads having chat status =1 it will still query all leads this is what slowing my query
where we use join it will not work in this way i think which will save time and space both that's my i post this question i think a lot of people having same problem and no one discuss this point
Let's take a look at one part of this.
if(!empty($filter['keyword'])) {
$leads=$leads->where(function ($q) use ($filter) {
$q->where('ld_name','like', "%".$filter['keyword']."%")
->orWhere('ld_email','like', "%".$filter['keyword']."%")
->orWhere('ld_phoneno','like', "%".$filter['keyword']."%");
});
}
This keyword-matching scheme is inherently, and disastrously, slow. It's slow in both Eloquent and native SQL. There's no way it can work in MySQL without doing a full table scan. That is, it must examine every row of your table looking for matches and cannot, in MySQL, exploit any indexed lookup scheme. Why?
column LIKE 'constant%'
can look at an index on column and quickly find any value starting with 'constant'. But
column LIKE '%constant%'
has to look at every value in the table. The leading % makes the index lookup useless.
In MySQL you would be wise to investigate MySQL's FULLTEXT searching as a way of handling your keyword lookup. (Recent versions of postgreSQL can handle this sort of query directly with a different kind of index, but not MySQL.)
Related
Trying to get matching id's from a table and inserting them again in the same table under differnet relationship.
$contentPack = ContentPack::find($id);
$cloned_pack_goals = DB::table('content_pack_goal')->where('content_pack_id' , $contentPack->id)->get();
$cloned_pack_goal_ids = $cloned_pack_goals->goal_id;
Produces Exception
Exception
Property [goal_id] does not exist on this collection instance.
dd($cloned_pack_goals); outputs:
Illuminate\Support\Collection {#2466 ▼
#items: array:2 [▼
0 => {#3129 ▼
+"goal_id": 4
+"content_pack_id": 2
}
1 => {#2467 ▼
+"goal_id": 9
+"content_pack_id": 2
}
]
}
How to get goal_ids from the output to insert them into the same table again but with a different relation?
$newPack = $contentPack->replicate();
DB::table('content_pack_goal')->insert(['content_pack_id' => $newPack->id,'goal_id' => $cloned_pack_goal_ids]);
Am doing something wrong when getting the ID's and when inserting them. tried using ->first(); it works but only one id gets inserted
$cloned_pack_goals is a collection, so you need to exclude goal_ids from all collection records separately.
This snippet may help you:
$cloned_pack_goal_ids = DB::table('content_pack_goal')->where('content_pack_id' , $contentPack->id)->pluck('goal_id')->toArray();
foreach($cloned_pack_goal_ids as $key => $goal_id) {
DB::table('content_pack_goal')->insert(['content_pack_id' => $newPack->id,'goal_id' => $goal_id]);
}
To get an array of only the Ids, use pluck() and toArray()
$cloned_pack_goal_ids = DB::table('content_pack_goal')
->where('content_pack_id' , $contentPack->id)
->pluck('goal_id') // returns a collection of only Ids.
->toArray(); // returns an array from the collection.
Write your query in this format this will give you the require output:
$cloned_pack_goals = DB::table('content_pack_goal')->where('content_pack_id' , $contentPack->id)->get()->toArray();
$cloned_pack_goal_ids = $cloned_pack_goals[0]->goal_id;
I am trying to apply several filters to one dql in Symfony using Doctrine. I want order it by several columns (although for the moment I am having problems just with one column) and I want show first values that matches with specific values. I will write a simple example to illustrate it about the result that I am searching:
col1 col1
---- -----
A B
B => A
C C
W W
I was searching information about how to make it but I am a bit confused due to that some people says that I can't do it directly, other people says that it is possible using case when or if/else. I tried to use case when but without success. The code that I am using is the following:
Updated code and error
$query = $this->createQueryBuilder('article')
->where('article.title LIKE :article')
->setParameter('title', '%'.$term.'%')
->addSelect('(CASE WHEN article.to = \'WOR\' THEN 1 ELSE 0 END) AS to')
->addOrderBy('article.to', 'ASC')
->getQuery();
And if I want set the "B" value as parameter, should I use setParameter after the addSelect?
Right now with the abovecode I am getting the following error:
Key "title" for array with keys "0, to" does not exist in result.html.twig at line 177.
Information related about how I call this method into my controller and pass it to twig template:
$prodMan = $this->get('app.article.manager');
$articles = $prodMan->getResults((string)"t", $page);
$limit = 50;
$maxPages = ceil($articles->count() / $limit);
$thisPage = $page;
return $this->render('result.html.twig', compact('categories', 'maxPages', 'thisPage', 'articles', 'images'));
And the twig template where I have the error:
<td><p>{{articles.title}}></p></td>
Result of {{ dump(articles) }}
Paginator {#475 ▼
-query: Query {#484 ▼
-_state: 2
-_dql: "SELECT article, (CASE WHEN article.to = 'WOR' THEN 1 ELSE 0 END) AS to FROM AppBundle\Entity\Article article WHERE article.title LIKE :title ORDER BY article.to ASC"
-_parserResult: null
-_firstResult: 0
-_maxResults: 50
-_queryCache: null
-_expireQueryCache: false
-_queryCacheTTL: null
-_useQueryCache: true
#parameters: ArrayCollection {#483 ▼
-elements: array:1 [▼
0 => Parameter {#480 ▼
-name: "title"
-value: "%t%"
-type: 2
}
]
}
#_resultSetMapping: null
#_em: EntityManager {#105 …10}
#_hints: []
#_hydrationMode: 1
#_queryCacheProfile: null
#_expireResultCache: false
#_hydrationCacheProfile: null
}
-fetchJoinCollection: true
-useOutputWalkers: null
-count: 143
}
I executed the same dql query without the case when (with that dql I haven't any problem), and I compare the dumps in Twig and the only difference that I see is that in the other dql I am getting #483 and #482 indexes instead of #484 and #480 respectly
And I can't var_dump any position of the array, but the array has the right number of results, although I can't check if results are sorted in the right way
I am stuck with this problem and if someone could lead me to the right answer I'd be very grateful!
I'm not sure if your query will work, but what if you change this line like so:
->addSelect('(CASE WHEN article.to = \'B\' THEN 1 ELSE 0 END) AS HIDDEN to')
Escaping the quote, or:
->addSelect("(CASE WHEN article.to = 'B' THEN 1 ELSE 0 END) AS HIDDEN to")
That way the B is in quote. Again, not sure about the query itself.
EDIT #2 - Based on dump of articles
Looks like the dump is still a query.
You need to get the results like so:
$articles = $query->getResult();
Then pass the articles to you twig and render it.
Normally done like so:
return $this->render('result.html.twig', array(
'articles' => $articles,
));
See if that works.
You might need changes, but the above code gives you some idea of what to do.
I'm using route binding to determine if each part of the URL is related to the previous. I'm trying to access the route parameter/variable ($stage_number) in my query builder but no luck. To confuse things, if I substitute the variable with a hard value it works, e.g. 4
How do I use the $stage_number variable in my query?
/*
* Route
*/
Route::get('/application/{application_id}/stage/{stage_number}', [
'as' => 'application.stage',
'uses' => 'ApplicationController#stage'
]);
/*
* Route Service Provider
*/
// Application
$router->bind('application_id', function($application_id)
{
$application = Application::find($application_id);
if (! $application)
{
abort(404);
}
return $application;
});
// Stage
$router->bind('stage_number', function($stage_number)
{
$application = $this->getCurrentRoute()->application_id;
$stage = collect($application->stages)->where('order', $stage_number)->all();
if (! $stage)
{
abort(404);
}
return $stage;
});
Update in response to patricus:
Thanks for the information about calling where() on collections; I did not realise it handled differently to the query builder. Updating my where() for the collection works perfectly - thanks. However, I am still having trouble when using the query builder:
// Route with data
/application/1/stage/4
// Actual data returned
array:4 [▼
0 => array:2 [▼
"name" => "Information about the University or Education Course"
"order" => 3
]
1 => array:2 [▼
"name" => "Information about your education to date"
"order" => 4
]
2 => array:2 [▼
"name" => "Other Information"
"order" => 5
]
3 => array:2 [▼
"name" => "Declaration"
"order" => 6
]
]
// Desired data to be returned
array:1 [▼
0 => array:2 [▼
"name" => "Information about your education to date"
"order" => 4
]
]
Regardless of what order I specify in my route I seem to get everything returned that is not null unless I choose 1 or 2 (which are rows with no order number and excluded from the array example above) and then I get that row returned with all of the other rows that is not null (as shown in the example above) but any other null rows are excluded. Is this a problem caused by the relationship on my Application object?
public function stages()
{
return $this->hasMany('App\Entities\Application\Stage', 'type_id')->orWhereNull('type_id')->orderBy('order', 'asc');
}
Update:
Setting the foreign_key and local_key on my relationship seemed to resolve the other issues:
public function stages()
{
return $this->hasMany('App\Entities\Application\Stage', 'type_id', 'type_id')->orWhereNull('type_id')->orderBy('order', 'asc');
}
You're actually calling the where() method on a Collection object, not on a Builder object.
The where() method on the Collection works a little differently. First, it can only do an equals comparison. Second, by default, it does a strict equals (===) comparison, and this is your issue. Since your $stage_number is a string, and your order field is most likely an integer, the strict comparison doesn't return any results.
You can change the comparison to a loose equals (==) by passing false as the third parameter. You can also use the whereLoose() method, which does the same thing.
Also note, assuming that stages is a hasMany or belongsToMany relationship on the Application object, there is no need for the call to collect(). $application->stages will already return a Collection.
// pass false as third parameter
$stage = $application->stages->where('order', $stage_number, false)->all();
// or use whereLoose
$stage = $application->stages->whereLoose('order', $stage_number)->all();
Now, having said all that, you probably want to be calling where() on the query builder. While $application->stages will give a Collection of related stage objects, $application->stages() returns the Relation object for the relationship, which gives you access to the query builder. Since you have access to the builder, you can add your where clause to the query being run and will provide a performance boost (and also doesn't care about variable type).
$stage = $application->stages()->where('order', $stage_number)->get();
Note that get() will return a Collection object that contains the matching stage objects. If order is unique and you want to get that one stage object, you can use first() instead of get():
$stage = $application->stages()->where('order', $stage_number)->first();
I want to query the latest + distinct name.
I got the distinct part to work, but they're not the latest.
I'm not sure how to do that in Laravel.
I’ve tried
$localDevices = Device::orderBy('created_at', 'desc')->groupBy('mac')->get();
$localDeviceName = [];
$i = 0;
foreach ($localDevices as $localDevice) {
foreach ($devices as $device) {
if($localDevice->mac == $device->device_mac ){
$localDeviceName[$i]['name'] = $localDevice->name;
$localDeviceName[$i]['mac'] = $device->device_mac;
$i++;
}
}
}
Database
I got
array:1 [▼
0 => array:3 [▼
"name" => "Apple Watch"
"img" => "/images/photos/devices/apple-watch.jpg"
"mac" => "080027E2FC7D"
]
]
I want it to show ps4 because it is the latest.
Try #2
tried update my
orderBy('created_at', 'desc') to orderBy('created_at', 'asc')
I got the same result.
Try #3
tried placing orderBy after groupBy
Device::groupBy('mac')->orderBy('created_at', 'desc')->get();
I got the same result.
Any hints / suggestions on that will much appreciated !
You are doing a groupBy on your mac value which isn't unique, your Apple watch and PS4 have the same mac, mysql first groups by then orders your grouped results. That's why you are always getting Apple watch.
What you want is to fetch the latest record from each group and for that you might write a Raw query, check this Retrieving the last record in each group
I'll explain this with mysql query.
select * from stock where home>away
home away
1 3
2 1
1 0
4 5
I just wanna make same query with mongodb. but I couldn't make this.
array('column' => array('$gt' => what should I write here?))
I need some help please for PHP usage.
You can not do this directly with a MongoDB query. Queries in MongoDB only allow comparisons with static values. However, there are a few options.
First of all, you can just store the result of the comparison whenever you update the values in each field, f.e., you can store it as:
home away gt
1 3 0
2 1 1
1 0 1
4 5 0
This is the simplest solution, and has the additional benefit that you can set an index on gt. Of course, it does mean more overhead when updating values. Doing this sort of pre-calculation is very similar to denormalisation. Denormalisation is something you will often have to do in NoSQL databases in order to make most of the system.
There is an alternative, but it wouldn't allow you to do an indexed search on >. You can use the aggregation framework in the following way:
db.so.aggregate( [
{ $project: {
'away' : 1,
'home': 1,
'better_at_home': { $cmp: [ '$home', '$away' ] }
} },
{ $match: { 'better_at_home': { $gt: 0 } } }
] );
In the first step, we use $cmp to compare home and away. In the second step ($match), we then filter out all the documents where the difference is less than or equal to 0.
The answer of the aggregation is:
{
"result" : [
{
"_id" : ObjectId("51ee7cfb812db9ff4412f12f"),
"home" : 2,
"away" : 1,
"better_at_home" : 1
},
{
"_id" : ObjectId("51ee7cff812db9ff4412f130"),
"home" : 1,
"away" : 0,
"better_at_home" : 1
}
],
"ok" : 1
}
Sadly, $gt cannot compare two fields.
What you can do for non time critical queries is to use $where;
> db.test.insert({home:5, away:3})
> db.test.insert({home:1, away:3})
> db.test.find({$where: 'this.home > this.away'})
{ "_id" : ObjectId("51ec576418fd21f745899945"), "home" : 5, "away" : 3 }
Your performance will be better though if you just store an additional "diff" field in the row object and search on that using $gt:0.
> db.test.insert({home:5, away:3, diff:2})
> db.test.insert({home:1, away:3, diff:-2})
> db.test.find({diff: {$gt:0}}, {_id:1,home:1,away:1})
{ "_id" : ObjectId("51ee982a6e4b3b34421de7bc"), "home" : 5, "away" : 3 }
You cannot use normal querying for this, however, never fear, the aggregation framework can help with the $cmp operator: http://docs.mongodb.org/manual/reference/aggregation/cmp/
db.collection.aggregate({$project:{c: {$cmp:['$col1','$col2']}}},{$match:{c:{$gt:0}}})
However, this being said it isn't very index friendly and is currently limited to 16meg of results.
edit
To take your edit into account:
$mongo->collection->aggregate(array(
array('$project'=>
array(
'c'=>array('$cmp'=>array('$col1','$col2'))
)
),
array('$match'=>array('c'=>array('$gt'=>0)))
))