검색, 정렬 기능 - join, outer 조인 이론

3607 단어 FlaskpythonFlask

이제 책에서 제공하는 마지막 기능인 검색, 정렬 기능이다.
특히 검색 기능을 사용하려면 조인, 아우터조인, 서브쿼리라는 개념을 알아야한다. 다행히 나는 SQLD 자격증 공부를 했을때 개념들은 공부 했었다.

조인의 경우 교집합이라고 보면 쉽다.

Question.query.join(User).filter(User.username=='홍길동')

위 코드는 Question모델과 User모델을 조인해서 User.username이 홍길동인 Question모델을 찾는것이다.

그런데 검색 기능을 위해서는 그냥 조인이 아닌 아우터 조인이 필요하다. 아우터조인을 먼저 설명하고 왜 필요한지 설명한다.

일단 아우터조인이 무엇인지 아래 표를 예를들어 설명한다.

<전교생 학년반 테이블>

<과목 우수자 테이블>


여기서 조인으로 두 테이블의 이름 컬럼값이 같은 경우를 조건으로하면 과목 우수자인 바둑이, 철수, 초코만 나오게된다.

하지만 아우터 조인의 경우, 모든 전교생을 출력하되 전교생테이블 이름 컬럼값이 과목 우수자 테이블 이름 컬럼에 존재할때 과목과 등수 정보를 가져온다. 즉, 기준 테이블의 데이터가 누락되지 않으면서 조건에 맞는 칼럼이 있다면 가져오는것이다.


검색 기능은 특정 문구를 검색 했을때 질문제목, 질문내용, 질문작성자, 답변내용, 답변작성자에 해당 문구가 포함된 질문 목록들을 조회해야 한다.

예를들어, '안녕하세요'라는 문구를 포함한 질문내용 또는 답변내용을 찾아 질문 목록을 조회하려고 조인을 사용하면, 답변이 없는 질문들을 모두 제외 하기때문에 원하는 것 처럼 나오지 않는다.

하지만 이런 경우에는 또 조인이 필요하다고한다... 답변이 존재하는 질문글 중에서, 질문글이나 답변글에 '안녕하세요'라는 문구가 포함된 질문글 목록을 조회할 수 있다.

하지만 질문에 검색한 문구가 없지만, 답변에는 문구가 있는 질문 목록을 조회하려면 반드시 조인이 필요하다.

개념은 알겠는데 막상 적용하려니 되게 헷갈린다.

그러면, 모든 질문글에서(답변유무 상관x) '안녕하세요' 문구가 있는 목록을 뽑아내고 '안녕하세요' 문구가 있는 질문글 답변글을 조인해서 뽑아낸 다음에 중복을 제거하면 될듯하다. 이 기능이 바로 아우터조인이다.

기준 테이블 : 질문글
대상 테이블 : 답변글

  1. 질문글에서 먼저 '안녕하세요' 문구가 있는 질문글 목록을 뽑아낸다.
  2. 그리고 답변이 달린 질문글 중에 질문내용, 답변내용에 '안녕하세요' 문구가 있는 질문글 목록을 뽑아낸다.
  3. 그리고 둘을 합친다. 그럼 1번에서도 뽑힌글이 2번에서도 뽑힐수도있다. 이때 중복으로 뽑힌 부분은 1번만 출력될 수 있도록 제거하면 된다.


현재 질문글은 8개, 답변은 26개다.

두글을 outerjoin하면 27개가 나온다.

질문글1 답변1
질문글1 답변2
질문글1 답변3
...
질문글7 답변25
질문글7 답변26
질문글8

이런식으로 총 27개가 나온다. 여기서 이제 중복을 없애주면 질문글 개수인 8개가 남는다.

현재, 안녕하세요 문구가 답변이 없는 질문글에 1개, 다른 질문글 3개에 답변글로 각각 3개가 있다. outerjoin후 중복을 제거해주니 총 4개로 잘 나온다.

이미 아우터조인한 내용을 다시 또 조인해야하는 등의 조인이 복잡한경우 서브 쿼리로 만들어 사용한다.

서브쿼리는 db.session.query로 만든 쿼리에 subquery 함수를 이용해 만든다.

sub_query = db.session.query(Answer.question_id, Answer.content, User.username)\
	.join(User, Answer.user_id == User.id).subquery()

Answer 모델과 User 모델을 조인해 만든 서브쿼리다.
User 모델과, Answer.user_id와 User.id값이 같을 경우 조인 --> 답변의 질문id, 답변의 내용, User의 username을 조회하여 쿼리로 저장.

Question.query.outerjoin(sub_query, sub_query.c.question_id == Question.id).distinct()

이 서브쿼리를 Question 모델과 아우터조인을 할 수 있다.

sub_query.c.question_id 에서 c는 서브쿼리의 조회항목을 의미한다.
위 코드는 Question과 서브쿼리를 아우터조인후 question_id 항목이 Question항목의 id랑 같은경우를 조회한다. 이 경우에도 중복을 배제한다.

kw = request.args.get('kw', type=str, default='')

GET방식으로 검색할때 플라스크로 입력받는 방법이다.

검색 기능은 GET 방식을 사용해야한다. POST 방식으로 전달하면 page도 POST방식으로 전달해야 하는데, 뒤로가기등으로 이전에 접속한 페이지로 다시 갈때 같은 POST 요청 발생으로 중복을 방지해 오류를 발생시킨다.

db 부분이 좀 햇갈린다. 서브쿼리 등을 완전히 이해할때까지 반복적으로 읽어보면서 관련 내용을 찾아 공부 해야겠다.

다음에는 이 내용을 가지고 기능을 구현한다.

좋은 웹페이지 즐겨찾기