Skip to content
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

Parameter limitation on prefetch_related #7

Closed
techknowfile opened this issue Mar 2, 2021 · 7 comments
Closed

Parameter limitation on prefetch_related #7

techknowfile opened this issue Mar 2, 2021 · 7 comments

Comments

@techknowfile
Copy link

This is an issue that of course also exists in the ESSolution library that this repo is forked from.

When using prefetch_related to optimize queries for many-to-one and many-to-many relationships, Django generates an IN clause containing all of the primary keys that a M:1 model's foreign key should be filtered on, thus solving the N+1 problem.

However, MS SQL Server has a 2100 parameter limit, which causes the rdbms to fail when trying to prefetch objects with more than 2100 possible foreign keys. It would be awesome if the driver could add support for handling this stark limitation.

The official MS SQL Server IN clause documentation recommends creating a temp table and then joining over that. Without being too familiar with Django ORM internals, I don't know how feasible it would be to override the prefetch_related as_sql query to implement this solution.

There is a related issue in the ESSolutions/django-mssql-backend repo, where a suggested workaround is to override Django's In lookup class to basically pass in a single giant parameter string and then have SQL Server split it via {lhs} IN ( SELECT * FROM STRING_SPLIT(CONVERT(nvarchar(max), %s),','))). While this seems to work when filtering a queryset explicitly with __in, the overridden lookup class is not used for prefetch_related calls.

A committed solution would be awesome, but I'd also love any advice you may have as a temporary workaround.

@vwarchu
Copy link
Contributor

vwarchu commented Mar 2, 2021

@techknowfile - Thanks for the issue report. We'll take a look and get back to you regarding a potential solution and/or temp workaround. Cheers

@techknowfile
Copy link
Author

Thanks, @vwarchu. I think we're going to temporarily hack around this issue by monkey-patching the base.execute() function to identify when we have too many parameters with a query that contains a single IN clause and move those parameters into a temp table instead.

@absci
Copy link
Contributor

absci commented Mar 5, 2021

@techknowfile I'm able to reproduce the issue, I think Django has some code to split parameters. But it doesn't work with SQL Server. I'll try to provide a solution for that.

@absci
Copy link
Contributor

absci commented Mar 12, 2021

I have committed a fix in dev branch. Let me know if you encountered any problems.

@techknowfile
Copy link
Author

techknowfile commented Mar 12, 2021

Thank you, @absci! We had come to a similar approach of creating a temp table, but this is a much cleaner place of putting it

One issue we have rn is that your solution doesn't work when UUIDs are used as the PK. Added comments for suggested changes to your commit

@absci
Copy link
Contributor

absci commented Mar 12, 2021

I see. If that's the case, it might be better to use different SQL data types for each Django models.

absci added a commit that referenced this issue Mar 16, 2021
…d unicode

Skipped 1 test cause CI fail, will fix later
@absci absci closed this as completed Apr 9, 2021
@absci
Copy link
Contributor

absci commented Apr 9, 2021

This issue has been fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants