Maybe a silly question here, but I am working on an app where we need to calculate some basic demographic stats. I want to make a trip to the data base once and then try and query that array of Eloquent models. Right now I am getting an array using the query, and then looping through it to count some stats.
i.e.
$people = Person::all();
$total = count($people);
$count = 0;
foreach($people as $person)
if($person->age > 60) {
count++;
}
}
$percent = $count/$total;
Which works but we have to do this for hundreds of data points.
Can I do something like this?
i.e.
$people = Person::all();
$total = count($people);
$count = $people->where('age', '>=', '60')->count();
$percent = $count/$total;
My thought is that the latter will be more efficient (if possible) because it only makes one trip to the database and doing the aggregate stuff on server side? Or would it be better to make multiple queries and get the database to do all the aggregate stuff?
Thanks,
Matt
You want Collections.
https://laravel.com/docs/5.2/collections
Collections in Laravel are handy Array wrappers that provide additional logic that is very similar to the ODB structure. In fact, when you get Eloquent results, you get a special Database collections.
https://laravel.com/api/5.1/Illuminate/Support/Collection.html
https://laravel.com/api/5.1/Illuminate/Database/Eloquent/Collection.html
You can use a variety of logic on both of these object types that are basically identical to Builders for Eloquent DB queries.
Yes you can, Eloquent has an aggregate count method, try this:
$total = Person::count();
$count = Person::where('age', '>=', '60')->count();
$percent = $count/$total;
It would be more efficient to query the database once, and then filter the resulting collection:
$people = Person::all();
$total = count($people);
$peopleOver60 = $people->filter(function($item) {
return $item->age >= 60;
});
$percent = count($peopleOver60) / $total;
Yes is great for performance
and You can change your code like that :$people to Person::
$people = Person::all();
$total = count($people);
$count = Person::where('age', '>=', '60')->count();
$percent = $count/$total;
Your database optimized for searching queries but PHP isn't. You should use your "where" queries in DB. But if you want it anyway you can check this library :
https://phplinq.codeplex.com/
Related
I have a query about this project that I am doing, I make a query to my two tables and the data that I call in this case is a quantity number for both, the data displayed is the one that has the same id for both tables.
The problem occurs when I pass two identifiers and to those two identifiers I want to add their current amount with the amount obtained from the other table
In general, what I want to do is add the amounts obtained, this is my code that I am working with, I would really appreciate if you can help me solve it or guide me.
$id_servis = [1077,1078];
$sum_quantity_add = Servis_tareas::where('servis_id',$id_servis)->get();
foreach($sum_quantity_add as $sum_add){
$quantity_two[] = $sum_add->quantity;
}
$quantity_actual = Servis::wherein('id',$id_servis)->get();
foreach($quantity_actual as $quantity_act){
$quantity_one[] = $quantity_act->quantity_final;
}
dd($id_servicios,$quantity_one, $quantity_two);
//ERROR
$total[] = $quantity_one + $quantity_two;
//ERROR
if(is_numeric($total) < 0 ){
Servis::wherein('id',$id_servis)->update(['quantity_final' => 0]);
}else{
Servis::wherein('id',$id_servis)->update(['quantity_final' => $total]);
}
In MySql/SQL there is SUM query which handles the addition and they are called Aggregation Functions, and in Laravel there is a Eloquent equivalent of these Laravel Aggregates, using these methods you will be able to count, max, min, avg on the query end rather than in the PHP end.
So, your code will look like
$id_servis = [1077, 1078];
$sum_quantity_add = Servis_tareas::where('servis_id', $id_servis)->SUM('quantity');
$quantity_actual = Servis::wherein('id', $id_servis)->SUM('quantity_final');
$total = $sum_quantity_add + $quantity_actual;
What you are trying is treating array as numeric value and adding it, which is wrong, + operator behaves totally different while you are using with array, it merges the two array, it is different than array_merge too, so i recommend giving this answer a read + operator for array in PHP
UPDATED:
I still don't understand if you want to replace with the SUM from Servis_tareas in the Servis Table or sum the each others quantity and save it, Code below sum the data from both table and save it.
$id_servis = [1077, 1078];
$servisTareas = Servis_tareas::selectRaw("SUM(`quantity`) as total, `servis_id` ")
->where('servis_id', $id_servis)
->groupBy('servis_id')
->having('total', '>', 0)
->get();
$foundId = [];
$servisTotal = Servis::query()->whereIn('id', $id_servis)->pluck('quantity_final', 'id')->toArray();
foreach ($servisTareas as $servisTarea) {
$foundId[] = $servisTarea->servis_id;
$total = $servisTarea->total + ($servisTotal[$servisTarea->servis_id] ?? 0)
Servis::where('id', $servisTarea->servis_id)->update(['quantity' => $total]);
}
if (!empty($foundId)) {
Servis::whereNotIn('id', $foundId)->update(['quantity' => 0]);
}
I want to use the WhereIn method in Eloquent but it now works like the below function.
<?php
$product = DB::table('product')
->join('supplier','supp_code','=','prod_supplier_code')
->select('product.*','supplier.supp_margin')
->where('prod_seo_title','=',$id)
->first();
$decArr = explode(',',$product->prod_decoration_type);
for($i=0; $i<count($decArr); $i++)
{
if($i==0)
$inString = "'".$decArr[$i]."','";
elseif ($i<(count($decArr))-1)
$inString .= $decArr[$i]."','";
else
$inString .= $decArr[$i]."'";
}
$proDeco = DB::table('decoration')->whereIn('deco_print_type', [$inString])->get();
?>
But In the query, it's displaying like this.
select * from `decoration` where `deco_print_type` in ('\'100109C9\',\'100110B9\',\'100144C9\',\'100186C9\'');
I can't understand why these slashes are coming. Please help me to fix this issue.
The whereIn() method will accept an array of all the items. In your example you are passing an array with one element with all the values already concatenated. Based on your example you only need to pass $decArr
DB::table('decoration')->whereIn('deco_print_type', $decArr)->get();
I'm building a survey platform and I need to get the average answer rate of the survey.
What I'm currently doing is retrieving all the questions and then dividing times viewed and times answered. Is there a more efficient / less resource consuming method by calculating average on the DB and not looping through thousands of results?
Here is my working code right now that takes forever:
$total_showed = 0;
$total_answered = 0;
$total_queries = Query::where('client_app_id','=', $app_id)->get();
foreach ($total_queries as $app_query) {
$total_showed = $total_showed + $app_query->showed;
$total_answered = $total_answered + $app_query->answered;
}
if ($total_showed > 0) {
$total_arate = round(($total_answered / $total_showed) * 100, 1);
} else {
$total_arate = 0;
}
try
$total_showed = $total_queries->sum('showed')
$total_answered = $total_queries->sum('answered')
since $total_queries is a collection you can use it's sum method
see https://laravel.com/docs/5.3/collections#method-sum
this would be mre efficient I think
Sure you can go into Raw SQL:
instead of:
$total_queries = Query::where('client_app_id','=', $app_id)->get();
use something like:
$total_queries = Query::select(DB::raw('SUM(showed) as counter, SUM(answered) as answered'))
->where('client_app_id','=', $app_id)->get();
try this aggregate function avg(); like this
$price = DB::table('orders')->where('finalized', 1)
->avg('price')
I need to calculate in a function the average score of a column named: "totalscore" from my database table "score"
I tried to do Active record select_avg() but I am not getting anything.
Any idea how I can do this?
function calculateaverage(){
$dataArr = array();
$data = $this->db->get('score');
$maxrows = $data->num_rows();
$data = $this->db->get('score');
for ($i = 1; $i<= $maxrows-1; $i++){
$this->db->select('totalscore');
foreach ($data->result() as $row) {
$dataArr[$i] = $row->totalscore;
}
}
return $dataArr;
}
You can try this code, very simple and straight forward. write it in your model. use in Controller like $this->yourmodel->calculateaverage;
basically we are telling codeigniter query builder to select the AVG of our totalscore..
function calculateaverage(){
$query = $this->db->select('AVG(totalscore) as average_score')->from('score')->get();
return $query->row()->average_score;
}
I'm fairly new to cassandra but i have making good progress so far.
$conn = new ConnectionPool('Cluster');
$User = new ColumnFamily($conn, 'User');
$index_exp = CassandraUtil::create_index_expression('email', 'John#dsaads.com');
$index_clause = CassandraUtil::create_index_clause(array($index_exp));
$rows = $User->get_indexed_slices($index_clause);
foreach($rows as $key => $columns) {
echo $columns['name']."<br />";
}
Im using this type of query to get specific date from somebodys email adress.
However, i now want to do 2 things.
Count every user in the database and display the number
List every user in the database with $columns['name']." ".$columns['email']
In mysql i would just remove the 'where attribute' from the select query, however i think its a little bit more complicated here?
In Cassandra there's no easy way to count all of the rows. You basically have to scan everything. If this is something that you want to do often, you're doing it wrong. Example code:
$rows = $User->get_range("", "", 1000000);
$count = 0;
foreach($rows as $row) {
$count += 1;
}
The second answer is similar:
$rows = $User->get_range("", "", 1000000, null, array("name", "email"));
foreach($rows as $key => $columns) {
echo $columns["name"]." ".$columns["email"];
}
Tyler Hobbs give very nice example.
However if you have many users, you do not want to iterate on them all the time.
It is better to have this iteration once or twice per day and to store the data in cassandra or memcached / redis.
I also would do a CF with single row and put all usernames (or user keys) there on single row. However some considered this as odd practice and some people will not recommend it. Then you do:
$count = $cf->get_count($rowkey = 0);
note get_count() is slow operation too, so you still need to cache it.
If get_count() returns 100, you will need to upgrade your phpcassa to latest version.
About second part - if you have less 4000-5000 users, I would once again do something odd - put then on single row as supercolumns. Then read will be with just one operation:
$users = $scf->get($rowkey = 0, new ColumnSlice("", "", 5000));
foreach($users as $user){
echo $user["name"]." ".$user["email"];
}