Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Django ORM에서는 어떻게 SQL Where절 조건 순서를 고정할 수 있을까?

Django ORM에서는 어떻게 SQL Where절 조건 순서를 고정할 수 있을까?

AhnSeongHyun

August 15, 2023
Tweet

More Decks by AhnSeongHyun

Other Decks in Programming

Transcript

  1. v
    Django ORM에서는 어떻게 SQL
    where절 조건 순서를 고정할 수
    있을까? 

    안성현

    View full-size slide

  2. v
    목차
    Trigger 


    Django ORM 이 SQL이 되기까지 


    History 


    결론


    View full-size slide

  3. 발표자 소개 

    안성현 @sh84ahn

    페이히어 CTO (2020~) 

    뱅크샐러드 송금 테크리드 

    백엔드 개발자, 글쓰기

    ash84.io






    View full-size slide

  4. Trigger

    Django ORM에서 

    SQL Where절 조건의 순서를 고정하고
    싶은데

    어떻게 어떻게 찾은 방법.. 


    View full-size slide

  5. Trigger

    Q객체를 쓰면 순서가 바뀌지 않는다는 제보


    사내 블로그를 쓰기로 했지만


    블로그는 쓰지않고…. 😇

    파이콘 발표로 이어지는데… 


    View full-size slide

  6. ORM에서 어떻게 where절의 순서를
    고정할까?
    where절의 순서를 고정할 필요는 없다. 


    그러나, 

    당연히 .filter(a=a, b=b) 로 하면 where a=’a’ and b=’b’ 로 SQL을 생성하지 않을까?


    작성한 순서대로 출력하지 않으면, 

    Django ORM은 어떤 기준으로 순서를 변경할까?
    🧐


    Django Official Site에서 해당 내용을 찾지 못함 




    View full-size slide

  7. 오늘의 대상 ORM 


    View full-size slide

  8. 기대했던 SQL 

    적은 순서대로 나오겠지? 


    View full-size slide

  9. 실제 SQL
    달라진 순서, 뭐가 문제일까? 


    View full-size slide

  10. filter chaining

    하나의 필터 여러조건를 filter chaining으로 구성 가능 


    View full-size slide

  11. filter chaining SQL
    원하는 순서대로 출력 


    View full-size slide

  12. Q를 사용하면 고정?

    filter chaining 대신에 Q를 이용해서 다르게 표현 가능 


    View full-size slide

  13. Q를 사용하면 고정?

    Q를 사용하면 순서를 고정이 된다?!! 😇


    View full-size slide

  14. 왜 다를까? 


    View full-size slide

  15. Django ORM 이 SQL이 되기까지 

    - filter 와 query에 대해서


    View full-size slide

  16. filter를 알아보자 


    View full-size slide

  17. QuerySet.filter() 함수 

    기본적인 역할은 새로운 QuerySet 인스턴스 생성 반환 

    combined_queries가 있으면 에러 발생


    View full-size slide

  18. filter() 함수 내 combined_quires 예제 

    django.db.utils.NotSupportedError:
    Calling QuerySet.filter() after union() is not supported.

    union, difference, intersection 같은 함수 이후에 filter 사용시 에러 발생 


    View full-size slide

  19. QuerySet._filter_or_exclude()
    QuerySet의 clone을 만들어서 반환하는 부분 


    View full-size slide

  20. QuerySet._chain()

    View full-size slide

  21. QuerySet._chain()

    View full-size slide

  22. QuerySet._filter_or_exclude_inplace
    negate : 부정 여부 

    negate 여부에 따라 ~Q 또는 Q 객체를 생성

    Query의 add_q() 함수 호출 


    View full-size slide

  23. negate 가 True인 케이스 

    False
    False
    True

    View full-size slide

  24. QuerySet._filter_or_exclude_inplace
    args:() kwargs:{'seller_id': 1, 'cooking_day__gte': '2021-08-01', 'status': 'ORDERED'}

    filter 함수에서 전달한 조건들이 kwargs를 통해서 Q까지 전달 

    filter 함수는 내부적으로 Q객체를 만들고 _query 에 추가 


    View full-size slide

  25. Q 클래스 

    결정적으로 조건의 순서가 바뀌는 부분

    View full-size slide

  26. Q 클래스 생성자에서 kwargs에 대한 오름차순 정렬 진행 

    kwargs.items:dict_items([
    ('seller_id', 1),
    ('cooking_day__gte', '2021-08-01'),
    ('status', 'ORDERED')
    ])
    sorted:[
    ('cooking_day__gte', '2021-08-01'),
    ('seller_id', 1),
    ('status', 'ORDERED')
    ]

    View full-size slide

  27. Query.add_q
    clause: (AND: 0x125039a00>, object at 0x121ebe0a0>, 0x125010670>)

    View full-size slide

  28. Query.add_q
    self.where: (AND: 0x16afe5dc0>, 0x16afe5ee0>, )

    View full-size slide

  29. QuerySet에 대한 복사본을 생성 반환하는 역할

    해당 복사본에 filter로 전달된 조건들(kwargs)을 Q객체로 생성됨

    생성 시점에 조건들에 대해서 정렬(sorted) 수행 

    해당 복사본의 _query에 생성한 Q객체를 추가 


    filter 정리 


    View full-size slide

  30. .filter(a=a, b=b) vs. .filter(a=a).filter(b=b) 

    filter() 함수를 호출하면 1개의 Q객체가 만들어지고 where node에 들어가게 된다. 

    1개의 Q객체 내 3개의 조건 데이터를 가지게 됨 

    3개의 조건 데이터가 Q객체 생성 시점에 정렬 

    where node에 추가 


    View full-size slide

  31. .filter(a=a, b=b) vs. .filter(a=a).filter(b=b)
    3개의 Q객체가 생성, 각 객체가 하나씩의 조건

    정렬을 해도 조건이 하나기 때문에 정렬영향을 받지 않음 

    이후, where node에 호출 순서대로 추가됨 


    View full-size slide

  32. .filter(a=a, b=b) vs. .filter(a=a).filter(b=b)
    args:() kwargs:{'seller_id': 1}
    clause: (AND: 0x124a43c10>)
    self.where: (AND: 0x124a43c10>)
    args:() kwargs:{'cooking_day__gte': '2021-08-01'}
    clause: (AND: )
    self.where: (AND: 0x124a43c10>, )
    args:() kwargs:{'status': 'ORDERED'}
    clause: (AND: )
    self.where: (AND: 0x124a43c10>, ,
    )

    View full-size slide

  33. Q를 사용하면 왜 고정될까?
    .filter 가 호출이 되면 Q가 만들어지고 조건들이 정렬될텐데?


    View full-size slide

  34. Q를 사용하면 왜 고정될까?
    args:('ORDERED'))>,) kwargs:{}
    clause: (AND: 0x1623c1850>, ,
    )
    self.where: (AND: 0x1623c1850>, ,
    )

    View full-size slide

  35. Q를 사용하면 왜 고정될까?
    args:('ORDERED'))>,) kwargs:{}
    args는 정렬하지 않는다. 

    작성한 순서대로 where 절의 조건으로 출력 


    View full-size slide

  36. query에 대해서 알아보자

    View full-size slide

  37. QuerySet.query
    Q객체 생성 후 self._query.add_q() 함수를 통해서 추가된다. 


    View full-size slide

  38. Query.sql_with_params
    .query property print시, sql_with_params() 함수 호출

    SQLCompiler가 sql을 생성해서 리턴 


    View full-size slide

  39. SQLCompiler 클래스
    django.db.models.sql.compiler


    다양한 SQLCompiler 가 존재 

    select, insert, update, delete, aggregate


    View full-size slide

  40. SQLCompiler 클래스
    as_sql() 함수에서 각 목적에 맞는 쿼리 생성 

    SQLUpdateCompiler의 예시 


    View full-size slide

  41. Query - SQLCompiler 클래스
    Query
    InsertQuery(Query)
    UpdateQuery(Query)
    DeleteQuery(Query)
    AggregateQuery(Query)
    SQLCompiler
    SQLInsertCompiler(SQLCompiler)
    SQLUpdateCompiler(SQLCompiler)
    SQLDeleteCompiler(SQLCompiler)
    SQLAggregateCompiler(SQLCompiler)

    View full-size slide

  42. Query - SQLCompiler 클래스

    View full-size slide

  43. 왜 Q생성시의 정렬기능을 넣었을까? 

    의문을 갖게 됨 

    도대체 왜 Q객체의 생성시, kwargs만 정렬을 할까? 

    정렬을 하지 않으면, .filter 와 .filter chaining의 결과가 다르지
    않을텐데? 


    django는 오픈소스 

    GitHub를 통해서 추적을 할 수 있다. 


    View full-size slide

  44. django repository
    https://github.com/django/django/commit/b95c49c954e3b75678bb258e9fb2ec30d0d960bb


    View full-size slide

  45. code.djangoproject.com
    https://code.djangoproject.com/ticket/29125


    View full-size slide

  46. Python 3.6 release note
    https://docs.python.org/3/whatsnew/3.6.html#pep-468-preserving-keyword-argument-order

    View full-size slide

  47. 처음에 의문을 품었던 3가지 질문 

    .filter(a=a, b=b) vs. .filter(a=a).filter(b=b) 는 sql문이 왜 다른가? 


    .filter() 함수 호출시 Q객체가 만들어지고 

    Q객체 생성자에서 조건이 전달되는 kwargs가 오름차순 정렬

    작성한 조건 순서가 변경된다. 


    View full-size slide

  48. 처음에 의문을 품었던 3가지 질문
    Q객체를 사용하면 왜 고정이 될까? 


    .filter() 함수 호출시 Q객체를 인자로 전달하면 

    args로 전달이 되게 되고 .filter() 함수에 대한 Q객체 생성시 

    args는 정렬을 수행하지 않기 때문에 작성한 그대로가 보장된다. 


    View full-size slide

  49. 처음에 의문을 품었던 3가지 질문
    Django ORM 에서 어떻게 SQL Where 절의 순서를 고정할까?


    Q객체의 kwargs 정렬을 피해서 작성하면 된다. 

    - .filter chaining 사용, 1 filter, 1 조건

    - Q객체 사용, .filter() 함수에 args로 넘기도록 작성 


    View full-size slide

  50. v
    감사합니다


    View full-size slide