-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Add support for STRING_SPLIT when using .Contains() #15593
Comments
Notice that using this requires the user database to be compatibility level 130, as well as hosted on SQL Server 2016+ https://docs.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql?view=sql-server-2017 |
Absolutely. This definitely wasn’t meant to be a fix for everyone, but it’s a really great option for those that are on SQL Server 2016, and can use 130 compatibility level. |
@jeradrose How do you suggest the query translator behaves when the server reaches it's 2100 parameter limit? In the mean time, if this is affecting your query plan cache, I suggest setting up an agent job and periodically purging one-time query plans from the cache/store that are more than X minutes old. I've had good luck with this in environments where developers don't understand SQL and create big messes. |
@jzabroski what I'm requesting will only require a single parameter for a comma-separated string, regardless of the number of values. So this shouldn't be impacted by the 2100 parameter limit. In fact, I believe that Re: purging the plan cache, we have looked into this a bit, and actually have set up |
Sorry, good point. Do you know if this is faster than supplying a SqlClient table valued parameter? I guess the other side of things is I imagine most SQL dialects support table valued parameters, so which do you think is most portable? As for purging the plan cache, YMMV but the basic idea is to get the top 10 most expensive ad-hoc query plans with a single use count, and evict them from the cache by their plan handle. The core thing to stress here is that this will be especially beneficial on servers with 32GB or less memory. Glen Barry has a good script for identifying the offenders - you just need to take his output and call DBCC FREEPROCCACHE on the individual plan_handle values of the highest cache memory-intensive plans. Good luck. |
This is a vastly underrated feature which would make a huge impact. Please also make this work for
|
Duplicate of #13617 |
Work on implementing Contains via SQL Server OPENJSON is currently in progress; this is similar to STRING_SPLIT but is better in various ways (e.g. quoting issues). |
Summary
.Contains()
in a.Where()
expression translates to a SQL query that uses literal values, causing poor plan caching. SQL Server 2016 supportsSTRING_SPLIT
, which can be used to pass in a single string parameter containing a delimited list of values. This is a feature request to add support so that.Contains()
can leverageSTRING_SPLIT
to drastically improve plan caching.Steps to repro
Currently when trying to match a list of values,
.Contains()
within a.Where()
clause gets translated to a list of literal values.For example, this:
Gets translated to:
Unfortunately, this creates a massive number of query plans for every combination of values, which often means around one per query that's executed, which has two big downsides:
EF6 would translate these to a set of
OR
expressions with a large number of parameters. This is a bit better for plan caching, but can be expensive for EF, and still requires a plan for each number of parameters.Proposed solution
One solution that solves this is for EF Core to support the SQL Server 2016
STRING_SPLIT
function to allow a single string to be passed in for the list of values in a single parameter, allowing a single plan to be used for each query.We came up with an extension to use
.FromSql()
to supportSTRING_SPLIT
:Which allows us to do the same query as above:
But instead gets translated to a query using
STRING_SPLIT
:Unfortunately, this solution also has some downsides:
.WhereIn()
filters, since.FromSql()
can't be chained.IQueryable<>
, and we don't yet have a solution to work withExpression<Func<>>
, which we sometimes use for reusable filtering.ICollection<>
, so can't be used on.Any()
in subqueries, such as:So this is a request for updating the
.Contains()
SQL translation to supportSTRING_SPLIT
natively, which should address all of the above issues.Note that this is similar to this issue, except I'm actually not asking for a solution for parameterizing each value -- I'm proposing for the values to be sent in as a delimited string in a single parameter, specifically for
STRING_SPLIT
support in SQL Server 2016.Further technical details
EF Core version: 2.2.4
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10 x64
IDE: Visual Studio 2017 15.9.11
The text was updated successfully, but these errors were encountered: