How to get cheapest product from every group - laravel

How to do the following with laravel ORM?
select f.type, f.variety, f.price
from (
select type, min(price) as minprice
from fruits group by type
) as x inner join fruits as f on f.type = x.type and f.price = x.minprice;

$subQuery = "(select type, min(price) as minprice from fruits group by type) as x";
$query = DB::table('fruits')::selectRaw( "fruits.type,fruits.variety,x.minprice" );
$query->join( DB::raw($subQuery), function ($join) {
$join->on ( 'fruits.type', '=', 'x.type' );
});
$data = $query->get();
I have not tested the code but hopes it works.

Related

convert subquery to use Cases and Join

I have a query which is working fine. I want to convert it to use Cases and Join.
SELECT id, Ticket_id, serial_no, operator,
(SELECT value FROM Forms_Details where FORM_ID = cv.id and status = 'ACTIVE' and key = 'COMPUTER_TYPE') as COMPUTER_TYPE,
(SELECT value FROM Forms_Details where FORM_ID = cv.id and status = 'ACTIVE' and key = 'BRAND') as BRAND,
(SELECT value FROM Forms_Details where FORM_ID = cv.id and status = 'ACTIVE' and key = 'MODEL') as MODEL,
status, datetime
FROM Form cv
where status = 'ACTIVE' order by id desc;
If you want to join, then join. What keeps you from doing so?
SELECT
f.id,
f.Ticket_id,
f.serial_no,
f.operator,
ct.value AS computer_type,
b.value AS brand,
m.value AS model,
f.status,
f.datetime
FROM form f
LEFT JOIN forms_details ct ON ct.form_id = f.id AND ct.status = 'ACTIVE' AND ct.key = 'COMPUTER_TYPE'
LEFT JOIN forms_detailswhere b ON b.form_id = f.id AND b.status = 'ACTIVE' AND b.key = 'BRAND'
LEFT JOIN forms_detailswhere m ON m.form_id = f.id AND m.status = 'ACTIVE' AND m.key = 'MODEL'
WHERE f.status = 'ACTIVE'
ORDER BY f.id DESC;
Or, in order not to read the same table twice, you can use conditional aggregation to get the values from the table:
SELECT
f.id,
f.Ticket_id,
f.serial_no,
f.operator,
ct.value AS computer_type,
fdw.brand,
fdw.model,
f.status,
f.datetime
FROM form f
LEFT JOIN forms_details ct ON ct.form_id = f.id AND ct.status = 'ACTIVE' AND ct.key = 'COMPUTER_TYPE'
LEFT JOIN
(
SELECT
form_id,
MAX(CASE WHEN key = 'BRAND' THEN value END) AS brand,
MAX(CASE WHEN key = 'MODEL' THEN value END) AS model
FROM forms_detailswhere
WHERE status = 'ACTIVE' AND key IN ('BRAND', 'MODEL')
GROUP BY form_id
) fdw ON fdw.form_id = f.id
WHERE f.status = 'ACTIVE'
ORDER BY f.id DESC;
You can PIVOT and then join:
SELECT cv.id,
cv.Ticket_id,
cv.serial_no,
cv.operator,
fd.computer_type,
fd.brand,
fd.model,
cv.status,
cv.datetime
FROM Form cv
LEFT OUTER JOIN (
SELECT *
FROM (
SELECT form_id, key, value
FROM Form_Details
WHERE status = 'ACTIVE'
)
PIVOT (
MAX(value)
FOR key IN (
'BRAND' AS brand,
'MODEL' AS model,
'COMPUTER_TYPE' AS computer_type
)
)
) fd
ON (cv.id = fd.form_id)
where cv.status = 'ACTIVE'
order by id desc;

How can I convert mysql to laravel eloquent left join?

Mysql query like this :
SELECT a.id, a.name, b.TotalMeal, c.TotalCollection
FROM users a
LEFT JOIN (
SELECT user_id, SUM( breakfast + dinner + lanch ) AS TotalMeal
FROM meals
GROUP BY user_id) b ON a.id = b.user_id
LEFT JOIN (
SELECT user_id, SUM( amount ) AS TotalCollection
FROM collections
GROUP BY user_id) c ON a.id = c.user_id
LIMIT 0, 30
I want to convert it to Laravel eloquent, but I'm confused. I have Three tables. eg - users( id, name,email ..), meals( user_id, breakfast,lanch,dinner) and collections(user_id, amount) .
There is a little hack that uses withCount() to select sums of related tables which can be used in your scenario:
User::query()
->select([
'id',
'name',
])
->withCount([
'meals as total_meals' => function ($query) {
$query->select(DB::raw('SUM(breakfast + dinner + lunch)'));
},
'collections as total_collections' => function ($query) {
$query->select(DB::raw('SUM(amount)'));
},
])
->get();
In this query, withCount will perform the joins for you.

How to join on a select result with eloquent in Laravel?

I want to join on a result of other select like this :
SELECT *
FROM TABLE1
JOIN (
SELECT cat_id FROM TABLE2 where brand_id = 2 GROUP BY TABLE2.cat_id) AS b ON TABLE1.id = b.cat_id
is there any way to do this with eloquent?
As it is mentioned here, using DB:raw() will solve your problem.
DB::table('table1')->join(DB::raw("(SELECT
cat_id
FROM table2
WHERE brand_id = 2
GROUP BY table2.cat_id
) as b"),function($join){
$join->on("b.cat_id","=","table1.id");
})->get();
\DB::table('table1')->join('table2' , function($join){
$join->on('table1.id', '=', 'table2.cat_id');
})->select(['table2.cat_id' , 'table1.*'])
->where('table2.brand_id' , '=' , '2')
->groupBy('table2.cat_id');
Depends on whether brand_id is in table1 or table2
You can also use model approach for it.
TABLE1::join('table2' , function($join){
$join->on('table1.id', '=', 'table2.cat_id');
})->select(['table2.cat_id' , 'table1.*'])
->where('table2.brand_id' , '=' , '2')
->groupBy('table2.cat_id');

Multiple whereHas calls are not being aggregated together

I've got multiple calls to whereHas() on an instance of \Illuminate\Database\Query\Builder ($cars):
$cars->whereHas("finance", function (Eloquent\Builder $query) {
$query->where('term'...)
}
$cars->whereHas("finance", function (Eloquent\Builder $query) {
$query->where('payment'...)
}
Is there some way to aggregate the where(s) together without needing to do all the where calls within the containing whereHas?
The SQL query being executed:
SELECT id
FROM `cars`
WHERE EXISTS
(SELECT `finance`.`payment` as payment
FROM `finance`
INNER JOIN `car_finance` ON `finance`.`id` = `car_finance`.`finance_id`
WHERE `car_finance`.`car_id` = `cars`.`id`
AND `payment` >= 50)
AND EXISTS
(SELECT *
FROM `finance`
INNER JOIN `car_finance` ON `finance`.`id` = `car_finance`.`finance_id`
WHERE `car_finance`.`car_id` = `cars`.`id`
AND `payment` <= 200)
AND EXISTS
(SELECT *
FROM `finance`
INNER JOIN `car_finance` ON `finance`.`id` = `car_finance`.`finance_id`
WHERE `car_finance`.`car_id` = `cars`.`id`
AND `term` = 48)
AND EXISTS
(SELECT *
FROM `finance`
INNER JOIN `car_finance` ON `finance`.`id` = `car_finance`.`finance_id`
WHERE `car_finance`.`car_id` = `cars`.`id`
AND `deposit` = 1000)
AND `active` = 1
The SQL query that I would like to be executed:
SELECT *
FROM cars
WHERE EXISTS
(SELECT *
FROM `finance`
INNER JOIN `car_finance` ON `finance`.`id` = `car_finance`.`finance_id`
WHERE `car_finance`.`car_id` = `cars`.`id`
AND deposit = 1000
AND term = 48
AND payment >= 50
AND payment <= 200)
AND active = 1
Your question it's not clear but I think you want this:
$cars->whereHas("finance", function ($query) {
$query->where('deposit', 1000)->where('term', 48)
->whereBetween('payment', 50, 200);
})->where('active', 1)->get();

orWhereHas - parameter grouping on Eloquent query - how do I do this in Laravel?

In an eloquent query I am building, I am placing a constraint on a has relationship using Laravel 4.1's whereHas and orWhereHas methods.
In the example soccer application, I wish to place a constraint on the homeClub and awayClub relationships so that I can the result set will select where the homeClub = Arsenal OR awayClub = Arsenal.
This issue has evolved from an earlier question It seems that when using the orWhereHas method - the resulting sql doesn't group the or constraint.
The query (relevant excerpt that is placing the constraints):
$ret
->where( function( $subquery ) use ( $ret ){
$ret->whereHas('homeClub', function ( $query ){
$query->where('name','Arsenal' );
})->orWhereHas('awayClub',function ( $query ){
$query->where('name','Arsenal' );
});
})
->where( function ( $subquery ) use ( $ret, $parameterValues ){
$ret->whereHas('season', function ($query) use ( $parameterValues ){
$query->where('name', $parameterValues['season_names'] );
});
} )
->whereHas('territory',function( $query ) use ( $parameterValues ){
$query->where('region','Australia');
})->get()->toArray();
This produces the sql:
SELECT * FROM `broadcasts` WHERE
(SELECT count(*) FROM `uploads` WHERE `broadcasts`.`upload_id` = `uploads`.`id` and `type` = 'international-audience') >= '1'
and
(SELECT count(*) FROM `clubs` WHERE `clubs`.`id` = `broadcasts`.`home_club_id` and `name` = 'Arsenal') >= '1'
or
(SELECT count(*) FROM `clubs` WHERE `clubs`.`id` = `broadcasts`.`away_club_id` and `name` = 'Arsenal') >= '1'
and
(SELECT count(*) FROM `seasons` WHERE `broadcasts`.`season_id` = `seasons`.`id` and `name` = '2012/13') >= '1'
and
(SELECT count(*) FROM `territories` WHERE `broadcasts`.`territory_id` = `territories`.`id` and `region` = 'Australia') >= '1'
But, this isn't what I want, because referring to the eloquent statement, the club queries are grouped and the query above either selects the homeClub constraints OR, the awayClub, season name, territory region. What I'm intending is the following SQL:
SELECT * FROM `broadcasts` WHERE
(SELECT count(*) FROM `uploads` WHERE `broadcasts`.`upload_id` = `uploads`.`id` and `type` = 'international-audience') >= '1'
and
((SELECT count(*) FROM `clubs` WHERE `clubs`.`id` = `broadcasts`.`home_club_id` and `name` = 'Arsenal') >= '1'
or
(SELECT count(*) FROM `clubs` WHERE `clubs`.`id` = `broadcasts`.`away_club_id` and `name` = 'Arsenal') >= '1' )
and
(SELECT count(*) FROM `seasons` WHERE `broadcasts`.`season_id` = `seasons`.`id` and `name` = '2012/13') >= '1'
and
(SELECT count(*) FROM `territories` WHERE `broadcasts`.`territory_id` = `territories`.`id` and `region` = 'Australia') >= '1'
Note.. the parentheses on the club subquery.
Does anyone know how I would write this as the eloquent query? I really don't want to have to revert to fluent / joins.
You need to reference the query passed through to the where closure. Otherwise you are adding the grouped where clauses to the main query bypassing any groupings:
$ret
->where( function( $query ){
$query->whereHas('homeClub', function ( $subquery ){
$subquery->where('name','Arsenal' );
})
->orWhereHas('awayClub',function ( $subquery ){
$subquery->where('name','Arsenal' );
});
})
->where( function ( $query ) use ( $parameterValues ){
$query->whereHas('season', function ($subquery) use ( $parameterValues ){
$subquery->where('name', $parameterValues['season_names'] );
});
})
->whereHas('territory',function( $query ) use ( $parameterValues ){
$query->where('region','Australia');
})
->get();

Resources