Select rows with equal values - php

How to do in Laravel?
1) get from table rows where user_id has the equal values?
2) return sum of some_amount values from this selected rows
Table:
- id;
- user_id;
- some_amount;
Table has bunch of certificates with some amount of money that belongs to different users. I need to find all certificates that belongs to one user (one user can have few certificates) and count how much money he have from all his certificates

Given you aren't looking for a solution to query this for an individual user, it sounds like you want to group by the user and sum the result of certificates.
The answer from #PhilCross is pretty close, you'd just need to modify it to add the group clause and remove the where condition. Something like this:
ModelName::groupBy('user_id')->sum('some_amount');
or
\DB::table('table_name')->groupBy('user_id')->sum('some_amount');
Generally eloquent or the query builder will have a method that relates to how you would do this in raw SQL.
I find it helpful to write out or think about how I would write the raw SQL and then slowly fill the eloquent or query builder in from that.

If you use a Model:
ModelName::where('user_id', 1)->sum('some_amount')
If you're using the query builder:
\DB::table('table_name')->where('user_id', 1)->sum('some_amount');
This is the documentation for the query builder:
https://laravel.com/docs/5.6/queries
Models: https://laravel.com/docs/5.6/eloquent
Model Relationships: https://laravel.com/docs/5.6/eloquent-relationships
Collections: https://laravel.com/docs/5.6/collections

Related

Laravel collection count() takes too long

I have a contacts table and a contact_list table, a contact_list has many contacts, contacts table has a is_active field.
to get the number of active contacts for a contact_list i use the query builder as follows:
$contact_list->contacts()->where('is_active', 1)->count();
if the number of contacts is too big the query takes too long.
is there a better way to implement this?
as I can see you want to optimize the performance of your request.
to achieve that you should use eager-loading, so your new request should be like below:
$contactList = App\ContactList::with(['contacts' => function ($query) {
$query->where('is_active', 1);
}])->count();
For more information about how Laravel implement eager-loading please check the link of documentation: eager-loading documentation
With InnoDB COUNT() works slowly for tables with million rows.
That's a problem with count() as the InnoDB engine locks the table per row, and if the table is being changed on a regular bases the count() function is not trivial.
The Eloquent ORM doesn't differenciatie between InnoDB or MyISAM so the count() process always triggers something like SELECT COUNT(*) AS aggregate FROM table.
If you change the query with this one SELECT COUNT(1) FROM table you will realize that the count() process is way faster.
So in Laravel that would be reduced to something like:
Model::select(DB:raw('count(1)'))->first();
Or in your case
$contact_list->contacts()->select()->where('is_active', 1)->count();

laravel eloquent order by multiple columns and show in one column

I have 4 tables:
user
particular
pro
advertiser
I have a table where i show the users data.
in this table i have a column named Name where i use the code below
<td>
#if($user->pro))
{{$user->pro->company_name}}
#elseif($user->particular))
{{$user->particular->name." ".$user->particular->last_name}}
#elseif($user->advertiser)
{{$user->advertiser->company_name}}
#endif
</td>
So this column is popuplated from 3 different tables and different column.
How can i sort this column table.
You're going to need a complex ORDER BY clause that you won't be able to set up using Eloquent's methods. However, Eloquent builder offers orderByRaw() method that let's you use raw SQL expression in your ORDER BY clause. Combine that with a couple of SQL IF's (for MySQL see https://dev.mysql.com/doc/refman/5.1/en/control-flow-functions.html#function_if) and you should get what you need.
In your case it would be sth like:
->orderByRaw("IF(user.is_pro,pro.company_name,IF(user.is_particular, particular.name, IF(user.is_advertiser,advertiser.company_name,'')))")
Keep in mind that ordering by such expressions might have negative performance impact and that using raw SQL might make your code less portable.

Id Column Overwritten by Join

Using Laravel 4.2 & MySQL.
I have an applications table with an id and a fit_score_id column, and a fit_scores table with an id column. It's a basic "belongs to" relationship.
The following code:
$query = Application::join('fit_scores', 'applications.fit_score_id', '=', 'fit_scores.id');
$collection = $query->get();
...produces a collection of Application models with the id property set to the value of the fit_score_id. What am I doing to cause this?
I should note that it is necessary to do this join rather than simply using eloquent relations, because I'm going to want to order the results by a column on the fit_scores table. I don't believe this is possible using Eloquent without an explicit join.
The best way to solve this is by chaining the join method to a select method as following:
Application::select('*', \DB::raw("applications.id as appid"))
->join('fit_scores', 'applications.fit_score_id', '=', 'fit_scores.id')
->get();
Explained: The solution simply suggest that instead of thinking to prevent the behavior of overwriting the first id with the joined id, we can hook into the primary selection query (before joining) and change the label of the id column into something else (in this case 'appid'). By doing so, we end up with both the id of the parent table being labeled 'appid' and the id of the joined table being labeled 'id' again while they lives together on the final result.
I was able to find a possible solution using this answer:
Laravel 4 - JOIN - Same column name
Basically, since Laravel does not automatically prefix column names with table_name. for joined tables, we need to manually work around it by aliasing any conflicting column names in joins. Adding this select statement to my query did it:
->select(DB::raw("applications.*, fit_scores.*, applications.id as id"))
It depends on what you need but probably you can achieve it using eager loading. In case you need to mix joins and eager loading check this out. http://www.jmilan.net/posts/eager-loading-joins-in-laravel

Yii relation generates GROUP BY clause in the query

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.

MySQL and PHP: Searching for rows through a field in an undefined number of joined tables

There is an example table, 'main_table' with the fields 'ID', 'some_data'.
There is an aggregate table, 'agg' with the fields 'id', 'main_table_id', 'joinee_id'.
And then there is the final table, 'joinee', with the fields 'id', 'email'.
The tables 'main_table' and 'joinee' are in a many:many relationship, through 'agg'.
I would like to be able to search all the 'main_table' entries by 'email' from 'joinee', without doing a left join and then group by 'main_table'.'id'.
The final result needs to list all the 'main_table' entries, once per entry. Imagine it like this - I would like 'main_table' to get a temporary field "participants" which would contain all the 'emails' - I would then perform a LIKE match on this field in the same query that does this, in order to find the 'main_table' entries that have anything to do with the email I entered.
Is this possible?
Mind you, this is only a fragment of a much larger query. 'main_table' is already joined with 5 other tables, and their fields are already used as filters. Thing is, I know there can be only one joined table in each of those cases - with 'joinee', the number of connected entries varies.
Thank you.
Combining row results into single cells is something the relational model does not support, and most DBMSes are exceptionally bad at it. If you were to go down that road, you'd need to pull a considerable amount of hacking, like using user-defined functions or nonstandard syntax features to combine the values.
But if I understand you correctly, your problem is that you need to find entries in the main table that have desirable entries in their related email rows, and the problem is that there's a many-to-many relation.
How about this:
Write a query that finds the emails you're interested in and inner join that on the agg table. This will give you the related main_table_ids. Use distinct or group by to remove doubles.
Use the query from 1 as a subquery, and plug it into the query as you have it now, using something like WHERE main_table.id IN (/* subquery */), or, alternatively, inner join your existing query on the subquery from step 1. Which of these you use depends on the circumstances; traditionally, subqueries are slower than joins (all else being equal), but it may be the other way around, depending on your structure and actual data. On some older DMBSes, buffering the subquery result in a temporary table can prove beneficial.

Categories