programing

EF가 불필요한 null-check를 사용하여 SQL 쿼리를 생성하는 이유는 무엇입니까?

padding 2023. 10. 15. 17:03
반응형

EF가 불필요한 null-check를 사용하여 SQL 쿼리를 생성하는 이유는 무엇입니까?

문자열 필드에서 검색할 때 EF에서 끔찍한 쿼리를 생성하는 문제를 발견했습니다.그것은 게으른 프로그래머 스타일의 쿼리를 만들어 전체 인덱스를 스캔하도록 강요하는 널 체크(null checking)를 포함합니다.

다음의 질문을 고려합니다.

  1. 쿼리 1

    var p1 = "x";
    var r1 = ctx.Set<E>().FirstOrDefault(
                            subject =>
                                p1.Equals(subject.StringField));
    
  2. 쿼리 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 체크를 무효화합니다.

for @Martin

세트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

반응형