Refining My Eloquent Query (Laravel) - php

Edited for Clarity:
I need some way for this Query to only return distinct course_ids in the
->wherein('course_id', $array)
part of the query. It gets the total number of completed courses, but if a user has completed a course more than once it counts it toward the total. So if you want to know how many students have done a course, the number will be off if a student has completed a course more than once.
public function report_count(){
$array = \Session::get('course_report')['course'];
return $this->hasOne('Tracking', 'roster_id')
->selectRaw('roster_id, count(*) as aggregate')
->where('status', 1)
->wherein('course_id', $array)
->groupBy('roster_id');
I've tried adding groupBy('course_id') at the end but it does not work.

Fixed. Changed the query to
return $this->hasOne('Tracking', 'roster_id')
->selectRaw('roster_id, count(distinct course_id) as aggregate')
->where('status', 1)
->wherein('course_id', $array)
->groupBy('roster_id');
per #Robin R suggestion, I went on a search of ways to utilize SQL in my selectRaw query instead of trying this the eloquent way. Thanks to all who helped me brainstorm though this one.

Related

What’s most efficient way to query a LEFT JOIN with tons of data with Doctrine 2 DQL

I have an entity “coin” linked with a oneToMany on prices. Prices is updated every minute and has millions of entries.
What I’m trying to achieve is a DQL query that will grab the last price to date.
Right now I see 2 ways to do it and I’m wondering which one is best in term of performance:
I could look for prices in the DB with ‘prices’.’lastupdated’ equal to the last update.
OR I could grab the last 100 price ids (we update 100 coins every minute and add 100 new rows in the Database) with a LIMIT 100 and ORDER BY ‘id’ DESC on my left join.
I know it’s tricky to use LIMIT on a LEFT JOIN with doctrine but I found a solution here:
How to limit results of a left-join with Doctrine (DQL)
and here: https://www.colinodell.com/blog/201703/limiting-subqueries-doctrine-2-dql
I’m wondering what will take the least amount of ressources to execute that query.
Of course I’m using getArrayResults() and am using partials and doctrine cache.
What is your opinion on that?
Thanks!
I've been in similar situations. For example, I run a social commerce network and want to get all follower_ids from a business to update them that an action has been performed. It's the same if you want liker_ids, etc.
In this scenario, you are only interested in a value from one column (price for you) but based off queries involving different fields (coin_id, lastupdated). For this, I greatly advise using doctrine to send a native SQL query. It's orders of magnitude more efficient, evading costly doctrine hydration, etc.
I wrote a sample query in an entity repository for you.
<?php
namespace App\EntityRepository;
use Doctrine\ORM\EntityRepository;
use PDO;
class CoinPricesRepository extends EntityRepository
{
public function queryLatestPricesForCoinId(int $coin_id, int $limit)
{
$sql = 'SELECT price FROM coin_prices WHERE coin_id = :coin_id ORDER BY lastupdated DESC LIMIT = :limit;';
$params['coin_id'] = $coin_id;
$params['limit'] = $limit;
$stmt = $this->getEntityManager()->getConnection()->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
}
}
I have been working on optimizing a my Doctrine request and got some awesome perf improvement I’ll be sharing here if anyone is looking at a similar solution.
First, limit your left join with a where clause as much as possible
Second, use partial objets
Third, use Array results. This actually changes everything.
/**
* #return Coins[] Returns an array of Crypto objects
*/
public function findOneByTickerRelationnal($ticker)
{
$em = $this->getEntityManager();
$updatesrepository = $em->getRepository(Updates::class);
$updates = $updatesrepository->findOneBy(['id'=> 1 ]);
// This is where I’ve been doing additional work to limit my left join as much as possible with a ‘with’ on left join
$recentMarkets = $updates->getMarket();
$recentPrices = $updates->getPrice();
$recentSources = $updates->getSources();
$cryptos = $this->createQueryBuilder('c')
->select('partial c.{id, name, ticker}’) //<= use of partial is a plus but you need to know exactly which fields you want
->leftJoin('c.prices', 'p','WITH', 'p.last_updated >= :recentPrices')
->addSelect('partial p.{id, price_usd, daily_volume_usd, change_1h, change_1d, change_7d, rank}')
->leftJoin('c.markets', 'm','WITH', 'm.last_updated >= :recentMarkets')
->addSelect('partial m.{id, cur_supply, market_cap, max_supply}')
->leftJoin('c.sources', 's','WITH', 's.last_updated >= :recentSources')
->addSelect('s')
->where('c.ticker = :ticker')
->setParameter('recentPrices', $recentPrices)
->setParameter('recentMarkets', $recentMarkets)
->setParameter('recentSources', $recentSources)
->setParameter('ticker', $ticker)
->getQuery()
->getArrayResult(); //<=Changes everything
$results = $cryptos[0];
return $results;
}

How can I retrieve the information I want using MySQL `joins` or Laravel `relationships`?

I am working on a project using the Laravel framework. In this project I have three tables:
1) Master Part Numbers (master_part_numbers)
Columns: id, part_number
Values: 1, MS26778-042
2) Inventory (inventory)
Columns: id, master_part_number, stock_qty
Values: 1, 1, 7
3) Inventory Min Maxes (inventory_min_maxes)
Columns: id, master_part_number, min_qty
Values: 1, 1, 10
I am trying to find the inventory where the stock level is below the min_qty. I have been attempting this using joins, like so:
$test = MasterPartNumber::table('master_part_numbers')
->join('inventory', 'master_part_numbers.id', '=', 'inventory.master_part_number_id')
->join('inventory_min_maxes', 'master_part_numbers.id', '=', 'inventory_min_maxes.master_part_number_id')
->select('master_part_numbers.part_number')
->where('inventory.stock_qty', '<=', 'inventory_min_maxes.min_qty')
->get();
However I am getting an empty collection every time. I have tried removing the where() clause and I get all the part numbers in the inventory, so it feels like I'm on the right track, but missing a critical component.
Also, I don't know if there is an easier or more efficient way to do this using Laravel's Eloquent Relationships, but that option is available.
Note: I added the space after table('master_part_numbers') in my query displayed here on purpose, for readability.
EDIT 1:
This sql query returns the expect result:
SELECT master_part_numbers.part_number
FROM master_part_numbers
JOIN inventory ON master_part_numbers.id=inventory.master_part_number_id
JOIN inventory_min_maxes ON master_part_numbers.id=inventory_min_maxes.master_part_number_id
WHERE inventory.stock_qty<=inventory_min_maxes.min_qty;
EDIT 2:
I finally got it working with some help from the Laravel IRC, however it isn't ideal because I am missing out on some of the data I would like to display, normally collected through relationships.
Here is what I am currently using, but I hope to get refactored:
DB::select(DB::raw('SELECT master_part_numbers.id, master_part_numbers.part_number, master_part_numbers.description, inventory.stock_qty, inventory.base_location_id, inventory_min_maxes.min_qty, inventory_min_maxes.max_qty
FROM master_part_numbers
JOIN inventory ON master_part_numbers.id = inventory.master_part_number_id
JOIN inventory_min_maxes ON master_part_numbers.id = inventory_min_maxes.master_part_number_id
WHERE inventory.stock_qty <= inventory_min_maxes.min_qty'));
If I have understood your problem correctly then
'masters_part_numbers.id' == 'inventory.id' and
'inventory.master_part_number' == 'inventory_min_maxes.master_part_number'
$test = DB::table('master_part_numbers')
->join('inventory', 'master_part_numbers.id', '=', 'inventory.id')
->join('inventory_min_maxes', 'inventory.master_part_number', '=', 'inventory_min_maxes.master_part_number')
->where('inventory.stock_qty', '<=', 'inventory_min_maxes.min_qty')
->whereNotNull('inventory_min_maxes.master_part_number');
->select(DB::raw('part_number'))
->get();
Based on above criteria. This code will work. I tried in laravel 5.4 .
Try and let me know. nd if it work give me a thumbs up
I discovered a way to solve this problem using the Laravel ->whereRAW() statement:
$test = $inventory->join('inventory_min_maxes', 'inventory.id', '=', 'inventory.inventory_min_max_id')
->whereRaw('inventory.stock_qty <= inventory_min_maxes.min_qty')
->whereRaw('inventory.inventory_min_max_id = inventory_min_maxes.id') // required so it tests against the specific record, without it will test against all records.
->get();
The major advantage for me, other than it looked terribly ugly before, was that I can now use the power of relationships.
Note: $inventory is an instance of my Inventory model, which I type hinted in the index() method.

Join query codeigniter with where condition returns all rows

Here is my join query for getting data from two tables.
$this->db->select('*');
$this->db->from('george_hotel_bkd_customers');
$this->db->where('george_hotel_bookings.bookingref',$ref);
$this->db->join('george_hotel_bookings', 'george_hotel_bookings.user_id = george_hotel_bkd_customers.user_id');
$query = $this->db->get();
According to my where condition it returns only one row but it will returns all the rows with matches the join condition.
Seems like my where condition is not executed here.
please help me
What does $ref =, how many results should it pick up roughly?
what do you get when you omit the where? if you get full result then must be an issue with $ref value, or, that is the result.
Just a note, you don't need to select(*), this is default, if this is an just for examples sake, sorry for picking up. You can also add the ->from(whaterver_table) to ->get(whatever_table) You need to add ->get() anyway, so why not remove the ->from() line, just a choice of readability, but i did not realise you could do this for a while, so thought id add it to my answer.
Or another problem solving path is whether the join should be 'left' as 3rd arg. IE is there always a join?
Altering the position or the join in the statement would not make any difference, as Anant suggested
$this->db->select('*');
$this->db->from('george_hotel_bkd_customers');
$this->db->join('george_hotel_bookings', 'george_hotel_bookings.user_id = george_hotel_bkd_customers.user_id');
$this->db->where('george_hotel_bookings.bookingref',$ref);
$query = $this->db->get();
always where is last

codeigniter database query / mysql

I have written a query with codeigniters database library, it looks like this,
public function getCandidateCredits($candidate_id)
{
$this->db->select('*')
->from('credits')
->join('candidate_has_credits', 'candidate_has_credits.credits_credit_id = credits.credit_id', 'left')
->where('credits.candidates_candidate_id', (int)$candidate_id)
->order_by('candidate_has_credits.date_created', 'DESC')
->order_by('credits.credit_position', 'DESC');
$query = $this->db->get();
return $query->result_array();
}
What this query is meant to do is, get all a members credits, and the order them firstly by date (newest first), then by credit position (highest number first).
I am having a couple of problems though, the credits should be ordered by date, but only if there is no credit position, if there is a credit position (0 - 999) then that should take precendence where ordering the credits, if there is a date and a credit position then the credits should be ordered by credit position and then date.
Is this possible? I feel like I am miles away from where I need, the results I return seem to be return no obvious ordering. Not sure if it makes a difference but the date_created field is a DATETIME.
You are correctly using a left join, but have put the order by clauses in the wrong order, so first of all, flip them:
->order_by('credits.credit_position', 'DESC')
->order_by('candidate_has_credits.date_created', 'DESC')
This should do it, except that now those candidate_has_credit rows that do not have corresponding credits records (so the credit_position is null) will be in the end, and I assume you want those on top.
There is a small trick to push null values to top when using DESC sorting providing you know the maximum value available in that field:
->order_by("ifnull('credits.credit_position',1000) desc,candidate_has_credits.date_created desc")
Note that I am using the form of order_by method which contains only one parameter, that one should not escape the function and is marginally faster.
Check the ifnull documentation to see how it works.
Here's a shortened version:
public function getCandidateCredits($candidate_id)
{
return $this->db->from('credits')
->join('candidate_has_credits', 'candidate_has_credits.credits_credit_id = credits.credit_id', 'left')
->where('credits.candidates_candidate_id', (int)$candidate_id)
->order_by('credits.credit_position', 'DESC')
->order_by('candidate_has_credits.date_created', 'DESC')
->get()->result_array();
}

Kohana orm order asc/desc?

I heed two variables storing the maximum id from a table, and the minimum id from the same table.
the first id is easy to be taken ,using find() and a query like
$first = Model::factory('product')->sale($sale_id)->find();
but how can i retrieve the last id? is there a sorting option in the Kohana 3 ORM?
thanks!
Yes, you can sort resulting rows in ORM with order_by($column, $order). For example, ->order_by('id', 'ASC').
Use QBuilder to get a specific values:
public function get_minmax()
{
return DB::select(array('MAX("id")', 'max_id'),array('MIN("id")', 'min_id'))
->from($this->_table_name)
->execute($this->_db);
}
The problem could actually be that you are setting order_by after find_all. You should put it before. People do tend to put it last.
This way it works.
$smthn = ORM::factory('smthn')
->where('something', '=', something)
->order_by('id', 'desc')
->find_all();
Doing like this, I suppose you'll be :
selecting all lines of your table that correspond to your condition
fetching all those lines from MySQL to PHP
to, finally, only work with one of those lines
Ideally, you should be doing an SQL query that uses the MAX() or the MIN() function -- a bit like this :
select max(your_column) as max_value
from your_table
where ...
Not sure how to do that with Kohana, but this topic on its forum looks interesting.

Categories