Laravel postgres Distinct ON query builder - php

I would like to make this query the most Laravel way possible by using the query builder.
Thanks
SELECT DISTINCT ON (spaceid) * FROM (SELECT DISTINCT ON (galleryid) * FROM gallery_spaces ORDER BY galleryid, RANDOM()) AS intermediate ORDER BY spaceid, RANDOM() LIMIT 8
Sorry, this is as close as I can get, but it's not the same
$diferentsGalleries = GallerySpace::select(DB::raw('DISTINCT ON(galleryid) *'))
->wherehas('gallery')
->orderByRaw('galleryid, RANDOM()')
->limit(30)
->pluck('id');
$featured_spaces = GallerySpace::select(DB::raw('DISTINCT ON(spaceid) *'))
->with('space')
->with('gallery')
->wherein('id',$diferentsGalleries)
->orderByRaw('spaceid, RANDOM()')
->limit(8)
->get();

I was finally able to figure it out.
$featured_spaces = GallerySpace::select(DB::raw('DISTINCT ON (spaceid) *'))
->fromSub(function ($query) {
$query->from('gallery_spaces')
->select(DB::raw('DISTINCT ON (galleryid) *'))
->orderby('galleryid')
->inrandomorder();}, 'intermediate')
->orderby('spaceid')
->inrandomorder()
->limit(8)
->get();

Laravel offers many ways, none are better than others, having your query the easiet way is to use db facade
$result=DB::select("SELECT DISTINCT ON (spaceid) * FROM (SELECT DISTINCT ON (galleryid) * FROM gallery_spaces ORDER BY galleryid, RANDOM()) AS intermediate ORDER BY spaceid, RANDOM() LIMIT 8");

Related

How to do order by the eloquent query without using join relationship in laravel?

How to order laravel eloquent query using parent model?
I mean I have an eloquent query where I want to order the query by its parent without using join relationship?
I used whereHas and order by on it, but did not work.
Here is a sample of my code:
$query = Post::whereHas('users')->orderBy('users.created_at')->get();
If you want to order Post by a column in user you have to do a join in some way unless you sort after you retrieve the result so either:
$query = Post::select('posts.*')
->join('users', 'users.id', 'posts.user_id')
->orderBy('users.created_at')->get();
Note that whereHas is not needed anymore because the join (which is an inner join by default) will only result in posts that have a user.
Alternatively you can do:
$query = Post::has('users')
->with('users')
->get()
->sortBy(function ($post) { return $post->users->created_at; });
The reason is that eloquent relationships are queried in a separate query from the one that gets the parent model so you can't use relationship columns during that query.
I have no clue why you wanted to order Posts based on their User's created_at field. Perhaps, a different angle to the problem is needed - like accessing the Post from User instead.
That being said, an orderBy() can accept a closure as parameter which will create a subquery then, you can pair it with whereRaw() to somewhat circumvent Eloquent and QueryBuilder limitation*.
Post::orderBy(function($q) {
return $q->from('users')
->whereRaw('`users`.id = `posts`.id')
->select('created_at');
})
->get();
It should generate the following query:
select *
from `posts`
order by (
select `created_at`
from `users`
where `users`.id = `posts`.id
) asc
A join might serve you better, but there are many ways to build queries.
*As far as I know, the subquery can't be made to be aware of the parent query fields
You can simply orderBy in your Post model.
public function users(){
return $this->belongsTo(User::class, "user_id")->orderByDesc('created_at');
}
I hope this helps you.
You can try
Post::query()
->has('users')
->orderBy(
User::select('created_at')
->whereColumn('id', 'posts.user_id')
->orderBy('created_at')
)
->get();
The sql generated would be like
select * from `posts`
where exists (select * from `users` where `posts`.`user_id` = `users`.`id`)
order by (select `created_at` from `users` where `id` = `posts`.`user_id` order by `created_at` asc) asc
But I guess join would be a simpler approach for this use case.
Laravel Docs - Eloquent - Subquery Ordering

laravel elequent builder join with paginate

i have this query to return data from two tables based on DISTINCT destination_tbls.destination
like the following:
SELECT DISTINCT
destination_tbls.destination,
MIN(sms_details.id),
MIN(sms_details.msg_timestamp) AS TIMESTAMP,
MIN(destination_tbls.count)
FROM
sms_details
JOIN
(
SELECT
*
FROM
destination_tbls
)
destination_tbls
ON destination_tbls.id = sms_details.destination_tbls_id
GROUP BY
destination_tbls.destination;
Now how to use the paginate with them,
I tried something like this but don't work:
DB::select('
SELECT DISTINCT destination_tbls.destination,MIN(sms_details.id),MIN(sms_details.msg_timestamp) AS TIMESTAMP,MIN(destination_tbls.count)
FROM sms_details
JOIN(SELECT * FROM destination_tbls) destination_tbls ON destination_tbls.id=sms_details.destination_tbls_id
GROUP BY destination_tbls.destination
')->simplePaginate(100);
Any help would be appreciated!
If you want to use laravel's pagination, you need to write the query using the query builder methods.
$results = DB::table('sms_details')
->select('destination_tbls.destination')
->selectRaw('min(sms_details.id)')
->selectRaw('min(sms_details.msg_timestamp) as timestamp')
->selectRaw('min(destination_tbls.count)')
->distinct()
->joinSub(
function ($sub) {
$sub->from('destination_tbls');
},
'destination_tbls',
function ($join) {
$join->on('destination_tbls.id', '=', 'sms_details.destination_tbls_id');
}
)
->groupBy('destination_tbls.destination')
->simplePaginate(100);
Since you're not really doing anything in the subquery join, you could join the table instead.
$query = DB::table('sms_details')
->select('destination_tbls.destination')
->selectRaw('min(sms_details.id)')
->selectRaw('min(sms_details.msg_timestamp) as timestamp')
->selectRaw('min(destination_tbls.count)')
->distinct()
->join('destination_tbls', 'destination_tbls.id', '=', 'sms_details.destination_tbls_id')
->groupBy('destination_tbls.destination')
->simplePaginate(100);

WhereHas with WhereRaw on one to many relationship (Laravel)

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.

How can I make select in select on laravel eloquent?

I use laravel 5.3
My sql query is like this :
SELECT *
FROM (
SELECT *
FROM products
WHERE `status` = 1 AND `stock` > 0 AND category_id = 5
ORDER BY updated_at DESC
LIMIT 4
) AS product
GROUP BY store_id
I want to change it to be laravel eloquent
But I'm still confused
How can I do it?
In cases when your query is to complex you can laravel RAW query syntax like:
$data = DB::select(DB::raw('your query here'));
It will fire your raw query on the specified table and returns the result set, if any.
Reference
If you have Product model, you can run
$products = Product::where('status', 1)
->where('stock', '>', 0)
->where('category_id', '=', 5)
->groupBy('store_id')
->orderBy('updated_at', 'desc')
->take(4)
->get();
I think this should give you the same result since you pull everything from your derived table.

get user with most articles and its amount with active record or query

I want to get the user that wrote the most articles. I do so fine in two ways with ActiveRecord like the following:
$table = Articles::find()
->select('articles.*, COUNT(*) AS cnt')
->with('user','userDetails')
->groupBy('articles.user_id')
->orderBy(('cnt DESC'))
->limit(10)
->offset($offset)
->all();
and with a query like the following:
$query = (new Query())
->select('articles.user_id, COUNT(*) AS num_articles')
->from('articles')
->join('LEFT JOIN', 'user_details', 'user_details.user_id = articles.user_id')
->groupBy('articles.user_id')
->orderBy('num_articles DESC')
->limit(10)
->offset($offset)
->all();
The problem is that the ActiveRecord gives me further needed informations userDetails that I need. But I do not get the amount of articles of user that should be on cnt
With the Query I get the user_id and the amount of articles. But I do not get it working by joining with userDetails. All of these does not work: LEFT JOIN, RIGHT JOIN, INNER JOIN.
I am interested in resolving both for learning, but for concrete I need help with the ActiveRecord problem.
Okay well I solved it for the ActiveRecord. The ActiveRecords needs a public $var; in the Model. So to get the amount you have to add the mentioned public... to your Model so that in my case:
public $cnt; extends the ActiveRecord of Articles
now I can access it with the given Request in my Question. But this just solves the first point. I am still interested in the second way for more complex Queries.
I dont have much idea about active record but I think the below is something what you are looking for
select * from user_details where user_id in
(select A.user from
(select user_id as user, COUNT(*) AS num_articles
from articles group by user_id order by num_articles desc LIMIT 10)A
);
for second point you should include required column from joined table to select statement:
$query = (new Query())
->select('articles.user_id, COUNT(*) AS num_articles, user_details.username as username')
->from('articles')
->join('LEFT JOIN', 'user_details', 'user_details.user_id = articles.user_id')
->groupBy('articles.user_id')
->orderBy('num_articles DESC')
->limit(10)
->offset($offset)
->all();

Categories