EF가 불필요한 null-check를 사용하여 SQL 쿼리를 생성하는 이유는 무엇입니까?
문자열 필드에서 검색할 때 EF에서 끔찍한 쿼리를 생성하는 문제를 발견했습니다.그것은 게으른 프로그래머 스타일의 쿼리를 만들어 전체 인덱스를 스캔하도록 강요하는 널 체크(null checking)를 포함합니다.
다음의 질문을 고려합니다.
쿼리 1
var p1 = "x"; var r1 = ctx.Set<E>().FirstOrDefault( subject => p1.Equals(subject.StringField));
쿼리 2
const string p2 = "x"; var r2 = ctx.Set<E>().FirstOrDefault( subject => p2.Equals(subject.StringField));
쿼리 1이 생성합니다.
WHERE (('x' = [Extent2].[StringField]) OR (('x' IS NULL) AND ([Extent2].[StringField] IS NULL)))
4초안에 실행됩니다.
쿼리 2가 생성합니다.
WHERE (N'x' = [Extent2].[StringField])
2밀리초 안에 실행됩니다.
주변의 일을 아는 사람 있습니까?(아니오 매개변수는 사용자 입력으로 입력되는 상수일 수는 없지만 null일 수는 없습니다.
N.B 프로파일링 시 두 쿼리 모두 EF에 의해 sp_executesql로 준비됩니다. 쿼리가 방금 실행된 경우 쿼리 옵티마이저는 OR 'x' IS NULL 체크를 무효화합니다.
세트UseDatabaseNullSemantics = true
;
언제
UseDatabaseNullSemantics == true
,(operand1 == operand2)
다음과 같이 번역됩니다.WHERE operand1 = operand2
언제
UseDatabaseNullSemantics == false
,(operand1 == operand2)
다음과 같이 번역됩니다.WHERE ( (operand1 = operand2) AND (NOT (operand1 IS NULL OR operand2 IS NULL)) ) OR ( (operand1 IS NULL) AND (operand2 IS NULL) )
이 문서는 Microsoft에서 다음과 같이 문서화합니다.
두 피연산자를 비교할 때 데이터베이스 널 시맨틱이 표시되는지 여부를 나타내는 값을 가져오거나 설정합니다.기본값은 false입니다.
당신은 그것을 당신의 것에 설정할 수 있습니다.DbContext
하위 클래스 생성자는 다음과 같습니다.
public class MyContext : DbContext
{
public MyContext()
{
this.Configuration.UseDatabaseNullSemantics = true;
}
}
또는 이 설정을 사용자의dbContext
아래 코드 예제와 같은 외부의 인스턴스(@GertArnold comment 참조)는 기본 데이터베이스 동작이나 구성을 변경하지 않기 때문에 이 접근 방식이 더 나을 것입니다.
myDbContext.Configuration.UseDatabaseNullSemantics = true;
추가하면 수정할 수 있습니다.[Required]
StringField 속성에서
public class Test
{
[Key]
public int Id { get; set; }
[Required]
public string Bar{ get; set; }
public string Foo { get; set; }
}
string p1 = "x";
var query1 = new Context().Tests.Where(F => p1.Equals(F.Bar));
var query2 = new Context().Tests.Where(F => p1.Equals(F.Foo));
이것은 query1 입니다.
{SELECT [Extent1].[Id] AS [Id], [Extent1].[Bar] AS [Bar], [Extent 1].[푸] AS [푸] FROM [dbo].[테스트] AS [Extent1] WHERE @p__linq_0 = [Extent1].[바]}
그리고 이것은 query2 입니다.
{SELECT [Extent1].[Id] AS [Id], [Extent1].[Bar] AS [Bar], [Extent 1].[푸] AS [푸] FROM [dbo].[테스트] AS [익스텐트1] WHERE (@p__linq_0 = [익스텐트1].[Foo]) OR((@p__linq_0 IS NULL) AND([Extent1]).[bar2] IS NULL)}
제 동료가 방금 정말 좋은 해결책을 찾았습니다.상수를 사용하면 올바른 SQL이 생성된다는 것을 이미 알았기 때문입니다.식의 변수를 상수로 바꿀 수 있는지 궁금했습니다. 알고 보니 가능한 것 같습니다.저는 이 방법이 DB 컨텍스트의 null 설정을 변경하는 것보다 덜 침습적이라고 생각합니다.
public class Foo_test : EntityContextIntegrationSpec
{
private static string _foo = null;
private static DataConnection _result;
private Because _of = () => _result = EntityContext.Set<E>().Where(StringMatch<E>(x => x.StringField));
private static Expression<Func<TSource, bool>> StringMatch<TSource>(Expression<Func<TSource, string>> prop)
{
var body = Expression.Equal(prop.Body, Expression.Constant(_foo));
return Expression.Lambda<Func<TSource,bool>>(body, prop.Parameters[0]);
}
[Test] public void Test() => _result.ShouldNotBeNull();
}
언급URL : https://stackoverflow.com/questions/38433594/why-is-ef-generating-sql-queries-with-unnecessary-null-checks
'programing' 카테고리의 다른 글
구조물의 새 인스턴스(instance)를 만드는 방법 (0) | 2023.10.15 |
---|---|
IIS 8에서 응용 프로그램 풀을 원격으로 중지/시작하는 방법 (0) | 2023.10.15 |
오름차순으로 마지막 20개 순서 선택 - PHP/MySQL (0) | 2023.10.15 |
"일반 오류: 1005 테이블을 만들 수 없습니다" 라라벨 스키마 빌드 및 외부 키 사용 (0) | 2023.10.15 |
PDO를 사용하여 PHP의 결과 배열을 가져오려면 어떻게 해야 합니까? (0) | 2023.10.15 |