2025.8.1: SQL의 두 가지 스타일

jiyongg·2025년 8월 1일

TIL: Today I Learned

목록 보기
13/30

벌써 8월이라는게 믿기질 않는다. 8월이 되었다는 것은 기말고사가 끝난 날보다 2학기 개강일이 더 가깝게 되었다는 것을 의미하기도 한다... 예비수강신청도 열렸고, 다음 주면 수강신청이던데 나는 아직 수강신청 계획을 마무리하지 못했다. 이때까지 이런 경우가 있었던가?

7월에는 이것저것 공부하고, 미니 해커톤을 통해 간단한 서비스도 만들어 보고, 중순부터는 TIL 공유 활동으로 서로 공부한 내용을 공유하는 시간을 가졌다. 작년에 비슷한 시기에는 독학을 했었다. 그때와 비교해본다면 해커톤과 벌금 피하기라는 동기부여가 있어서 그런지, 확실히 올해가 얻어가는 것이 훨씬 많은 느낌이다.

8월에도 TIL은 계속된다. TIL 공유 활동은 22일까지긴 한데, 공유 활동이 끝나도 TIL을 계속 쓸까싶다. 블로그에 TIL을 정리하는 일은 솔직히 좀 고된 일이긴 하다. 그래도 나름 보람찬 일인 것 같다. 블로그에 공유할 만할 정도로 만들기 위해서 레퍼런스도 찾고, 예시를 위해 실습도 하다 보니, 단순히 옵시디언에 정리했던 때보다 얻는 지식의 양도 많아졌음을 느낀다. 그러면 이제 8월의 첫 번째 주제를 시작해보고자 한다.

🎨 두 가지 스타일

이 주제를 간단히 요약하면 다음과 같다.

  • 스타일 1
(SELECT f.species_name,
        AVG(f.height) AS average_height, AVG(f.diameter) AS average_diameter
   FROM flora as f
  WHERE f.species_name = 'Banksia'
     OR f.species_name = 'Sheoak'
     OR f.species_name = 'Wattle'
  GROUP BY f.species_name, f.observation_date)

  UNION ALL

(SELECT b.species_name,
        AVG(b.height) AS average_height, AVG(b.diameter) AS average_diameter
   FROM botanic_garden_flora AS b
  WHERE b.species_name = 'Banksia'
     OR b.species_name = 'Sheoak'
     OR b.species_name = 'Wattle'
  GROUP BY b.species_name, b.observation_date);
  • 스타일 2
(
    SELECT
        f.species_name,
        AVG(f.height) AS average_height,
        AVG(f.diameter) AS average_diameter
    FROM
        flora AS f
    WHERE
        f.species_name = 'Banksia'
        OR f.species_name = 'Sheoak'
        OR f.species_name = 'Wattle'
    GROUP BY
        f.species_name,
        f.observation_date
)

UNION ALL

(
    SELECT
        b.species_name,
        AVG(b.height) AS average_height,
        AVG(b.diameter) AS average_diameter
    FROM
        botanic_garden_flora AS b
    WHERE
        b.species_name = 'Banksia'
        OR b.species_name = 'Sheoak'
        OR b.species_name = 'Wattle'
    GROUP BY
        b.species_name,
        b.observation_date
);

스타일 1은 Simon Holywell의 SQL 스타일 가이드에서 가져온 코드이고, 스타일 2는 스타일 1의 코드를 쿼리 내용을 유지하고 스타일만 바꿔서 다시 쓴 코드이다.

"두 스타일 중 어느 것이 마음에 드는가?"라고 물어보면 사람마다 다르게 대답할 것이다. 각자 선호하는 스타일이 다르다. 누군가는 1번 스타일에서 편안함을 느낄 수도 있고, 누군가는 2번 스타일에서 편안함을 느낄 수도 있다. 그래서 미리 결론을 말하자면, 프로젝트 내에서 일관된 스타일을 사용하는 것이 가장 중요하다는 것이다. 물론 직접 코드를 한줄한줄 스타일에 맞도록 고치는 일은 힘들고, 효율적이지 않다. 이럴 때 사용할 수 있는 포매터도 후술하겠다.

♒ River (강)

위의 스타일 1이 River 스타일의 예시이다. 그런데 이 스타일이 대체 왜 강이라고 불리는 것일까?

SELECT f.species_name,
       AVG(f.height) AS average_height, AVG(f.diameter) AS average_diameter
  FROM flora as f
 WHERE f.species_name = 'Banksia'
    OR f.species_name = 'Sheoak'
    OR f.species_name = 'Wattle'
 GROUP BY f.species_name, f.observation_date

위 예시에서 앞쪽 쿼리만 떼어냈다.

SELECT|
  FROM|
 WHERE|
    OR|
    OR|
 GROUP|

각 줄의 첫번째 키워드들만을 적어본 것이다. 키워드들이 우측 정렬 되어 있다. 거기에 |로 우측 경계를 만들어 보았다.

       |f.species_name,
       |AVG(f.height) AS average_height, AVG(f.diameter) AS average_diameter
       |flora as f
       |f.species_name = 'Banksia'
       |f.species_name = 'Sheoak'
       |f.species_name = 'Wattle'
       |BY f.species_name, f.observation_date

이번에는 각 줄의 첫번째 키워드 다음의 내용을 적은 것이다. 몇 칸을 띄고 좌측 정렬이 되어 있다. 아까와 같은 방법으로 좌측 경계를 만들어 보았다.

다시 둘을 합쳐 보자.

SELECT| |f.species_name,
      | |AVG(f.height) AS average_height, AVG(f.diameter) AS average_diameter
  FROM| |flora as f
 WHERE| |f.species_name = 'Banksia'
    OR| |f.species_name = 'Sheoak'
    OR| |f.species_name = 'Wattle'
 GROUP| |BY f.species_name, f.observation_date

각 줄의 첫 키워드와 그 다음의 내용이 경계를 두고 나눠져 있다. 뭔가 강처럼 보이지 않는가?

이건 막 지어낸 소리가 아니라, 진짜 이래서 River이다. River에 대한 영문 위키백과를 보면 알 수 있다.

이 River 스타일은 위에서 볼 수 있듯, 마치 강 하나를 두고서 서로 마주보고 있는 느낌이다. 첫번째 키워드는 강의 좌측 경계 기준으로 우측 정렬되고, 그 다음 내용들은 강의 우측 경계 기준으로 좌측 정렬되는 스타일이다.

이 스타일은 보기에는 좋지만, 쓰기에는 불편하다는 단점이 있다.

➡️ Indent (들여쓰기)

스타일 2는 Indent 스타일의 예시이다.

SELECT
    f.species_name,
    AVG(f.height) AS average_height,
    AVG(f.diameter) AS average_diameter
FROM
    flora AS f
WHERE
    f.species_name = 'Banksia'
    OR f.species_name = 'Sheoak'
    OR f.species_name = 'Wattle'
GROUP BY
    f.species_name,
    f.observation_date

River와 다르게 SELECT, FROM, WHERE, GROUP BY 등의 절 다음의 내용을 들여쓰고 있는 것을 볼 수 있다.

🖌️ 포매터

결국 중요한 것은 프로젝트 내, 팀 내에서 스타일을 일관되게 유지하는 것이라고 할 수 있다. 그렇지만 이러한 스타일을 유지하기 위해 일일이 한줄한줄 확인하는 것은 규모가 작을 땐 괜찮겠지만, 규모가 커지면 매우 힘든 일이 될 것이다. 그래서 실무에서는 포매터를 도입하여 사용하고 있다고 한다.

🪮 SQLFluff

SQLFluff는 파이썬으로 만들어진 오픈소스 패키지이다. 따라서 컴퓨터에 파이썬이 설치되어 있어야 한다.

  • 설치
$ pip install sqlfluff
  • 검사
$ sqlfluff lint 파일명 --dialect 방언명

SQL에서 방언이란 표준 SQL과 다르게 특정 DBMS에서 사용되는 함수나 문법을 의미한다. 예를 들자면 oracle (Oracle), sqlite (SQLite), tsql (SQL Server에서 쓰인다) 등이 있다.

  • 검사 및 수정
$ sqlfluff fix 파일명 --dialect 방언명

SQLFluff는 기본적으로 Indent 스타일로 포매팅해준다. 공식 문서의 가이드에서 콤마 위치, 들여쓰기 공백 개수 등을 지정하는 법을 설명하고 있다.

또한, lintfix를 사용할 때, --dialect뿐만 아니라 다른 매개변수를 사용해서 특정 규칙만을 포함하거나 특정 구성을 사용할 수 있다. 매개변수 리스트는 공식 문서의 Simple API Commands에서 볼 수 있다.

🪟 VS Code의 확장 기능

VS Code의 마켓플레이스에서 SQL이라는 키워드로 확장 기능을 검색해보면, 다양한 SQL 포매터를 볼 수 있다. 확장 기능의 설명과 기능을 잘 보고, 마음에 드는 것을 하나 설치하면 될 것 같다. 나는 아직 VS Code로 SQL 코드를 쓸 일은 없어서 확장 기능 설치나 사용은 해보지 않곘다.

🔚 결론

처음에는 SQL 코드의 스타일 컨벤션이 궁금해서 시작한 주제였는데, 점점 찾으면서 이 주제는 정해진 답이 없다는 것을 강하게 느꼈다. 찾아보는 글마다 알려주는 내용이 조금씩 달랐다. 그리고 그 글의 댓글에는 글쓴이의 생각에 반대하며 자신이 생각하는 더 좋은 스타일을 제시하는 경우도 있었다. 이러한 가이드들과 논쟁을 읽어보며 큰 그림을 뽑아봤더니, River파와 Indent파로 나눌 수 있었다. 그래서 이 두 스타일 방식에 대해서 정리하는 식으로 접근했다.

여기 적어둔 내용은 최대한 공통된 내용을 뽑아내긴 한 것이지만, 무조건적인 정답은 아니다. 예를 들자면, 같은 River파여도 서브쿼리의 첫 키워드를 4칸 들여쓰기해서 적는 가이드도 있고, 서브쿼리를 강의 우측에 좌측 정렬시키는 가이드도 있다. 그래서 디테일한 부분은 다룰 수가 없었다. 또는 어떤 가이드에서는 위의 River 스타일에서 우측 경계는 유지하되 첫 키워드를 좌측 정렬한 경우도 있었다. 참고로, 이 방식도 River파에 해당한다고 볼 수 있다. 위의 위키백과 문서를 보면 알 수 있겠지만, River가 꼭 일직선의 강이어야 한다는 조건은 없기 때문이다.

결론적으로 위에서도 적었듯, 프로젝트 내에서 일관된 스타일을 사용하고 가독성 좋은 SQL 코드를 작성할 수 있는 방법을 추구하는 것이 제일 중요하다고 할 수 있지 않을까?

profile
그냥 쓰고 싶은 것 쓰는 개발(?) 블로그

0개의 댓글