eloquent query with join and sum - laravel

I have these tables:
A: id, total_import, other_columns
B: id, a_id, partial_import
I want know all rows in A where the sum of B.partial_import is less then A.total_import.
This is my query:
select A.*, sum(B.partial_import) as sum
from A
inner join B on A.id = B.a_id
group by B.a_id
having sum < A.total or sum is null
Any suggestions on the best solution to replicate it in eloquent will be appreciated.

The fastest way and also a good way is to use query builder.
If you want to use Collection instead, then you should firsly set relationships, and then to build your eloquent query.
I suppose that your A table has many B rows, so the relation could look like (inside A model):
public function B()
{
return $this->hasMany('App\B');
}
Then your eloquent will look like:
App\A::all()
->B()
->groupBy('B.a_id')
->havingRaw('sum < A.total or sum is null');
If you want to use query builder your code will look like this:
Firstly at the beginning of your controller use:
use DB;
And then your query will be something like the following code:
DB::table('A')
->join('B', 'A.id', '=', 'B.a_id')
->groupBy('B.a_id')
->havingRaw('sum < A.total or sum is null')
->selectRaw('A.*, sum(B.partial_import) as sum')
->get();

Related

Eloquent query distinct sub id

I have these models
I want to make a query that shows me all the products whose stock quantity> 0 and that does not repeat the products.
My query:
$stock_products_limit = Stock::distinct('product_id')->where('quantity', '!=', 0)->get();
This would be much easier using a size chart relating it to stocks ... but for now I don't have it
I need the model to return me, and then do a foreach:
#foreach($stock_products_limit as $stock_product)
#foreach($stock_product->product->product_images as $i=>$product_image)
...
#endforeach
...
#enforeach
In my models I have the hasMany and belongsTo relations made
How could I make the query? I've been trying the distinct, group by ... but nothing works for me. It only removes the ones with quantity 0 and repeats the product ID ...
Example of the query I want:
SELECT DISTINCT(stocks.product_id)
FROM stocks
INNER JOIN products ON stocks.product_id = products.id
WHERE quantity != 0
ORDER BY product_id
LIMIT 10;
Another example query (but LIMIT doesn't work with IN)
SELECT * from products where id in (SELECT DISTINCT(product_id)
FROM stocks
INNER JOIN products ON stocks.product_id = products.id
WHERE quantity != 0
ORDER BY product_id)
Instead of making the Stock model as the starting point, you might want to use the Product model. Then you don't even have to think about using DISTINCT. Let's use whereHas
return Product::whereHas('stocks', function ($query) {
$query->where('quantity', '>', 0);
})
->limit(10)
->get();

Eloquent query alternative

How can I write the following in Laravel's Eloquent?
SELECT *
FROM
( SELECT real_estate.property_id,
real_estate.amount_offered,
payee.summa
FROM real_estate
LEFT JOIN
(SELECT property_id,
SUM(amount) AS summa
FROM payments
GROUP BY property_id) payee ON payee.property_id = real_estate.property_id ) yoot
WHERE summa = 0.05 * amount_offered
Been on this for a while now and really can't get around it. Lemme explain the whole cause for the panic.
I have two tables, one for property and another for payments made on those properties. Now at any given time I will like to query for what properties have been paid for to a certain percentage hence the 0.05 which reps 5%. As it is the query works but I need an Eloquent alternative for it. Thanks
Anywhere you have subqueries in your SQL you'll need to use DB::raw with Eloquent. In this case you have a big subquery for the FROM statement, so the easiest way would be to do this:
DB::table(
DB::raw('SELECT real_estate.property_id, real_estate.amount_offered, payee.summa FROM real_estate LEFT JOIN (SELECT property_id, SUM(amount) AS summa FROM payments GROUP BY property_id) payee ON payee.property_id = real_estate.property_id)')
)
->where('summa', DB::raw('0.05 * amount_offered'))->get();
Notice I used DB::raw for the WHERE statment value as well. That's because you are doing a multiplication using a column name, and the value would otherwise be quoted as a string.
If you want to go a step further and build each subquery using Eloquent, then convert it to an SQL string and injecting it using DB::raw, you can do this:
$joinQuery = DB::table('payments')
->select('property_id', 'SUM(amount) AS summa')
->groupBy('property_id')
->toSql();
$tableQuery = DB::table('real_estate')
->select('real_estate.property_id', 'real_estate.amount_offered', 'payee.summa')
->leftJoin(DB::raw('(' . $joinQuery . ')'), function ($join)
{
$join->on('payee.property_id', '=', 'real_estate.property_id');
})
->toSql();
DB::table(DB::raw('(' . $tableQuery . ')'))->where('summa', DB::raw('0.05 * amount_offered'))->get();
In this case, the second approach doesn't have any benefits over the first, except perhaps that it's more readable. However, building subqueries using Eloquent, does have it's benefitfs when you'd need to bind any variable values to the query (such as conditions), because the query will be correctly built and escaped by Eloquent and you would not be prone to SQL injection.

Laravel 4.2 Eloquent using lists() with a join query

I have a query that makes use of multiple joins:
public function scopePurchased($query, $userId)
{
return $query
->join('products','characters.id','=','products.productable_id')
->join('bundle_product','bundle_product.product_id','=','products.id')
->join('bundles','bundles.id','=','bundle_product.bundle_id')
->join('purchases','purchases.bundle_id','=','bundles.id')
->join('users','purchases.user_id','=','users.id')
->whereNull('purchases.deleted_at')
->where('purchases.refunded', false)
->where('products.productable_type', '=', get_class($this))
->where('users.id','=',$userId)
->groupBy('characters.id')
->orderBy('characters.title', 'ASC');
}
And I want to retrieve an array of ID's from this query to use in another scope so:
$query->purchased($userID)->lists('id')
My initial thought was to use lists('id') which complained about an ambiguous query on the ID.
Column 'id' in field list is ambiguous
(
SQL: select `id` from `characters`
inner join `products` on `characters`.`id` = `products`.`productable_id`
inner join `bundle_product` on `bundle_product`.`product_id` = `products`.`id`
inner join `bundles` on `bundles`.`id` = `bundle_product`.`bundle_id`
inner join `purchases` on `purchases`.`bundle_id` = `bundles`.`id`
inner join `users` on `purchases`.`user_id` = `users`.`id`
where `characters`.`deleted_at` is null
and `purchases`.`deleted_at` is null
and `purchases`.`refunded` = 0
and `products`.`productable_type` = Character and `users`.`id` = 1
group by `characters`.`id`
order by `characters`.`title` asc
)
Makes sense, fair enough so I changed the lists to
$query->purchased($userID)->lists('characters.id')
Thinking that naming the table and column should fix it but finding that the lists function drops the 'character.' part and so having the same error.
It appear that lists may not use a dot notation, bring me to my question... Can I escape the dot notation or is there another way to get the list of ID's as an array?
Many thanks
You can alias the column name before using lists:
$query->purchased($userID)->select('characters.id as _id')->lists('_id');
This will avoid any column name conflicts.

Laravel: Improved pivot query

I am successfully querying following and it create 130 queries, I want to optimise it and reduce the number of queries, I have set upped the model and controllers following way.
Post Modal
class Post extends Eloquent {
public function Categories () {
return $this->belongsToMany('Category', 'category_post');
}
}
Category Modal
class Category extends Eloquent {
public function posts () {
return $this->belongsToMany('Post', 'category_post');
}
}
and in the Controller, I am using following query, what following query does is, querying the results based on category id.
$category = Category::with('posts')->where('id','=',$id)->paginate(10)->first();
return Response::json(array('category' => $category));
If anyone can give me a hand to optimise the query, would be really greatful.
You are wrong, it doesn't create 130 queries.
It will create the following 3 queries:
select count(*) as aggregate from `categories` where `id` = '5';
select * from `categories` where `id` = '5' limit 10 offset 0;
select `posts`.*, `posts_categories`.`category_id` as `pivot_category_id`, `posts_categories`.`post_id` as `pivot_post_id` from `posts` inner join `posts_categories` on `posts`.`id` = `posts_categories`.`post_id` where `posts_categories`.`category_id` in ('5');
But the question is what exactly you want to paginate. Now you paginate categories and it doesn't make much sense because there's only one category with selected $id.
What you probably want to get is:
$category = Category::where('id','=',$id)->first();
$posts = $category->posts()->paginate(10);
and this will again create 3 queries:
select * from `categories` where `id` = '5' limit 1;
select count(*) as aggregate from `posts` inner join `posts_categories` on `posts`.`id` = `posts_categories`.`post_id` where `posts_categories`.`category_id` = '5';
select `posts`.*, `posts_categories`.`category_id` as `pivot_category_id`, `posts_categories`.`post_id` as `pivot_post_id` from `posts` inner join `posts_categories` on `posts`.`id` = `posts_categories`.`post_id` where `posts_categories`.`category_id` = '5' limit 10 offset 0;
If you would like to improve it, you will probably need to not use Eloquent in this case and use join - but is it worth it? You would now need to manually paginate results without paginate() so it would probably won't be want you want to achieve.
EDIT
What you probably do is:
you get all posts that belongs to the category (but in fact you want to paginate only 10 of them)
for each post you want do display all categories it belongs to.
To lower number of queries you should use:
$category = Category::where('id','=',$id)->first();
$posts = $category->posts()->with('categories')->paginate(10);
and to display it you should use:
foreach ($posts as $p) {
echo $p->name.' '."<br />";
foreach ($p->categories as $c) {
echo $c->name."<br />";
}
}
It should lower your number queries to 4 from 130

Doctrine : subquery in DQL

I'm trying to calculate a value using DQL on one single table. Say this table is named "TABLE". It has 5 colums :
id
people_count
animal_count
region_id
type_id
The result I'm looking for is the sum of people (when grouped by region), divided by the sum of animals (when grouped by type);
The SQL would be something like that :
SELECT SUM(people_count) /
(
SELECT SUM(animal_count)
FROM TABLE t2
GROUPED BY type_id
)
FROM TABLE t1
GROUPED BY region_id
How would you do that in Doctrine using DQL?
I resolved my problem by creating another query, executing it and including the result in the first one.
This is probably not the best way to do if you are dealing with a simple example, but it was the only solution for me regarding to my code architecture.
I have a solution but I think there is probably a best solution to resolve your problem.
In any case, make two queries and import results of first query in the second can be a solution. Unfortunately, it's a low-usage for our database. More, sometimes we must execute only one SQL to sort on column result per example.
<?php
namespace AppBundle\Repository;
use AppBundle\Entity\MyClass;
use Doctrine\ORM\EntityRepository;
class MyRepository extends EntityRepository
{
public function findWithSubQuery()
{
return $this->createQueryBuilder('a')
->addSelect(sprintf(
"(SELECT COUNT(b.id) FROM %s AS b WHERE b.a = a.id GROUP BY a.id) AS otherColumn",
MyClass::class
))
;
}
}
I use this solution. Maybe the subquery could be write with DQL ojbect rather that DQL string.

Resources