I have an entity named "Location" with the following fields:
id (auto increment)
vehicle (foreign key to "vehicle" table)
lat
lng
speed
I need to get the last locations (with last id) grouped by vehicle using doctrine Query Builder.
Here is my function:
// Find locations
public function findLocations($idFleet, $select, $from, $to)
{
$qb = $this->createQueryBuilder('l')
->innerJoin('l.vehicle', 'v')
->where('v.fleet = :fleet')
->setParameter('fleet', $idFleet)
->andWhere('v.gps = :gps')
->setParameter('gps', true);
// Last locations
if ( $select == "last"){
$qb->groupBy('l.vehicle')
->orderBy('l.id', 'ASC');
}
// else Interval locations
else if ( $select == "interval"){
if( $from != ""){
$from = (new \DateTime())->setTimestamp($from);
$qb->andWhere('l.time >= :from')
->setParameter('from', $from);
}
if( $to != ""){
$to = (new \DateTime())->setTimestamp($to);
$qb->andWhere('l.time <= :to')
->setParameter('to', $to);
}
}
$locations = $qb->getQuery()->getResult();
return $locations;
}
thanks for helping.
To pick the latest record from location entity for each vehicle you could do a self left join on location entity with additional join criteria using WITH clause and in where clause check for nulls, the rows with highest id per location will have a null against self joined rows.
// Last locations
if ( $select == "last"){
$qb
->leftJoin( 'YourBundle\Entity\Location', 'l1', 'WITH', 'l.vehicle = l1.vehicle AND l.id < l1.id' )
->andWhere('l1.vehicle IS NULL' )
->orderBy('l.id', 'ASC');
}
See similar solutions
Doctrine Query Language get Max/Latest Row Per Group
doctrine dbal get latest chat message per group
DQL Doctrine query translation
Doctrine DQL greatest-n-per-group
I had a similar problem recently.
This can be done using a subquery to get the location info by max location id for the vehicle, and then left join it to the outer vehicle select.
Unfortunately this cannot be done in DQL since it cannot join an entity with a subquery.
You will have to do this in native SQL
Something like
SELECT * from vehicle LEFT JOIN location ON location.vehicle = vehicle.id WHERE location.id in (SELECT MAX(Id) FROM location GROUP BY vehicle)
You might think at first that you can do this in a DQL subquery, but this query will work only if there is at least one location for each vehicle.
If you want to still get the vehicle data even if there is no location entry for that vehicle, you will have to work on the subquery to get * for it having max id group by vehicle, and then left join it to the main query. This is the part not supported in DQL
Related
how to left join table have been group, in my case is, table A have data account number and table B is transaction, i have to calculate and group by account number before joining into table A,
if in sql natife is like bellow
select name.account_no
,amount
from
ci_account_name name left join (
select account_no,currency_id,sum(amount) as amount from
ci_account_transaction
where status <> 'X' and store_id = 62242
group by account_no,currency_id
) as trans
on name.account_no = trans.account_no
that coding is working, but how to implement in laravel eloquent
i have try code bellow but there is error
public function reportShowZerro($data){
$return = $this->accountNameModel->select (['ci_account_name.*'
,'amount'
])
->leftJoin("
$this->model->select(['account_no','currency_id',\DB::raw('SUM(amount) amount')
])
->where('store_id',62242)
->where('status','<>','X')
->where('year',$data['year'])
->where('month',$data['month'])
->groupBy('account_no','currency_id')
) as trans",'ci_account_name.account_no','=','trans.account_no'
->whereIn('ci_account_name.store_id',[0,62242)
->get();
return $return;
}
public function reportWithModel($data){
return $this->accountNameModel->select([
'ci_account_name.*',
'trans.store_id as trans_store_id','currency_id','amount'
])->leftJoin(
\DB::raw('
(select account_no,currency_id,store_id,sum(amount) as amount from
ci_account_transaction
where status <> "X" and store_id = '.store()->id.'
and year = '.$data["year"].' and month = '.$data["month"].'
group by account_no,currency_id
) as trans
'),
'trans.account_no',
'=',
'ci_account_name.account_no'
)
->whereIn('ci_account_name.store_id',[0,store()->id])
->orderBy('ci_account_name.account_no')
->get();
}
I have a query which joins tables from two different databases. It is already showing results but then I wanted to show only unique results because some results are redundant. So I added a GROUP BY to get only the unique results but an error appears.
This is my code:
public function search_results_accommodations($location,$from_date,$to_date,$bedroom,$guests)
{
$this->db->select('*, akzapier.bookings.id as BOOKING_ID, akzapier.properties.id as PROPERTY_ID, ci_alexandrohomes.assigned_property.ID as ASSIGNED_PROPERTY_ID, ci_alexandrohomes.listings.ID as LISTING_ID');
$this->db->from('akzapier.bookings');
$this->db->join('akzapier.properties', 'akzapier.properties.id=akzapier.bookings.property_id', 'inner');
$this->db->join('ci_alexandrohomes.assigned_property', 'ci_alexandrohomes.assigned_property.property_id=akzapier.properties.id', 'inner');
$this->db->join('ci_alexandrohomes.listings', 'ci_alexandrohomes.listings.ID=ci_alexandrohomes.assigned_property.listing_id');
$this->db->where('akzapier.bookings.check_in !=', $from_date);
$this->db->where('akzapier.bookings.check_out !=', $to_date);
$this->db->where('ci_alexandrohomes.listings.city', $location);
$this->db->where('ci_alexandrohomes.listings.bedrooms', $bedroom);
$this->db->where('ci_alexandrohomes.listings.guests', $guests);
$this->db->group_by('akzapier.properties.id', 'ASC')
$query = $this->db->get();
return $query->result();
}
The error doesn't show up in the page so I converted it to SQL to see the real deal:
SELECT * akzapier.bookings.id as BOOKING_ID, akzapier.properties.id as PROPERTY_ID, ci_alexandrohomes.assigned_property.ID as ASSIGNED_PROPERTY_ID, ci_alexandrohomes.listings.ID as LISTING_ID
FROM akzapier.bookings
INNER JOIN akzapier.properties ON akzapier.properties.id=akzapier.bookings.property_id
INNER JOIN ci_alexandrohomes.assigned_property ON ci_alexandrohomes.assigned_property.property_id=akzapier.properties.id
INNER JOIN ci_alexandrohomes.listings ON ci_alexandrohomes.listings.ID=ci_alexandrohomes.assigned_property.listing_id
WHERE akzapier.bookings.check_in != '2019-09-21'
AND akzapier.bookings.check_out != '2019-09-30'
AND ci_alexandrohomes.listings.city = ‘1’
AND ci_alexandrohomes.listings.bedrooms = '2'
AND ci_alexandrohomes.listings.guests = '4'
GROUP BY akzapier.bookings.property_id ASC
ERROR SAYS:
1055 - Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'akzapier.bookings.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
In general, when you use GROUP BY, your SELECT statement must contain either aggregates (such as MAX(...), COUNT(...), etc.) or the columns must appear in GROUP BY. You have selected all of the fields, non-aggregated, with the asterisk *. In this case, it's complaining about the field akzapier.bookings.id, which is neither aggregated, nor in your GROUP BY.
If you actually want unique values, try SELECT DISTINCT, which will drop duplicate rows from the result.
I have an order table and an order_details table in my system.
Relationship between order table and order details table is one to many, means One order has many order details.
Now the problem is i am trying to filter the order with the quantity of items a that are stored in order_details table.
what i doing right know trying to access with whereHas
if ($request->has('quantity') && $request->quantity != null){
$query = $query->whereHas('orderDetails',function ($q) use ($request){
$q->whereRaw('SUM(Quantity) >= '.$request->quantity);
});
}
$orders = $query->orderBy('OrderID','desc')->get();
But it throws an error
General error: 1111 Invalid use of group function (SQL: select * from `orders` where `AddedToCart` = 0 and `PaymentSucceeded` = 1 and exists (select * from `order_details` where `orders`.`OrderID` = `order_details`.`OrderID` and SUM(Quantity) >= 12) order by `OrderID` desc)
I will be vary thankful if i get the solution
To be able to use sum function you need to group by data and as I see you are trying to group them by orderID.
An approach like this might help:
$ordersIDs = DB::table('orderDetails')
->groupBy('OrderID')
->havingRaw('SUM(Quantity)', '>=', 12)
->pluck('orderID')->toArray();
$orders = DB::table('orders')
->whereIn($ordersIDs)
->get();
The above code executes two SQL queries, you can mix them easily to make one.
Hope it helps.
I use Laravel Eloquent and I have this code:
<?php
$bulk = Bulk::where('id', 'bulkid1');
echo $bulk->info;
foreach($bulk->models as $model) {
echo $model->info;
$lastreport = $model->reports()->getQuery()->orderBy('created_at', 'desc')->first();
echo $lastreport->message;
}
?>
What I want to achieve is that the $lastreport is preloaded. In this piece of code, the query will be executed too many times (every bulk has 1000 models, resulting in 1000 subqueries).
While in plain sql I could just do:
SELECT * FROM bulk
LEFT JOIN models ON bulk.id = models.bulk_id
LEFT JOIN ( SELECT *, MAX(created_at) AS created_at
FROM
reports
GROUP BY
model_id )
lastreport ON models.id = lastreport.model_id
WHERE bulk.id = 'bulkid1'
Database pseudocode:
TABLE bulks
id, info
TABLE models
id, bulk_id, info
TABLE reports
id, model_id, message
This is the N+1 selects problem The laravel solution for this problem is eager loading
In your case you'd do:
<?php
$bulk = Bulk::with(['models', 'models.reports' => function ($query) {
return $query->orderBy('created_at', 'desc');
}
])->where('id', 'bulkid1')->first();
echo $bulk->info;
foreach($bulk->models as $model) {
echo $model->info;
$lastreport = $model->reports->first();
echo $lastreport->message;
}
This should ensure that (a) all models are loaded with only 1 additional query and (b) all model reports are loaded with another additional query. The downside with this is that there are more data being loaded than necessary because of the orderBy clause which can't really be represented as a query time condition.
I've just added some Joins to my application which uses Idiorm/Paris, I'm finding that when I search via Model::factory() the object returned is getting the ID from the joined object, not the 'parent' object.
How can I tell Paris which table alias should form the model?
I'm doing this in a search context, so I don't think that I can use has_many() but I'd be happy to be wrong!
Sample code:
// Find a booking with a join
$query = Model::factory('Booking');
$query->where('booking.id', '2282');
$query->join(
'customer',
array('booking.id', '=', 'customer.booking_id'),
'customer'
);
$bookingWithJoin = $query->find_one();
// Find the same booking, without a join
$query = Model::factory('Booking');
$query->where('id', '2282');
$bookingWithoutJoin = $query->find_one();
// The booking with a join gets the ID of the customer it's joined with
echo $bookingWithJoin->id .' != '. $bookingWithoutJoin->id;
I discovered the answer was $query->select('booking.*');
Does the equivelant of
SELECT booking.* FROM bookings JOIN customer on booking.customer_id = customer.id
So the returned results won't have customer.* fields included.