-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WhereHas query is too slow #18415
Comments
It could be because you are running a |
@AbiriAmir joins add up to what query is selecting, while If you need improved performance for a specific use case, just create a custom scope or a repository class, where you can use the query builder to add joins. Also, in most cases, |
I disagree with that @mrliptontea or maybe it is not clear enough |
@Dylan-DPC I mean by itself, Eloquent cannot use joins because that breaks the API. Every statement would have to be prefixed with the table name in order to avoid errors like |
@mrliptontea thanks for your answer but this block is not equivalent to my query.
Also Also, I think that there must be a solution for excluding the added fields in join statement |
Right, I didn't read your query through 😉
I didn't mean that join is adding fields, you still you need to specify them in select, but it is creating ambiguity when two tables have a column of the same name, while subqueries don't do that. This simple fact makes subqueries usable for the ORM that will work in all cases. Need something more fine-tuned? Write you own query. Simple as that. |
Actually, I don't see it as an impossible task. Eloquent could automatize it. Currently a developer has to replace all Maybe I just do something the wrong way, but it's exactly what I had to do a time ago, because I naively thought Eloquent will generate an efficient query. |
True, it could be done. However, it would require either you to provide a list of all the columns in the model or Laravel would have to run additional
That is not true. As I stated above, in the majority of cases a subquery with
When you use Eloquent query builder, it does. Obviously, to populate relationships correctly, it has to go through relationships but you can mix and match, i.e. you can have a join in the query, to fetch only desired rows, and still use eager loading. There is no definitive list of rules on how you should write your queries. It all depends on your database structure, its size, and what you're trying to achieve. Whenever you have performance issues with your SQL queries, |
@KristofMorva it's very hard to generate an efficient query, especially you have to support diffierent rdbms. for example, there are lots of sql queries which are considered to slow down the performance for mysql, like sub-query, exists. but for oracle, they are just piece of cake. |
maybe the slowness is because you are using 2 |
Yes, I'm sure it'd be complex to solve.
From now on, same here; but it'd great if there was a warning about it on the Eloquent doc page :) |
no you don't need to use "fully raw queries". You can achieve things in QB / Eloquent by using |
I think that's what @jhdxr meant. At least I'm combining Eloquent with QueryBuilder, so it's somewhere between efficiency and code readability. |
@KristofMorva nope. I have seen a lot of people end up with using "raw queries" instead of raw-supported-functions |
@Dylan-DPC fine. I'll edit my comments to prove @KristofMorva is right orz |
Eloquent has some edge cases, when you hit these just switch to using the query builder. Closing since there's no activity on the issue and it doesn't really indicate a bug. |
whereHas producing an AND EXISTS statement is causing tons of slowness (12+seconds) to my queries.. if I run a raw query using left joins it works in under a second. |
@joshuaGlass808 as explained above - it depends. For some queries Also if you want to query only rows that have some relationship you probably want a full join rather than |
For those who are interested in a specific case and to see how much improvement a simple change can make. I do not want to start a discussion about how this is bad programming by the developer. I want to bring focus on the bigger issue. On one side a framework exists to help developers ship more features more frequently, but on the other side a framework can be a pain in terms of performance. This is due to every abstraction layer adding overhead and reducing control over the underlying code-design. Don't get me wrong, I love Laravel. But I think it is important to address this issue in the laravel docs and add a disclaimer, that these kind of queries might cause a lot of performance-loss in the long term. Here is my result by changing 5 lines of code: DB (with indexes) with 2,610 calls per minute from various places But due to my lack of understanding, can somebody please explain to me why this is such a big improvement? |
Laravel is creating a temporary column on literally every row in the database, in which it fills with true or false. If any indexes can be used, they are probably used after this. Any query that uses whereHas is probably going to be a full table scan. Here is a similar query, except it does not induce full table scans.
Further reading: #18415 |
I am also wondering why Laravel uses I had to avoid using Would like to know the reason why to stick with |
After spend hours trying to solve this, i decided to make the query like the old way, it is ugly i know! But is blazing fast, the time to run the query drop from 28s to 300ms. |
I met a similar issue today...
This is very slow:
So I fix the issue with this:
Hope this helps. |
With #18415 (comment), also you may be able to solve like this: $collections = Collection::orderBy('created_at', 'desc')
->hasByNondependentSubquery('images')
->paginate(36); |
https://stackoverflow.com/questions/46785552/poor-wherehas-performance-in-laravel/65358549#65358549 may be of some help. whereHas() indeed is slow as hell |
The reason for laravel For example: // User hasMany Post
Users::has('posts')->get();
// Sql: select * from `users` where exists (select * from `posts` where `users`.`id`=`posts`.`user_id`) The 'exists' syntax is a loop to the external table, and then queries the internal table (subQuery) every time. However, there will be performance problems when the users table has a large amount of data, because above sql It can use // select * from `users` where exists (select * from `posts` where `users`.`id`=`posts`.`user_id`)
// =>
// select * from `users` where `id` in (select `posts`.`user_id` from `posts`) This will greatly improve performance! You can take a look at this package hasin, |
And here we go. It still use subquery and if, for example, posts (or any other DB entity) counted in millions there will be problem. This solution have potential to be faster in some situations but on even moderately loaded database it could become an issue. |
@Bedivierre Thanks for your reply! Maybe you're misunderstand why where exists slowly, when the both table is big, the main reason is external table(users), not internal table(posts), this is determined by the mysql mechanism, you can see this comment3543#41595672, that has been explained and the solution is given. And i saw your comment at stackoverflow, Finally, welcome to use hasin, give start!:smile: |
Cool it work! Really smooth.. |
Description:
As i tested, i founded out that whereHas query is much slower than the same query using join.
Imagine this domain model:
User - Like - Post - Tag
User likes posts
Post belongs to many tags
Tag belongs to many posts
Each post has several tags and each user may like several posts. We call a specified user interested in tag T if he/she had liked at least 10 posts which has tag T. Now, for a specified user U, we are going to find those tags which he/she is interested in.
With whereHas we have the following code:
Note that the relation between like and post is polymorphic.
It produces the following SQL statement:
But the following equivalent SQL statement performs 10 times faster with joins:
I think RDMSs do lots of work to perform joins faster and ORM should take advantage of this.
The text was updated successfully, but these errors were encountered: