I have to check if an user is online (in-game), and if he's not then do another checks to perform a sql update.
Actually, I have these two queries.
$isOnline = DB::connection('mssql')->table('USER_STAT')->where('user_id', $userID)->value('ConnectionStat');
$character = Character::where('acc_id', $userID)->where('Name', $characterName)->firstOrFail();
Is there a way I can use union or join in order to save a DB query? I believe this is not a good practice, and some better method is out there, which I can't find in the docs.
I tried something like this, but without success:
$character = Character::where('acc_id', $userID)->where('Name', $characterName)->firstOrFail();
$result = DB::connection('mssql')->table('USER_STAT')->where('user_id', $userID)->union($character)->get();
Thanks in advance!
Using a relation in your Character Model to the Model of the USER_STAT table you can easily lookup the things you need to determine if an update somewhere is needed.
Eloquent Relationships
This does not consolidate the queries into one, like with a join, but they are not heavy anyway, and using the ORM to the fullest will have more significant advantages in the long run.
If you think you must use an SQL-join you must stick to the querybuilder, but the ORM is usually the better choice
Query Builder Joins
Related
Please could somebody tell me which one is most efficient select in Laravel:
$car = Car::all(); ------- $car = Car::find();
$car = DB::table('car')->get(); ------ $car = DB::table('car')->first();
Your first approach:
$car = Car::all(); ------- $car = Car::find();
Makes use of Eloquent. This means, all the rows received from the query will be hydrated to Model instances, and all of those will be injected into an instance of Collection (for multiple elements, of course). This is useful because you then will have all the benefits that this brings. However, this comes with a little decrease on performance (understandable)
Your second one:
$car = DB::table('car')->get(); ------ $car = DB::table('car')->first();
Uses the Query Builder instead. The results (as a whole) will be also casted into an instance of Collection, but its items will be simple arrays. This means that the process will be faster (more performant) but on detriment of not having all the cool features of Eloquent.
There's even a more performant option: Using raw queries. That also has tradeoffs: Results are not hydrated into a Collection instance.
Which one to use? It depends on your needs. Usually I go for the Eloquent option. I use the Query Builder directly when I need to make queries to big databases and need speed.
For me most efficient is selecting from the Model: like Car:all(), but it's always better if you use pagination or just don't take all of the records from the database with all() method.
But selecting with DB is a bit faster and in some cases maybe it would be better to use.
In the end, it always depends on what`s your problem and which way do you want to solve it.
for a better understanding I recommend you to watch this video and after that maybe keep going to search for some more information or just try it out yourself.
https://www.youtube.com/watch?v=uVsY_OXRq5o&t=205s
I have a scenario like this:
I have User model that has an OneToMany relationships with the Post model.
I have a Hashtag model that has an OneToMany relationships with the Post model.
Recap: ONE user has MANY posts, ONE post belongs to ONE hashtag, ONE hashtag has MANY posts.
I would like to fetch only unique users records of an hashtag.
I'm able to do it in non scalable way (fetching all the posts first and then iterate filtering by user id), but I need to maintain scalability for large record numbers.
Edit: I saw a partial solution in Laravel docs.
Laravel eloquent has a method called unique().
With that method I can specify the parameter which should be unique in query.
In my case figured it out with:
$users = $hashtag->posts->unique('user_id');
But I can't paginate query in this way...
Has anyone a solution for that?
You have to paginate your main model caller.
I don't know exactly if this is can reproduce your actual scenario, but let's see this example:
Let's say you have the model hashtags
So let's say the code could be something like this:
$users = DB::table('hashtags AS tags')
->select('tags.*')
->join('[posts AS post','post.id','=','tags.id')
->distinct()
->paginate(5, ['tags.*']);
I don't know if your query it'll be very accurate, but for this, I believe the best approach it'll be you do a raw query, could look like it'll be costly to your database and the operation, but you can work around this approach indexing and partitioning your database.
Remembering always the eloquent sometimes even when we're building join queries could be more even costly to your database.
Since you're worried about scalability so the best thing could be for too could write a view to fetch all the data with proper indexing and partitioning.
Try to use DISTINCT
$users = $hashtag->posts()->selectRaw('DISTINCT(user_id) AS unique_user_id')->paginate(10);
OR
use groupBy()
$users = $hashtag->posts()->groupBy('name')->select('user_id')->paginate(10);
I understand the question might not be very clear but here is my situation. I'm using laravel 5 and i'm developing a CRM system. I have placed Marital Status and Sex/Gender into one Lookup table. Whenever i get the values from the database and pass it to the view, i have two separate queries.
$sexes = DB::table('Lookups')
->where('ValueType', '=', 'Sex')->get();`
$marstatus = DB::table('Lookups')
->where('ValueType', '=', 'Marital Status')->get();`
return view('clients.edit',compact('client'))
->with('sexes', $sexes)
->with('marstatus ', $marstatus );
This code actually works and i am able to get both the marital status and sex/gender on my view.
So, here is my question
Doesn't this mean that i am sending a query to the database twice which affects performance even if it is small
Isn't there a way to query all the values from the lookup table in one query and filter out the values on the controller. So it can be something like
$Lookups = DB::table('Lookups')
and then filter the $Lookups variable and assign it into two different variables ($sexes and $marstatus) based on my filter criteria. i.e ($sexes is for values that have ValueType = 'Sex' ...)
Which one is better for performance. Sending a query twice or three times or just filtering the data on the controller.
1) Yes it does. Just install Laravel Debugbar and see it yourself. It's a very handy tool strongly recommended.
2) Yes you can do that, laravel has nice helper functions for that type of needs:
$collection = collect(DB::table('Lookups')
->whereIn('ValueType', ['Marital Status', 'Sex'])
->get());
$marstatus = $collection->filter(function($item) {
return $item->ValueType == 'Marital Status';
});
$sexes = $collection->filter(function($item) {
return $item->ValueType == 'Sexes';
});
What this does is, it converts the result array to a Laravel Collection so that you can use the filter function. You can also use array_filter function to filter without converting the result array to a collection.
3) Databases are always one of the primary bottlenecks, the fewer the query number the better. However this should not be a general rule especially when cache is used. And for example making joins or subqueries to reduce the number of queries would be deadly mistake on some cases.
Performance is a huge subject. I'd recommend you to start with the Laravel Debugbar to compare the memory usage, number of queries etc. and investigate more on various techniques including cacheing and design patterns too. Accessing the tables directly within the controller is not a very good idea in the first place...
Yes it does mean that. How big is your Lookups table?
You probably mean $lookups = DB::table('Lookups')->all(); or perhaps consider using an Eloquent model class instead, e.g. $lookups = Lookup::all(); Perhaps you may want to cache the result if the table is small? e.g. use the Cache classes in Laravel.
Better for performance would be to use the cache.
It is belong to your query Data I mean your lookups table data.
You can write the query like this in one time:
$sexes_marital_status= DB::table('Lookups')->where('ValueType', '=', 'Sex')
->orWhere('ValueType' '=', 'Marital Status' )
->get();
return view('clients.edit',compact('client'))
->with('sexes_marital_status',$sexes_marital_status);
and this is better that you send your query in one time.
`
I have User, Play and UserPlay model. Here is the relation defined in User model to calculate total time, the user has played game.
'playedhours'=>array(self::STAT, 'Play', 'UserPlay(user_id,play_id)',
'select'=>'SUM(duration)'),
Now i am trying to find duration sum with user id.
$playedHours = User::model()->findByPk($model->user_id)->playedhours)/3600;
This relation is taking much time to execute on large amount of data. Then is looked into the query generated by the relation.
SELECT SUM(duration) AS `s`, `UserPlay`.`user_id` AS `c0` FROM `Play` `t` INNER JOIN
`UserPlay` ON (`t`.`id`=`UserPlay`.`play_id`) GROUP BY `UserPlay`.`user_id` HAVING
(`UserPlay`.`user_id`=9);
GROUP BY on UserPlay.user_id is taking much time. As i don't need Group by clause here.
My question is, how to avoid GROUP BY clause from the above relation.
STAT relations are by definition aggregation queries, See Statistical Query.
You cannot remove GROUP BY here and make a meaningful query for aggregate data. SUM(), AVG(), etc are all aggregate functions see GROUP BY Functions, for a list of all aggregate functions supported by MYSQL.
Your problem is for the calculation you are doing a HAVING clause. This is not required as HAVING checks conditions after the aggregation takes place, which you can use to put conditions like for example SUM(duration) > 500 .
Basically what is happening is that you are grouping all the users separately first, then filtering for the user id you want. If you instead use a WHERE clause which will filter before not after then aggregation is for only the user you want then group it your query will be much faster.
Although Active Record is good at modelling data in an OOP fashion, it
actually degrades performance due to the fact that it needs to create
one or several objects to represent each row of query result. For data
intensive applications, using DAO or database APIs at lower level
could be a better choice
Therefore it is best if you change the relation to a model function querying the Db directly using the CommandBuilder or DAO API. Something like this
Class User extends CActiveRecord {
....
public function getPlayedhours(){
if(!isset($this->id)) // to prevent query running on a newly created object without a row loaded to it
return 0;
$played = Yii::app()->db->createCommand()
->select('SUM(duration)')
->from('play')
->join("user_play up","up.play_id = play.id")
->where("up.user_id =".$this->id)
->group("up.user_id")
->queryScalar();
if($played == null)
return 0;
else
return $played/3600 ;
}
....
}
If you query still is slow, try optimizing the indexes, implement cache mechanism, and use the explain command to figure out what is actually taking more time and more importantly why. If nothing is good enough, upgrade your hardware.
I think one of the more difficult concepts to understand in the Zend Framework is how the Table Data Gateway pattern is supposed to handle multi-table joins. Most of the suggestions I've seen claim that you simply handle the joins using a $db->select()...
Zend DB Select with multiple table joins
Joining Tables With Zend Framework PHP
Joining tables wthin a model in Zend Php
Zend Framework Db Select Join table help
Zend DB Select with multiple table joins
My question is: Which object is best suited to handle this kind of multi-table select statement? I feel like putting it in the model would break the 1-1 Table Data Gateway pattern between the class and the db table. Yet putting it in the controller seems wrong because why would a controller handle a SQL statement? Anyway, I feel like ZF makes handling datasets from multiple tables more difficult than it needs to be. Any help you can provide is great...
Thanks!
By definition, TableData Gateway handles one table only.
ZF enforces this definition with an integrity check on Zend_Db_Table_Selects. However, the integrity check can be disabled and then you can do joins. Just create a method inside your table class to do the Join via the select object like this:
public function findByIdAndJoinFoo($id)
{
$select = $this->select();
$select->setIntegrityCheck(false) // allows joins
->from($this)
->join('foo', 'foo.id = bar.foo_id');
return $this->fetchAll($select);
}
If you want to stick to the definition, you you can use some sort of Service Layer or DataMapper that knows how to handle multiple tables. These sit between the Db classes and the Controllers.
Another alternative is not to use Joins but table relationships and then lazy load dependent rowsets as needed. Of course, that's not Joins then, but multiple queries.
And finally, you can still just use Zend_Db_Statement and craft your SQL by hand:
$stmt = $db->query(
'SELECT * FROM bugs WHERE reported_by = ? AND bug_status = ?',
array('goofy', 'FIXED'));