SQLite에 생각보다 많은 수학 함수가 있다고?
SQLite는 미니멀한 DB로 유명하지만, 의외로 숫자 함수만큼은 꽤 풍부하게 갖추고 있습니다. 반올림부터 절댓값, 올림과 내림, 거듭제곱, 제곱근, 로그, 삼각함수, 난수까지 거의 다 있죠. 대부분의 sqlite 수학 함수는 3.35 버전(2021년)에서 추가되었기 때문에, 요즘 쓰는 환경 — Python에 내장된 것, Node, 또는 공식 CLI 등 — 이라면 별도 설정 없이 바로 사용할 수 있습니다.
본격적으로 들어가기 전에, 먼저 맛보기 예제부터 살펴볼게요:
함수 여섯 개, 결과 한 줄. 이번 페이지의 나머지 부분에서는 각 함수군이 어떤 용도로 쓰이는지, 그리고 알아두면 좋은 함정들을 하나씩 짚어보겠습니다.
ROUND: 가장 자주 쓰게 될 sqlite 반올림 함수
ROUND(value, digits)는 지정한 sqlite 소수점 자리수만큼 반올림해 줍니다. 두 번째 인자는 선택값이라 생략하면 정수 단위로 반올림되지만, 반환 타입은 여전히 부동소수점입니다:
몇 가지 짚고 넘어갈 점이 있습니다.
ROUND(3.14159)은3이 아니라3.0을 반환합니다. 정수가 필요하다면CAST(ROUND(x) AS INTEGER)를 쓰거나, 단순히 버림이 목적이라면CAST(x AS INTEGER)를 사용하세요.- SQLite는 "0에서 멀어지는 방향으로 반올림(round half away from zero)" 방식을 씁니다. 즉
2.5는3,-2.5는-3이 됩니다. 일부 DB는 은행원 반올림(짝수로 반올림)을 쓰지만, SQLite는 그렇지 않습니다. digits인자에는 음수도 넣을 수 있습니다. 예를 들어ROUND(1234.5, -2)는 100 단위로 반올림해서1200을 돌려줍니다.
실무에서 가장 자주 쓰는 형태는 단연 금액 표시용 ROUND(price, 2)입니다.
ROUND와 CAST는 다릅니다
반올림하려는 의도로 CAST(x AS INTEGER)를 쓰다가 낭패를 보는 경우가 흔합니다.
CAST은 0 방향으로 잘라냅니다. 그냥 소수 부분을 버려버리는 거죠. 반면 ROUND는 가장 가까운 정수로 반올림합니다. 2.9 같은 값에서는 두 결과가 1만큼 차이 나게 됩니다. 원하는 동작이 무엇인지에 따라 골라 쓰면 됩니다.
ABS, SIGN — 숫자의 부호 다루기
ABS(x)는 절댓값을 돌려주고, SIGN(x)는 숫자의 부호에 따라 -1, 0, 1 중 하나를 반환합니다:
ABS는 그야말로 일꾼 같은 함수예요. "두 값이 얼마나 떨어져 있나?" 같은 질의에 딱입니다. SIGN은 자주 쓰이진 않지만, 별도의 CASE 문 없이도 행을 방향(입금 vs 출금, 이익 vs 손실)별로 분류할 때 요긴하죠.
CEIL, FLOOR, TRUNC 함수
이 세 함수는 가장 가까운 정수로 반올림하는 게 아니라, 정수 형태의 값을 얻어낼 때 씁니다. CEIL은 항상 위로 올림, FLOOR는 항상 아래로 내림, TRUNC는 항상 0 방향으로 잘라냅니다:
음수 케이스를 조심하세요. FLOOR(-2.9)는 -3(0에서 더 멀어짐)이지만, TRUNC(-2.9)는 -2(0 쪽으로 가까워짐)입니다. 음수를 다룰 때 FLOOR와 TRUNC는 서로 다른 결과를 내기 때문에, 둘 중 잘못된 걸 고르면 전형적인 off-by-one 버그로 이어집니다.
CEILING은 CEIL의 별칭입니다. 읽기 편한 쪽을 골라 쓰면 됩니다.
진짜 함정은 SQLite의 정수 나눗셈
이건 함수가 아니라 / 연산자 얘기지만, 어떤 수학 함수보다도 입문자들이 가장 많이 걸려 넘어지는 부분입니다:
양쪽 피연산자가 모두 정수면 SQLite는 정수 나눗셈을 수행하고 소수점 이하를 잘라냅니다. 한쪽이라도 REAL이 되는 순간 전체 식이 실수형으로 평가됩니다. 해결법은 간단해요. 적어도 한쪽 피연산자를 실수로 만들면 됩니다. 2 대신 2.0이라고 쓰거나, 명시적으로 캐스팅하면 됩니다.
이 함정은 컬럼을 참조할 때 특히 자주 발생합니다. total_cents / 100은 정수를 반환하지만, total_cents / 100.0이라고 쓰면 우리가 원했던 달러 단위 금액이 제대로 나옵니다.
MOD 함수와 % 연산자
MOD(x, y)는 x / y의 나머지를 돌려줍니다. % 연산자도 똑같은 동작을 합니다:
MOD(17, 5)와 17 % 5는 둘 다 2를 반환합니다. 그런데 SQLite에서 0으로 나눈 나머지는 에러를 내지 않고 NULL을 반환합니다. 대부분의 언어와 다른 부분이라 좀 의외인데, 이게 신경 쓰인다면 미리 나누는 값을 확인하거나 CASE WHEN y = 0 THEN ... END으로 감싸 두는 게 좋습니다.
함수 형태와 연산자 형태는 서로 바꿔 써도 됩니다. 보통은 짧다는 이유로 %를 더 많이 쓰죠.
POWER, SQRT, EXP, LOG
거듭제곱과 제곱근을 다룰 때 쓰는 함수들입니다:
사람들이 자주 헷갈리는 포인트 몇 가지를 짚고 갑니다:
POW는POWER의 별칭입니다.- SQLite의
LOG(x)는 상용로그(밑이 10) 입니다.LN(x)가 자연로그고요. 인자를 두 개 넘긴LOG(b, x)는 밑이b인 로그입니다. (다른 많은 언어에서는log가 자연로그인데, SQL 쪽은 관례가 다르니 주의하세요.) - 음수에
SQRT를 적용하면 에러가 아니라NULL이 반환됩니다. POWER(0, 0)은 관례에 따라1을 돌려줍니다.
복리 계산, 데시벨 정규화, 거리 계산처럼 기하급수적이거나 지수적인 수학이 들어가는 곳이라면 어디든 요긴하게 쓰입니다.
RANDOM과 RANDOMBLOB
RANDOM()은 부호 있는 64비트 정수 범위 전체에서 임의의 값을 반환합니다:
특정 범위의 숫자를 얻으려면 RANDOM()이 부호 있는 값을 반환하므로 ABS로 감싼 뒤 % 연산자를 사용하면 됩니다. 0과 1 사이의 실수를 얻고 싶다면 64비트 정수의 최댓값으로 나누면 됩니다. SQLite에는 0~1 사이 값을 돌려주는 내장 RAND() 함수가 없기 때문에 직접 만들어 써야 합니다.
RANDOMBLOB(n)은 n 바이트 분량의 임의 데이터를 반환하며, 세션 토큰이나 테스트용 더미 데이터를 만들 때 유용합니다. 출력 가능한 문자열로 바꾸려면 HEX()와 함께 사용하세요:
호출할 때마다 매번 새로운 값이 나옵니다. RANDOM()이 같은 행 안에서 두 번 같은 숫자를 돌려줄 거라고 기대하면 안 돼요. 한 표현식 안에서도 호출 하나하나가 서로 독립적으로 동작합니다.
한꺼번에 적용해 보기
상품 테이블을 예로 들어 거리를 계산하고 가격을 반올림하는 간단한 실전 예제를 살펴보겠습니다.
price_cents / 100.0에서 핵심은 바로 .0입니다. 이게 붙어야 나눗셈이 실수 연산으로 바뀌고, 그 결과를 ROUND로 소수점 둘째 자리까지 다듬을 수 있죠. 만약 .0을 빼면 1299 / 100은 12.99가 아니라 12가 됩니다 — sqlite 정수 나눗셈이 그렇게 동작하니까요.
다음 주제: 날짜와 시간
sqlite 숫자 함수로 수학 계산은 충분히 처리할 수 있습니다. 이제 날짜와 시간 차례인데, 이쪽은 별도의 도구가 필요합니다. SQLite는 날짜와 시간을 TEXT, REAL, INTEGER 중 하나로 저장하고, 이를 파싱하고 포맷팅하고 연산할 수 있는 작지만 강력한 함수들을 제공합니다. 다음 장에서 이 부분을 자세히 다뤄 보겠습니다.
자주 묻는 질문
SQLite에서 소수점 둘째 자리까지 반올림하려면?
ROUND(value, 2)를 쓰면 됩니다. 두 번째 인자가 남길 소수 자릿수예요. 예를 들어 ROUND(3.14159, 2)는 3.14를 반환합니다. 인자를 하나만 넘기면 — ROUND(x) — 가장 가까운 정수로 반올림하지만 반환 타입은 여전히 실수(float)라 헷갈릴 수 있으니 주의하세요.
SQLite에 CEIL이나 FLOOR도 있나요?
네, 있습니다. SQLite 3.35 (2021년)부터 수학 함수가 기본 내장됐어요. CEIL(x), FLOOR(x), SQRT(x), POWER(x, y), LOG(x), EXP(x) 등을 그대로 쓸 수 있습니다. 그 이전 버전에서는 math 확장을 따로 로드해야 하지만, 요즘 배포되는 환경(Python, Node, 브라우저 내장 등)은 거의 다 활성화된 상태로 들어옵니다.
왜 SQLite에서 5 / 2가 2.5가 아니라 2가 나오죠?
양쪽이 모두 정수라서 그렇습니다. 정수끼리 나누면 SQLite는 정수 나눗셈을 수행하고 소수점 이하를 버려버려요. 2.5를 얻으려면 한쪽을 실수로 만들어 주면 됩니다. 5 / 2.0이나 CAST(5 AS REAL) / 2처럼요. 이건 숫자 함수의 문제가 아니라 / 연산자가 정수 인자를 만났을 때의 동작 방식입니다.