7P by GN⁺ | ★ favorite | 댓글 2개
  • 데이터 분석 업무에서 자주 쓰는 SQL 작성 습관과 쿼리 패턴을 모은 목록으로, 모든 RDBMS에 동일하게 적용되지 않을 수 있다는 전제가 있음
  • 가독성 측면에서는 선행 쉼표, WHERE 1=1, 들여쓰기, CTE, 주석, USING을 통해 쿼리를 읽고 수정하기 쉽게 만드는 방식을 권장함
  • 데이터 처리에서는 anti-join, QUALIFY, GROUP BY ROLLUP, EXCEPT처럼 실무에서 결과 필터링·합계 생성·테이블 차이 확인에 쓰는 구문을 예시로 다룸
  • 성능과 정확성 측면에서는 NULL이 섞인 NOT IN, 암시적 형변환, 계산 필드 alias 충돌이 쿼리 결과나 속도를 흔들 수 있음
  • 복잡한 쿼리에서는 실행 순서, 문서 확인, 컬럼 출처 명시, 저장 쿼리 이름 같은 기본 습관이 디버깅과 재사용성을 높이는 데 중요함

SQL 작성 가독성을 높이는 습관

  • 이 저장소는 여러 해 동안 익힌 SQL 팁과 요령을 정리한 목록이며, 데이터 분석가의 일상 업무에서 유용한 것과 처음 SQL을 쓸 때 알았으면 좋았을 내용을 중심으로 함
  • 일부 팁은 모든 RDBMS에 맞지 않을 수 있음
  • 선행 쉼표와 선행 AND

    • SELECT 절의 필드 구분에는 후행 쉼표보다 선행 쉼표를 쓰는 방식을 권장함
    • 새 컬럼인지, 줄바꿈된 코드인지 더 명확하게 보임
    • 줄 길이가 달라도 쉼표 누락 여부를 찾기 쉬움
    • 같은 이유로 WHERE 절 조건 앞에도 선행 AND를 둘 수 있음
  • WHERE 1=1로 조건 테스트 쉽게 하기

    • WHERE 절에 더미 조건 1=1을 넣으면 테스트 중 조건을 주석 처리해도 쿼리가 깨지지 않음
    • 모든 조건을 주석 처리해도 1=1이 남아 쿼리가 계속 실행될 수 있음
  • 들여쓰기와 포매터

    • 들여쓰기는 동료와 미래의 자신이 쿼리를 더 쉽게 읽게 해줌
    • 팀이나 회사 가이드라인이 있으면 따르고, 없으면 자신에게 맞는 방식을 쓰는 편이 좋음
    • 온라인 포매터 poorsql이나 린터 sqlfluff를 사용할 수 있음
  • 복잡한 쿼리에는 CTE 고려

    • inline view를 2~3단계 이상 중첩하면 몇 주 뒤 다시 봤을 때 이해하기 어려운 쿼리가 되기 쉬움
    • CTE는 긴 쿼리를 더 정리된 형태로 만들고, 재사용성과 디버깅을 돕는 방식으로 제시됨
  • 주석은 “왜”를 설명하기

    • 시간이 지난 뒤에는 특정 처리를 왜 했는지 기억하기 어려울 수 있음
    • 주석은 일반적으로 코드가 “어떻게” 동작하는지보다 그렇게 했는지를 설명하는 편이 좋음
    • 예시는 새 CMS가 archive 비디오 포맷을 처리하지 못해 archive 콘텐츠를 제외하는 조건에 주석을 붙임
  • 같은 이름 컬럼 조인은 USING

    • 두 테이블에서 같은 이름의 컬럼으로 조인할 때 USING을 쓰면 ON보다 조인을 간단히 표현할 수 있음
    • USING은 공통 컬럼을 결과에서 중복 제거해 하나만 반환함
    • ON을 사용할 때 공통 컬럼을 명시하지 않으면 ambiguous column name 오류가 날 수 있음

데이터 처리에 유용한 구문

  • anti-join으로 다른 테이블에 없는 행 찾기

    • anti-join은 한 테이블에는 있지만 다른 테이블에는 매칭되지 않는 행을 반환할 때 사용함
    • 예시는 archive되지 않은 콘텐츠의 video_id만 가져오는 상황을 다룸
    • 구현 방식은 여러 가지가 있음
    • LEFT JOIN 후 매칭 테이블의 키가 NULL인 행만 필터링
    • NOT IN과 서브쿼리 사용
    • NOT EXISTS와 상관 서브쿼리 사용
    • NOT INNULL 값 때문에 의도대로 동작하지 않을 수 있어 사용을 권하지 않음
  • QUALIFY로 윈도 함수 결과 필터링

    • QUALIFY윈도 함수 결과를 기준으로 쿼리 결과를 필터링할 수 있게 함
    • inline view 없이 필터링할 수 있어 코드 줄 수를 줄일 수 있음
    • 예시는 제품별 상위 10개 시장을 DENSE_RANK()로 고른 뒤 QUALIFY로 필터링함
    • QUALIFY는 Snowflake, Amazon Redshift, Google BigQuery 같은 큰 데이터 웨어하우스에서만 제공되는 것으로 보인다는 제한이 있음
  • 컬럼 위치 기반 GROUP BYORDER BY

    • 컬럼 이름 대신 컬럼 위치GROUP BY 1, ORDER BY 2처럼 쓸 수 있음
    • 임시 또는 일회성 쿼리에는 유용할 수 있음
    • 프로덕션 코드에서는 항상 컬럼 이름을 직접 참조하는 방식을 권장함
  • GROUP BY ROLLUP으로 총합 만들기

    • GROUP BY ROLLUP은 소계와 총계를 만드는 데 사용할 수 있음
    • 예시는 부서별 급여 합계를 구하면서 전체 급여 합계 행을 함께 생성함
    • Transact-SQL 문서ROLLUP이 컬럼 표현식 조합별 그룹을 만들고 오른쪽에서 왼쪽으로 그룹 수를 줄이며 소계와 총계를 만든다고 설명함
    • COALESCE를 적용하면 총합 행을 Total처럼 표시할 수 있음
    • 총합 행이 결과 하단에 오도록 정렬 컬럼을 신경 써야 함
  • EXCEPT로 두 결과 집합 차이 찾기

    • EXCEPT는 첫 번째 쿼리 결과에는 있지만 두 번째 쿼리 결과에는 없는 행을 반환함
    • EXCEPTUNION ALL을 함께 쓰면 두 테이블이 같은 데이터를 갖는지 검증할 수 있음
    • 반환 행이 없으면 두 테이블이 동일함
    • 반환 행이 있으면 그 행들이 차이를 만드는 원인임

성능과 정확성을 해치는 패턴

  • NULL 가능 컬럼에서는 NOT EXISTSNOT IN보다 나음

    • 비교 대상 컬럼이 NULL을 허용하면 NOT IN은 보통 NOT EXISTS보다 느릴 수 있음
    • Snowflake에서 이 현상을 겪었고, PostgreSQL Wiki의 Don’t Do ThisNOT IN (SELECT ...)가 잘 최적화되지 않는다고 적고 있음
    • NOT IN은 비교 대상 값에 NULL이 있으면 의도대로 동작하지 않음
    • 컬럼이 NULL을 허용한다고 해서 실제 NULL 값이 있다는 뜻은 아니지만, 수정할 수 없는 테이블을 다룰 때는 NOT EXISTS가 속도 개선에 도움이 될 수 있음
  • 암시적 형변환은 느려지거나 실패할 수 있음

    • 컬럼과 다른 데이터 타입의 값을 조건에 넣으면 데이터베이스가 암시적 형변환을 시도할 수 있음
    • 예시는 문자열 타입 video_id 컬럼에 정수 200050을 비교하는 경우를 다룸
    • 암시적 형변환에 의존하면 문제가 생길 수 있음
    • 변환이 불가능한 값이 있으면 오류가 발생할 수 있음
    • 각 값을 지정한 타입으로 변환하는 추가 작업 때문에 쿼리가 느려질 수 있음
    • 컬럼과 같은 데이터 타입을 사용하거나, 오류를 피하려면 Snowflake의 TRY_TO_NUMBER 같은 함수를 사용할 수 있음
    • 속도 영향은 처리하는 데이터셋 크기에 따라 달라짐

자주 하는 실수

  • NOT INNULL

    • NOT IN은 비교 대상 값에 NULL이 있으면 동작하지 않음
    • NULL은 Unknown을 나타내므로 SQL 엔진이 검사 값이 목록에 없다고 검증할 수 없음
    • 이 경우 NOT EXISTS를 사용하는 방식이 대안임
  • 계산 필드 alias 충돌

    • 계산 필드의 이름을 기존 컬럼과 같게 만들면 예상하지 못한 동작이 생길 수 있음
    • Snowflake의 GROUP BY 문서GROUP BY 절의 이름이 컬럼명과 alias 모두에 매칭되면 컬럼명을 사용한다고 적고 있음
    • 예시에서 LEFT(product, 1) AS product로 alias를 만들고 GROUP BY product를 쓰면, 첫 글자가 아니라 원래 product 컬럼으로 그룹화되어 3행이 반환됨
    • 해결책은 두 가지임
    • product_letter처럼 고유 alias를 사용함
    • GROUP BY LEFT(product, 1)처럼 표현식을 명시함
    • 윈도 함수에서도 alias 문제가 생길 수 있음
    • 예시에서는 CASERobot의 revenue를 0으로 바꾸지만, 윈도 함수가 실행된 뒤 적용되어 순위가 기대와 다르게 나옴
    • 가능한 경우 고유 alias를 쓰거나, 윈도 함수의 ORDER BY 안에 계산식을 직접 넣는 방식이 필요함
  • 컬럼이 어느 테이블 소속인지 명시

    • 여러 조인이 있는 복잡한 쿼리에서는 값 문제를 원천 테이블까지 추적할 수 있어야 함
    • 두 테이블이 같은 컬럼명을 공유할 때 컬럼 소속을 명시하지 않으면 RDBMS가 오류를 낼 수 있음
    • 예시는 vc.video_id, metadata.season처럼 테이블 alias를 붙여 컬럼 출처를 분명히 함

실행 순서, 문서, 저장 이름

  • SQL 실행 순서 이해

  • 문서는 끝까지 읽기

    • Snowflake에서 여러 날짜 컬럼 중 최신 날짜를 반환하려고 GREATEST()를 사용한 사례가 있음
    • GREATEST()는 인자 중 하나가 NULL이면 NULL을 반환함
    • 문서를 더 읽었다면 COALESCE(GREATEST(...), ...) 대신 GREATEST_IGNORE_NULLS()를 사용할 수 있었음
    • 많은 경우 문서를 훑는 데 1분 이하만 걸리며, 예상과 다르게 동작하는 원인을 찾는 수고를 줄일 수 있음
  • 저장 쿼리는 설명적인 이름 사용

    • 다시 실행하거나 참고해야 할 쿼리를 찾지 못하는 상황을 피하려면 설명적인 이름으로 저장하는 편이 좋음
    • 저장 이름에는 보통 쿼리 주제, 실행 월, 요청자 이름이 들어감
    • 예시는 Lapsed users analysis - 2023-09-01 - Olivia Roberts 형식임

댓글과 토론

선행 쉼표가 이 게시물에는 전부 후행 쉼표로 작성되어있네요. 원문에는 선행으로 입력되어있습니다.

-- Good:  
SELECT   
timeslot_date  
, timeslot_channel   
, overnight_fta_share  
, IFF(DATEDIFF(DAY, timeslot_date, CURRENT_DATE()) > 7, -- First argument of IFF.  
	LAG(overnight_fta_share, 1) OVER (PARTITION BY timeslot_date, timeslot_channel ORDER BY timeslot_activity), -- Second argument of IFF.  
		NULL) AS C7_fta_share -- Third argument of IFF.  
, IFF(DATEDIFF(DAY, timeslot_date, CURRENT_DATE()) >= 29,   
		LAG(overnight_fta_share, 2) OVER (PARTITION BY timeslot_date, timeslot_channel ORDER BY timeslot_activity),   
			NULL) AS C28_fta_share  
FROM timeslot_data  
;  
Hacker News 의견들
  • 내가 덧붙일 팁은 이렇다: DB 서버를 제대로 익히고 실행 계획을 자주 확인해야 함. 의외의 결과가 나올 수 있으니 조정하고 다시 확인하는 게 좋음
    보통 EXISTSIN보다 빠르고, NOT EXISTSNULL 처리에서 EXCEPT와 다르게 동작함. 테이블을 조인한 뒤 DISTINCT 같은 것으로 행을 걸러내기보다, SELECT 목록의 하위 쿼리 컬럼을 쓰는 편이 훨씬 빠를 때가 있음. 같은 테이블에서 10개 넘는 값을 가져오더라도 그렇고, DB 서버가 lateral join을 지원해도 마찬가지일 수 있음. 단 하위 쿼리는 최대 한 행만 반환해야 함
    일회성이 아닌 쿼리는 테이블 전체 스캔을 하지 않게 해야 함. 오늘의 테이블 스캔이 내일의 장애가 될 수 있으니 인덱스를 추가해야 함. GROUP BY 절이 보통 인덱스 사용을 좌우한다는 점도 기억할 것
    표현식으로 필터링해야 한다면, 예를 들어 부분 문자열이 특정 값과 같은지 확인해야 한다면 계산 컬럼을 추가하고 거기에 인덱스를 걸 수 있음. 일부 DB는 표현식 인덱스를 직접 지원함. OR 대신 UNION ALL을 쓰면 복잡한 쿼리나 여러 OR 조건에서도 훨씬 빨라질 때가 많음
    DB가 필터링 순서를 똑똑하게 잡지 못할 때는 하위 쿼리를 JOIN해서 순서를 강제하는 것도 유용함

    • 가장 유용한 건 DBMS 자체를 배우는 것임. 각 DB마다 성능과 격리 수준의 특이점이 있고, 보너스 기능도 다르기 때문에 피할 수 없음
      Postgres에서 흥미로웠던 점은, 다른 DB도 그럴 가능성이 있는데, INSERT (SELECT ...) 작업을 CPU 코어 수에 맞춰 수동으로 샤딩하면 거의 선형으로 빨라질 수 있다는 것임. 조인이 10개쯤 있어도 가능했음. 먼저 EXPLAIN을 보고 가장 안쪽이나 바깥쪽 조인을 찾은 다음, 각 행 범위마다 별도의 병렬 쿼리(id >= start AND id < end)를 실행하면 됨. 6년 전 한 일에서 이상한 이유로 이 방식을 많이 썼음. Postgres 10+에는 병렬성이 추가됐지만, 내가 알기로는 아직 이 정도로 진보적이진 않음
    • SELECT 목록에서 하위 쿼리 “컬럼”을 쓰라는 게 정확히 무슨 뜻인지 모르겠음
      예를 들어 SELECT column1, (SELECT column2, column3, ... FROM table_b WHERE table_a.id = table_b.a_id) FROM table_a를 실행하면 예상대로 “subquery must return only one column”이 나옴. 여러 컬럼을 레코드/복합 타입으로 반환하라는 뜻인가?
      GROUP BY 절이 보통 인덱스 사용을 좌우한다는 이유가 바로 와닿지 않았는데, 궁금한 사람에게는 이 글이 단계별로 잘 설명해 줌: https://www.brentozar.com/archive/2015/06/indexing-for-group...
    • 동의함. EXPLAIN을 쓰고, 선호하는 도구로 해석하는 법을 배워야 함. 쿼리도 모니터링해야 함
      이전 스타트업에서 PgHero를 설치했는데, 성능 최적화와 우선순위 결정에 정말 큰 도움이 됐음
    • 잘 설계된 쿼리도 예상과 다르게 동작하는 경우가 많음. 컬럼 통계가 갱신되지 않았거나, 큰 테이블에서 데이터가 조각난 경우가 대표적임. 예를 들어 무작위 기본 키 삽입 같은 경우임
    • “일회성이 아닌 쿼리는 테이블 전체 스캔을 하면 안 된다. 오늘의 테이블 스캔이 내일의 장애가 될 수 있다”에는 동의하지 않음
      테이블 전체 스캔이 가장 효율적인 접근 전략인 쿼리도 있음. 보통 전체 테이블을 조회하는 분석/집계 쿼리가 그렇고, 때로는 전체 행의 50%만 가져오는 경우도 테이블 스캔이 더 낫다.
      읽기 전용 테이블 스캔이 어떻게 장애로 이어지는지도 잘 모르겠음. 동시 접근을 막지 않기 때문임. 단점은 I/O 부하가 커진다는 것뿐인데, 서버가 그 정도를 감당하지 못한다면 애초에 심각하게 과소 사양이라고 봄
  • “가독성” 섹션의 예시 3개는 이상함. 앞의 2개는 쓰기 쉽게 하려고 말 그대로 가독성을 희생하고 있고, 마지막은 들여쓰기로도 거의 구제되지 않는 읽기 어려운 괴물 같은 형태임

    • 앞쪽 쉼표 형식은 가독성 외에도 장점이 있음. 예를 들어 버전 관리 시스템에서 인자 하나당 한 줄 + 앞쪽 쉼표 형식은 인자 변경이 한 줄짜리 차이로만 나타남
      개발자는 실제 소스 코드만큼이나 커밋 기록도 많이 본다고 생각함
    • 처음 두 관례의 모양이 아주 마음에 드는 건 아니지만, 실제 SQL을 쓰는 사람들이 실제로 쓰는 관례임. 왜 존재하는지도 이해할 수 있음
      충분히 자주 봐서 이제는 별로 거슬리지 않음
    • 대안으로는, 세 살짜리 아이가 MSPaint를 처음 발견한 것처럼 SQL을 엉망으로 쓴 다음 “beautifier” 버튼을 누르고 이른 점심을 먹으러 가면 됨
    • 왜 더 나쁘다고 생각하는지 모르겠음
      문제도 안 보이고
      잘못된 것도 없어 보이는데
    • SELECT 블록에서 컬럼은 한 줄씩 나누면서도 150자짜리 줄을 그대로 두는 사람이 대체 누구임? 이건 가독성의 망가진 정의임. 쉼표 얘기는 시작도 못 하겠음
      코드 리뷰에서 긴 줄은 아무도 제대로 보지 않음. 그게 AngularJS의 가장 큰 문제였음. 머지가 잘못 처리되고 모든 게 깨졌는데, 90번째 컬럼쯤 가면 눈이 흐려지기 때문임. 코드 리뷰가 있는 팀을 반 dozen 넘게 겪었는데 항상 같았음. 이 문제를 아주 잘 의식하고 피하려고 해도, 나 역시 다른 사람의 절반 정도 빈도로는 여전히 실수함
      좀 나눠서 쓰자. 특히 다른 사람에게 예시를 보이려면 더더욱 그래야 함
  • 복잡한 저장 프로시저를 다룰 때의 팁은 이렇다

    1. 프로시저 시작 부분에서 영구 테이블을 즉시 임시 테이블로 복사하고, 필요한 행만 지정/제한/필터링함
    2. 중간에서는 필요한 대로 임시 테이블을 조작함
    3. 끝부분에서 트랜잭션 안에서 영구 테이블을 업데이트함. 오류가 감지되면 즉시 트랜잭션을 롤백하고 프로시저를 종료함. 이 세 단계를 따르면 동시성이 좋아지고, 데이터 찌꺼기를 수동으로 치우지 않고도 프로시저를 다시 시작할 수 있음
    4. 원격 테이블을 다룰 때는 극도로 조심해야 함. 원격 테이블은 현재 RDBMS 안에 있지 않으므로, 그 RDBMS의 통계나 인덱스를 거의 활용하지 못할 가능성이 큼. 많은 경우 원격 테이블 전체를 임시 테이블로 덤프/복사한 뒤 작업하는 편이 더 빠름. 원격 테이블에 기대할 수 있는 최대치는 WHERE 절 실행 정도임. JOIN이나 복잡한 작업을 시도하면 타임아웃이 날 가능성이 큼
    5. 실행 계획은 쉽게 헷갈림. 어떤 경우에는 실행 계획이 행 단위 처리로 떨어져 성능이 멈춰버릴 수 있음. 복잡한 저장 프로시저를 임시 테이블을 쓰는 작은 단계들로 나누는 편이 나은 경우가 많음
    6. RDBMS가 실제로 무엇을 하는지 보려면 항상 실행 계획을 확인해야 함
    • 5번을 꼭 필요하지 않은 상황에서 적용한 코드를 되돌려서 쿼리 성능을 크게 개선한 적이 있음. 때로는 쿼리를 여러 작은 쿼리로 쪼개는 것이, 쿼리 최적화기에게 전체 쿼리를 주고 최적 경로를 찾게 하는 것보다 훨씬 비효율적임
      6번 없이 5번을 했다면, 최적이 아닌 일을 하고 있다는 걸 못 볼 가능성이 큼. 조기 최적화를 피하고 먼저 가장 직관적인 방식으로 작성한 뒤 필요할 때만 최적화하라는 게 내 조언임. 가장 중요한 건 SQL을 절차적으로 작성하지 않는 것임. 원하는 데이터를 기술하는 것이지, 엔진에게 가져오는 방법을 명령하는 게 아님
    • 임시 테이블을 잔뜩 써야 하는 건 싫지만, 쿼리 플래너에게 맡기면 절대 끝나지 않을 쿼리를 자주 만남. 컴파일러처럼 쿼리 플래너의 능력도 크게 과대평가되어 있음
      반면 Microsoft는 쿼리 플래너가 제일 잘 안다는 듯이, 이를 튜닝하려 하지 말라는 경고를 계속 붙임
    • 이 규칙들이 어떤 벤더의 DB에서는 완전히 맞을 수 있지만, 다른 DB는 우선순위와 특성, 트레이드오프가 아주 다를 수 있음
      DB 버전도 영향을 줄 수 있음
    • 1~3번은 데이터 크기가 합리적이라고 보장할 수 있으면 괜찮음. 하지만 하드웨어가 감당하기에 데이터가 너무 커지면, 큰 데이터셋을 복사하고 다시 큰 데이터셋을 업데이트하는 작업이 상당한 오버헤드를 추가할 수 있음
  • “혹시 몰라서” 하는 개발을 좋아하지 않음. 인터페이스도 그렇고, where 1=1 같은 자리표시자도 마찬가지임
    필요할 때 하라. 언젠가 미래에 필요할지도 모른다고 해서 하지 말 것. 프로덕션 코드는 개발용 도우미를 남겨두는 곳이 아님. 개발 중에는 하고 싶은 대로 해도 되지만, 프로덕션 코드에서는 가독성과 명확한 의도가 훨씬 중요함

    • 테이블명과 컬럼명 참조를 모두 완전 수식해서 쓰는 편인가? 그렇게 하면 가독성이 한 자릿수 이상 좋아지는 경우가 많았지만, 금방 매우 장황해지고 쓰기가 믿을 수 없을 만큼 고통스럽게 지루해짐
  • “안티 조인”에 한 가지 더. 다른 큰 테이블이나 하위 쿼리에 조건에 맞는 행이 존재하는지만 확인하려면 IN이나 LEFT JOIN 대신 EXISTS를 쓰는 게 좋음
    EXISTS는 일치하는 항목을 찾는 즉시 참을 반환함. LEFT JOININ의 경우 엔진이 평가 전에 모든 결과를 모음

    • 그 부분이 좀 헷갈렸음. 내가 테스트한 모든 경우에 (NOT) EXISTS(LEFT) JOIN이나 (NOT) IN보다 더 나은 실행 계획을 만들거나 같은 계획을 만들었음
      게다가 의도도 더 명확함
  • “코드에 주석을 달라”와 관련해서, 적어도 MSSQL에서는 주석에 -- 대신 /**/를 쓰라고 자주 권장됨.** Query Store** 같은 기능이 줄바꿈 없이 쿼리를 저장하는 경우가 많아서, 거기서 쿼리를 가져오면 IDE 포매터를 바로 쓰는 대신 수동으로 전부 고쳐야 하기 때문임

    • 그건 Query Store의 버그처럼 들림
    • XML로 캐스팅할 수 있나? 나는 OBJECT_DEFINITION에 그걸 씀
      select name,cast((select OBJECT_DEFINITION(object_id) for xml path('')) as xml) from sys.procedures
      줄바꿈이 보존되기 때문에 정리하기가 더 쉬울 수 있음. 다만 >>로 바뀌는 것처럼 다른 XML 문자는 망가짐. 또 다른 선택지는 VARBINARY와 이를 다시 풀어주는 무언가를 쓰는 것임
  • 모두가 쉼표 제안에는 난리인데, WHERE 절의 1=1은 좋은 생각이라고 보는 건가? 코드 리뷰에서 그걸 보면 작성자를 어떻게 생각해야 할지 모르겠음

    • 뒤쪽 쉼표와 같은 이유로 정당화할 수는 있음. WHERE 문 변경이 다른 줄에 영향을 주지 않아서 코드 리뷰가 쉬워진다는 이유임
      하지만 이 경우처럼 동적 조건을 추가하기 위한 이유라면, 내가 일하는 곳에서는 확실히 해고될 것임
  • DB 설정으로 속도를 올리는 것, 거의 “구매”에 가까운 방식과, 사실상 수동으로 구현하는 “구축” 사이의 선을 어디에 그어야 하는지 일반적인 지침을 공유해 줄 수 있는 사람이 있을까? 내 제한적인 경험으로는 유능한 DBA들이 훨씬 높은 보수를 받고 다른 곳에서 일하기 때문에, 이 일이 앱 개발자에게 자주 떨어짐. 위에서 말했듯이 DB를 아는 게 중요함
    대표적인 예는 시간이 지나며 대량으로 쌓이고 최신 데이터가 가장 자주 접근되는 데이터임. DBA는 파티셔닝이나 부분 인덱스로 접근을 빠르게 유지할 수 있지만, 앱 개발자는 레코드를 별도 아카이브 테이블로 뒤에서 옮기면서 전체 데이터셋의 최종적 검색 같은 기능을 계속 지원할 수도 있음. 클라우드 DB의 기능 부족으로 제한되는 경우처럼, 적절한 시점에 테이블 하나를 여러 개로 나누는 초기 작업을 도구가 꽤 자동화할 수 있을 것 같다는 느낌도 듦
    또 다른 관리 선택지는 큰 블롭/파일을 모두 별도의 데이터베이스, 혹은 파일 시스템에 저장해 다른 저장소 설정을 쓰는 것임. 이 역시 DB가 수용하기도 하고 수동으로 처리하기도 함
    극단적으로는 인덱스를 직접 구현하는 데까지 갈 수 있을 듯함. 자동 증가 기본 키 하나와 수많은 컬럼을 가진 거대한 테이블을 두고, 그 ID와 검색 가능한 컬럼 몇 개를 가진 테이블을 따로 만드는 방식임. 전문 검색이나 벡터까지 갈 수도 있겠음
    MSSQL 2016+에서 구체화된 뷰 패턴을 수동 구현할 때 유용한 팁은 파티션 전환을 함께 쓰는 것임. https://github.com/cajuncoding/SqlBulkHelpers?tab=readme-ov-...에 잘 설명되고 구현돼 있음. 우연히 찾은, 상업적으로 가장 유용했지만 검색 순위는 밀리고 별은 적은 작은 라이브러리였고, .NET으로 MSSQL에 대량 삽입하는 데 초점을 둠. 파티션 전환 자동화를 통해 구매/구축의 선을 적절히 그은 좋은 예라고 생각함

  • 빠진 내용: SELECT * 사용을 멈춰야 함. 거의 확실히 테이블 전체 폭이 필요하지 않으며, 그렇게 하면 필터링하고 전송할 데이터가 늘고, 멋진 기능인 세미 조인도 막게 됨

    • SQL을 쓰는 사람은 크게 두 부류임. 분석가와 개발자임
      개발자라면 맞음. SELECT *에는 함정이 있고, 거의 항상 컬럼을 명시하거나 대신 그렇게 해주는 쿼리 빌더를 써야 함
      하지만 분석가라면 인생은 짧고, 때로는 모든 컬럼을 전부 타이핑하고 싶지 않을 수 있음. SELECT *도 괜찮음
  • 약간 주제에서 벗어날 수 있지만, 관리자가 풀 리퀘스트를 아무 코멘트나 논의 없이 그냥 닫는 것이 받아들일 만한 방식인가?
    저장소에 가끔 기여했거나 기여하려 했던 입장에서 묻는 것임
    예시: https://github.com/ben-n93/SQL-tips-and-tricks/pulls?q=is%3A...