| 순서 | 번호 | 문제 | 링크 | 배우는 내용 |
|---|---|---|---|---|
| 1 | 175 | Combine Two Tables | https://leetcode.cn/problems/combine-two-tables/ | SELECT, LEFT JOIN |
| 2 | 584 | Find Customer Referee | https://leetcode.cn/problems/find-customer-referee/ | WHERE |
| 3 | 595 | Big Countries | https://leetcode.cn/problems/big-countries/ | AND, OR |
| 4 | 1148 | Article Views I | https://leetcode.cn/problems/article-views-i/ | DISTINCT |
| 5 | 1683 | Invalid Tweets | https://leetcode.cn/problems/invalid-tweets/ | CHAR_LENGTH |
| 6 | 1378 | Replace Employee ID With The Unique Identifier | https://leetcode.cn/problems/replace-employee-id-with-the-unique-identifier/ | LEFT JOIN |
| 7 | 1068 | Product Sales Analysis I | https://leetcode.cn/problems/product-sales-analysis-i/ | JOIN |
| 8 | 1581 | Customer Who Visited but Did Not Make Any Transactions | https://leetcode.cn/problems/customer-who-visited-but-did-not-make-any-transactions/ | LEFT JOIN + NULL |
| 9 | 197 | Rising Temperature | https://leetcode.cn/problems/rising-temperature/ | Self Join |
| 10 | 607 | Sales Person | https://leetcode.cn/problems/sales-person/ | NOT EXISTS |
| 순서 | 번호 | 문제 | 링크 | 학습 내용 |
|---|---|---|---|---|
| 11 | 511 | Game Play Analysis I | https://leetcode.cn/problems/game-play-analysis-i/ | MIN |
| 12 | 619 | Biggest Single Number | https://leetcode.cn/problems/biggest-single-number/ | GROUP BY |
| 13 | 2356 | Number of Unique Subjects Taught by Each Teacher | https://leetcode.cn/problems/number-of-unique-subjects-taught-by-each-teacher/ | COUNT DISTINCT |
| 14 | 1141 | User Activity for the Past 30 Days I | https://leetcode.cn/problems/user-activity-for-the-past-30-days-i/ | DATE |
| 15 | 1527 | Patients With a Condition | https://leetcode.cn/problems/patients-with-a-condition/ | LIKE |
| 16 | 1873 | Calculate Special Bonus | https://leetcode.cn/problems/calculate-special-bonus/ | CASE WHEN |
| 17 | 1667 | Fix Names in a Table | https://leetcode.cn/problems/fix-names-in-a-table/ | UPPER, LOWER |
| 18 | 196 | Delete Duplicate Emails | https://leetcode.cn/problems/delete-duplicate-emails/ | DELETE |
| 순서 | 번호 | 문제 | 링크 |
|---|---|---|---|
| 44 | 176 | Second Highest Salary | https://leetcode.cn/problems/second-highest-salary/ |
| 45 | 177 | Nth Highest Salary | https://leetcode.cn/problems/nth-highest-salary/ |
| 46 | 178 | Rank Scores | https://leetcode.cn/problems/rank-scores/ |
| 47 | 262 | Trips and Users | https://leetcode.cn/problems/trips-and-users/ |
| 48 | 1045 | Customers Who Bought All Products | https://leetcode.cn/problems/customers-who-bought-all-products/ |
| 49 | 1164 | Product Price at a Given Date | https://leetcode.cn/problems/product-price-at-a-given-date/ |
| 50 | 1204 | Last Person to Fit in the Bus | https://leetcode.cn/problems/last-person-to-fit-in-the-bus/ |
| 51 | 1321 | Restaurant Growth | https://leetcode.cn/problems/restaurant-growth/ |
| 순서 | 번호 | 문제 | 링크 |
|---|---|---|---|
| 52 | 1873 | Calculate Special Bonus | https://leetcode.cn/problems/calculate-special-bonus/ |
| 53 | 1179 | Reformat Department Table | https://leetcode.cn/problems/reformat-department-table/ |
| 54 | 1205 | Monthly Transactions II | https://leetcode.cn/problems/monthly-transactions-ii/ |
| 55 | 1174 | Immediate Food Delivery II | https://leetcode.cn/problems/immediate-food-delivery-ii/ |
| 56 | 1587 | Bank Account Summary II | https://leetcode.cn/problems/bank-account-summary-ii/ |
| 순서 | 번호 | 문제 | 링크 |
|---|---|---|---|
| 57 | 1667 | Fix Names in a Table | https://leetcode.cn/problems/fix-names-in-a-table/ |
| 58 | 1527 | Patients With a Condition | https://leetcode.cn/problems/patients-with-a-condition/ |
| 59 | 1141 | User Activity for the Past 30 Days I | https://leetcode.cn/problems/user-activity-for-the-past-30-days-i/ |
| 60 | 1225 | Report Contiguous Dates | https://leetcode.cn/problems/report-contiguous-dates/ |
| 61 | 1454 | Active Users | https://leetcode.cn/problems/active-users/ |
| 62 | 1084 | Sales Analysis III | https://leetcode.cn/problems/sales-analysis-iii/ |
| 63 | 1795 | Rearrange Products Table | https://leetcode.cn/problems/rearrange-products-table/ |
| 64 | 1127 | User Purchase Platform | https://leetcode.cn/problems/user-purchase-platform/ |
| 순서 | 번호 | 문제 | 링크 |
|---|---|---|---|
| 65 | 178 | Rank Scores | https://leetcode.cn/problems/rank-scores/ |
| 66 | 185 | Department Top Three Salaries | https://leetcode.cn/problems/department-top-three-salaries/ |
| 67 | 1204 | Last Person to Fit in the Bus | https://leetcode.cn/problems/last-person-to-fit-in-the-bus/ |
| 68 | 1321 | Restaurant Growth | https://leetcode.cn/problems/restaurant-growth/ |
| 69 | 1341 | Movie Rating | https://leetcode.cn/problems/movie-rating/ |
| 70 | 1164 | Product Price at a Given Date | https://leetcode.cn/problems/product-price-at-a-given-date/ |
| 71 | 1934 | Confirmation Rate | https://leetcode.cn/problems/confirmation-rate/ |
| 72 | 1211 | Queries Quality and Percentage | https://leetcode.cn/problems/queries-quality-and-percentage/ |
| 73 | 601 | Human Traffic of Stadium | https://leetcode.cn/problems/human-traffic-of-stadium/ |
| 74 | 180 | Consecutive Numbers | https://leetcode.cn/problems/consecutive-numbers/ |
| 순서 | 번호 | 문제 | 링크 |
|---|---|---|---|
| 75 | 608 | Tree Node | https://leetcode.cn/problems/tree-node/ |
| 76 | 1225 | Report Contiguous Dates | https://leetcode.cn/problems/report-contiguous-dates/ |
| 77 | 1070 | Product Sales Analysis III | https://leetcode.cn/problems/product-sales-analysis-iii/ |
| 78 | 1321 | Restaurant Growth | https://leetcode.cn/problems/restaurant-growth/ |
| 순서 | 번호 | 문제 | 링크 |
|---|---|---|---|
| 79 | 262 | Trips and Users | https://leetcode.cn/problems/trips-and-users/ |
| 80 | 601 | Human Traffic of Stadium | https://leetcode.cn/problems/human-traffic-of-stadium/ |
| 81 | 184 | Department Highest Salary | https://leetcode.cn/problems/department-highest-salary/ |
| 82 | 185 | Department Top Three Salaries | https://leetcode.cn/problems/department-top-three-salaries/ |
| 83 | 1321 | Restaurant Growth | https://leetcode.cn/problems/restaurant-growth/ |
| 84 | 1341 | Movie Rating | https://leetcode.cn/problems/movie-rating/ |
| 85 | 1934 | Confirmation Rate | https://leetcode.cn/problems/confirmation-rate/ |
위 문제들을 풀며 반복해서 만나는 문법과 함정을 한곳에 모았습니다. ⚠️ 는 에러 없이 조용히 틀린 답을 내는 함정입니다. 문법 에러보다 위험합니다. 💡 는 실무에서 사고를 막아주는 팁입니다.
| 연산자 | 의미 | 비고 |
|---|---|---|
= | 같다 | NULL 과 비교하면 결과가 UNKNOWN |
<> (!=) | 다르다 | NULL 과 비교하면 결과가 UNKNOWN |
> < >= <= | 크다 / 작다 / 이상 / 이하 | 문자열은 콜레이션 기준, 날짜는 시간순 |
<=> | NULL-safe 같다 | 절대 UNKNOWN 을 안 냄. 항상 1 또는 0 |
IS NULL / IS NOT NULL | NULL 여부 | NULL 비교는 오직 이것으로만 |
IS [NOT] TRUE/FALSE | 불린 판정 | x IS TRUE = x = 1 과 유사 |
BETWEEN x AND y | x 이상 y 이하 | 양 끝 포함 |
IN (...) | 목록에 속함 | OR 의 축약형 |
LIKE | 패턴 매칭 | %(0글자+), _(1글자) |
REGEXP (RLIKE) | 정규식 매칭 | ⚠️ 인덱스를 절대 못 탐 → 풀스캔 |
= 와 <=> 의 차이: NULL = NULL → NULL(UNKNOWN), NULL <=> NULL → 1(TRUE). 값이 NULL 일 수 있는 컬럼을 정확히 비교할 땐 <=>.'a' < 'b' 처럼 콜레이션 정렬 순서로 비교합니다.DROP 은 테이블/DB 자체를 삭제, TRUNCATE 는 데이터만 비웁니다. DELETE 는 행 단위로 지우며 WHERE 로 조건을 줄 수 있습니다.ALTER 는 잠금·복제 지연을 유발할 수 있어, 운영에서는 pt-online-schema-change(Percona) / gh-ost(GitHub) 를 씁니다.TINYINT(1) 의 별칭일 뿐입니다. 진짜 불린 타입은 없습니다. TRUE=1, FALSE=0 으로 저장됩니다. WHERE is_active = 2 도 문법상 통과합니다.BIGINT 로. INT 의 상한(약 21억)은 트래픽 많은 서비스에서 실제로 넘칩니다. 나중에 INT → BIGINT 로 바꾸는 마이그레이션은 테이블 전체를 재작성하는 대공사가 됩니다.FLOAT/DOUBLE 을 쓰면 안 됩니다. 부동소수점은 근사값이라 0.1 + 0.2 ≠ 0.3 이 되어 정산이 어긋납니다. 반드시 DECIMAL(p, s)(예: 원화 DECIMAL(12, 2))를 쓰세요.CHAR 는 뒤 공백을 조용히 삼킵니다. CHAR(10) 에 'abc ' 를 넣고 꺼내면 뒤 공백이 사라진 'abc' 가 나옵니다. 공백이 의미 있는 값(비밀번호 해시 등)은 VARCHAR/BINARY 를 쓰세요.ENUM 은 값 추가 시 ALTER TABLE 이 필요합니다. 값이 자주 늘어나는 도메인(예: 결제수단이 계속 추가됨)이라면 ENUM 대신 별도 코드 테이블 + FK 가 낫습니다. 또 하나: ENUM 의 정렬 순서는 알파벳순이 아니라 선언 순서입니다. ORDER BY grade 가 사전순이 아닌 이유입니다.TIMESTAMP 는 32비트라서 2038-01-19 에 오버플로우합니다. 구독 만료일, 보증 기간처럼 먼 미래 날짜를 다룬다면 TIMESTAMP 대신 DATETIME 을 쓰세요.SELECT * 를 애플리케이션 코드에 넣지 마세요. 컬럼이 추가되면 네트워크·메모리 낭비, 인덱스 커버링 실패, 컬럼 순서 의존 버그가 생깁니다. * 는 탐색할 때만 쓰세요.`)으로 감싸야 합니다. 예: SELECT COUNT(*) AS 주문 수``.SELECT name price 는 에러가 아니라 price 를 name 의 별칭으로 해석해 컬럼이 조용히 사라집니다. 별칭엔 항상 AS 를 명시하세요.WHERE 는 조건이 참(TRUE)인 행만 남깁니다. 거짓(FALSE)뿐 아니라 NULL(unknown)인 행도 버립니다.ORDER BY 를 명시하세요.NULL 을 가장 작은 값으로 취급합니다. ORDER BY col ASC 면 NULL 이 맨 앞에 옵니다. 💡 MySQL 엔 NULLS LAST 문법이 없어 ORDER BY (col IS NULL), col 로 우회합니다.ORDER BY 없는 LIMIT 은 아무것도 보장하지 않습니다. "상위 10개" 를 원한다면 반드시 ORDER BY 를 함께 쓰세요.DISTINCT 는 함수가 아닙니다. DISTINCT(city) 라고 써도 동작하지만, 그건 DISTINCT (city) — 괄호가 그냥 무시된 것일 뿐입니다. DISTINCT 는 SELECT 목록 전체에 걸립니다. SELECT DISTINCT city, country 는 (city, country) 조합의 유일값입니다.WHERE 에서 SELECT 별칭을 못 쓰는 이유"(WHERE 가 SELECT 보다 먼저 실행됨), "HAVING/ORDER BY 에선 별칭을 쓸 수 있는 이유" 가 자연스럽게 이해됩니다.AND 는 OR 보다 먼저 묶입니다. 산술에서 * 가 + 보다 먼저인 것과 같습니다.OR 를 쓸 때는 무조건 괄호를 치세요. 우선순위를 외우고 있더라도 치세요. 6개월 뒤의 나와 동료를 위한 것입니다. 실무 버그 리포트의 상당수가 "OR 괄호 누락"입니다.BETWEEN — a BETWEEN x AND y 는 a >= x AND a <= y 와 완전히 같습니다. 양 끝을 포함합니다.BETWEEN 을 쓰면 반드시 사고가 납니다. logged_at BETWEEN '2024-01-01' AND '2024-01-31' 은 2024-01-31 00:00:00 까지만 포함해 그날 낮 시간대를 통째로 놓칩니다. → logged_at >= '2024-01-01' AND logged_at < '2024-02-01' 처럼 ">= 시작, < 다음날" 패턴을 쓰세요.IN 은 OR 를 짧게 쓴 것입니다. category_id IN (21, 22) 는 category_id = 21 OR category_id = 22 와 같습니다.NULL 은 "0" 도 "빈 문자열" 도 아닙니다. "값을 모른다" 입니다.TRUE / FALSE / UNKNOWN 3개입니다.NULL = NULL 조차 TRUE 가 아닙니다(→ UNKNOWN). NULL 은 전염됩니다 — 산술이든 문자열 연결이든 NULL 이 하나 끼면 결과가 통째로 NULL 이 됩니다. (1 + NULL → NULL, CONCAT('a', NULL) → NULL)<>, NOT LIKE, NOT IN)을 쓸 때는 항상 OR ... IS NULL 을 붙일지 결정하세요. "제외" 요구사항을 받으면 "NULL 인 행은 포함인가요, 제외인가요?" 를 반드시 물어보세요. 대개 기획자는 이 질문을 생각해 본 적이 없습니다.<=> 는 = 와 똑같지만 NULL 을 정상적으로 비교합니다. 절대 UNKNOWN 을 돌려주지 않고 항상 1 또는 0 입니다.NOT IN (서브쿼리) 대신 NOT EXISTS 를 기본값으로 쓰세요. (아래 서브쿼리 절 참고)| 함수 | 동작 |
|---|---|
IFNULL(a, b) | a 가 NULL 이면 b. 인자 2개 고정. MySQL 전용 |
COALESCE(a, b, c, ...) | 왼쪽부터 첫 번째 NULL 아닌 값. 표준 SQL, 인자 개수 무제한 |
NULLIF(a, b) | a = b 이면 NULL, 아니면 a |
| 함수 | 설명 | NULL 처리 |
|---|---|---|
COUNT(*) | 행의 개수 | NULL 포함, 무조건 셈 |
COUNT(col) | col 이 NULL 이 아닌 행의 개수 | NULL 제외 |
COUNT(DISTINCT col) | col 의 서로 다른 값의 개수 | NULL 제외 |
SUM(col) | 합계 | NULL 무시 |
AVG(col) | 평균 | NULL 무시(분모에서도 제외) |
MIN(col) / MAX(col) | 최소 / 최대 | NULL 무시. 숫자·문자·날짜 모두 가능 |
GROUP_CONCAT(col) | 그룹 내 값들을 문자열로 이어붙임 | NULL 무시. SEPARATOR, ORDER BY 지정 가능 |
STDDEV / VARIANCE | 표준편차 / 분산 | NULL 무시 |
AVG 는 NULL 을 분모에서도 제외합니다. NULL 을 0으로 치고 평균 내고 싶다면 AVG(IFNULL(col, 0)) 또는 SUM(col) / COUNT(*) 를 쓰세요.HAVING 이 아니라 WHERE 에 쓰세요. WHERE 는 집계 전에 행을 걸러 더 효율적입니다.COUNT(*) 등)로 거르는 건 WHERE 로는 불가능하고 HAVING 만 할 수 있습니다. (평가 순서상 WHERE 는 GROUP BY 보다 먼저라 아직 집계값이 없음)customer_id 만 있고 고객 이름은 없습니다. 이름을 붙이려면 customers 와 이어야 합니다. 양쪽에 짝이 있는 행만 남기는 것이 INNER JOIN 입니다.INNER JOIN 은 짝이 없으면 버립니다. 하지만 "상품이 하나도 없는 카테고리" 처럼 짝이 없는 쪽도 보고 싶을 때가 많습니다. LEFT JOIN 은 왼쪽 테이블의 행을 전부 남기고, 오른쪽에 짝이 없으면 그 자리를 NULL 로 채웁니다.LEFT JOIN 뒤의 COUNT 는 반드시 오른쪽 테이블 컬럼을 대상으로 하세요. NULL 확장으로 "상품이 전부 NULL 인 행" 이 1줄 생기는데 COUNT(*) 는 그 행도 셉니다. COUNT(오른쪽.컬럼) 을 쓰면 그 컬럼이 NULL 인 행은 안 세므로 올바른 0 이 나옵니다.LEFT JOIN 에서 오른쪽 테이블 조건은 ON 에, 왼쪽 테이블 조건은 WHERE 에. 오른쪽 조건을 WHERE 에 쓰면 NULL 행이 걸러져 사실상 INNER JOIN 이 되어버립니다.JOIN 결과에 WHERE 를 걸면, 조건에 맞는 행이 하나도 없는 그룹은 결과에서 통째로 사라집니다. "취소 제외 매출" 은 맞게 나오지만, "전 고객 목록" 을 기대했다면 3명이 조용히 빠집니다. 이걸 피하려면 (1) 필터를 SUM(o.status <> 'CANCELLED') 같은 조건부 집계로 옮기거나, (2) LEFT JOIN 으로 바꾸고 조건을 ON 절이나 집계 안으로 넣어야 합니다.CROSS JOIN 은 조건 없이 두 테이블의 모든 조합(곱집합)을 만듭니다. A 가 m 행, B 가 n 행이면 결과는 m × n 행입니다.FULL OUTER JOIN 이 없습니다 → LEFT JOIN 과 RIGHT JOIN 을 UNION 으로 합쳐 우회합니다.SELECT 를 서브쿼리라고 합니다. 왜 필요할까요? "평균보다 비싼 상품" 을 찾으려면 평균을 먼저 알아야 합니다. 그런데 평균은 그 자체로 또 하나의 SELECT 입니다. 즉 "질문에 답하기 위해 먼저 답해야 하는 작은 질문" 이 있을 때 서브쿼리를 씁니다.= 로 비교: WHERE price > (SELECT AVG(price) FROM products).= 대신 IN 을 씁니다. "서울에 사는 고객이 낸 주문" 처럼 목록에 속하는가를 묻는 형태입니다.(a, b) = (서브쿼리) 형태입니다.WHERE 는 집계 전에 실행되므로 쓸 수 없고, HAVING 으로도 되지만 조인까지 얽히면 읽기 어려워집니다. 이럴 때 "집계 결과를 하나의 테이블처럼" 취급하는 것이 파생 테이블(FROM (SELECT ...) AS t)입니다.EXISTS 는 "그런 행이 하나라도 있으면 참" 입니다. 값을 가져오는 게 아니라 존재 여부만 봅니다. 그래서 안쪽 SELECT 에 무엇을 쓰든(1, *, NULL) 성능은 같습니다.> ANY (...) 는 "서브쿼리 결과 중 하나라도 보다 크면", > ALL (...) 은 "전부보다 크면" 입니다. 결국 > ANY = > MIN(...), > ALL = > MAX(...) 와 같습니다.| 상황 | 권장 |
|---|---|
| 존재 여부만 확인 | EXISTS / IN |
| 서브쿼리 쪽 컬럼도 결과에 필요 | JOIN |
| 없는 것을 찾기(안티 조인) | NOT EXISTS (NOT IN 은 위험, 아래 참조) |
| 1:N 조인인데 개수를 세야 함 | JOIN + COUNT(DISTINCT ...) 또는 파생 테이블 |
NULL 을 허용한다면 NOT IN 을 쓰지 마세요. 에러도 안 나고 조용히 0행을 반환합니다. "왜 결과가 안 나오지?" 하며 몇 시간을 날리는 대표적 버그입니다. 습관적으로 NOT EXISTS 를 쓰는 것이 가장 안전합니다. (반대로 IN 은 NULL 이 있어도 "있는 것" 은 정상적으로 찾아주므로 상대적으로 안전합니다. 문제는 부정형뿐입니다.)WITH name AS (...))입니다. 결과는 같지만, 읽는 사람은 "위에서 아래로" 자연스럽게 따라갈 수 있습니다. CTE 는 한 번 정의하고 여러 번 참조할 수 있습니다.UNION 은 두 결과를 붙인 뒤 중복을 제거합니다. UNION ALL 은 중복 제거 없이 그냥 이어붙입니다.UNION ALL 이 빠른 이유: "중복 제거" 는 공짜가 아닙니다. 모든 행을 서로 비교해야 하고, 그러려면 정렬하거나 해시 테이블을 만들어야 합니다. 중복이 없다는 걸 안다면 항상 UNION ALL 을 쓰세요(실행계획에서 차이가 확연합니다).ORDER BY / LIMIT 는 전체 결과에 적용됩니다. 개별 SELECT 에 걸고 싶다면 괄호로 감쌉니다.ORDER BY 에는 첫 번째 SELECT 의 결과 컬럼명만 쓸 수 있습니다. ORDER BY p.price 처럼 테이블 별칭을 붙이면 에러입니다. 두 번째 SELECT 의 별칭도 못 씁니다. 별칭을 붙였다면 그 별칭으로 정렬하세요.ORDER BY 는 의미가 없습니다. MySQL 은 UNION ALL 중간의 ORDER BY 를(LIMIT 이 없으면) 최적화 과정에서 버립니다. 정렬을 기대하고 썼다가 순서가 뒤죽박죽 나오는 원인입니다.INTERSECT — 양쪽에 모두 존재하는 행만 남깁니다. 예: "10만원 이상 상품" ∩ "후기가 달린 상품".EXCEPT — 왼쪽에는 있고 오른쪽에는 없는 행만 남깁니다. 다른 DB에서는 MINUS(Oracle) 라고 부르기도 합니다.INTERSECT ALL / EXCEPT ALL — ALL 을 붙이면 중복을 남깁니다. 남는 개수는 "양쪽 중 적은 쪽"(INTERSECT ALL) 또는 "왼쪽 개수 − 오른쪽 개수"(EXCEPT ALL) 입니다.INTERSECT 가 UNION / EXCEPT 보다 먼저 평가됩니다(곱셈이 덧셈보다 먼저인 것과 같습니다). 순서를 바꾸려면 괄호를 쓰세요.INSERT INTO orders VALUES (...) 처럼 컬럼을 생략하면, 나중에 테이블에 컬럼이 하나 추가되는 순간 모든 INSERT 문이 깨집니다. 항상 INSERT INTO orders (a, b, c) VALUES (...) 로 컬럼을 명시하세요.LAST_INSERT_ID() 는 배치의 첫 번째 행 ID 를 돌려줍니다. AUTO_INCREMENT 는 연속이므로 나머지는 first + 1, first + 2 … 로 계산할 수 있습니다.INSERT ... ON DUPLICATE KEY UPDATE — 진짜 UPSERT. PK/UNIQUE 충돌 시 지정한 컬럼만 UPDATE 합니다. VALUES(col)(8.0.20+ 부터는 별칭)로 삽입하려던 값을 참조합니다.REPLACE 는 사실 DELETE + INSERT 입니다. 이름은 "replace" 지만 UPDATE 가 아닙니다. 충돌하는 기존 행을 DELETE 하고 새 행을 INSERT 합니다. 이 차이가 사고를 부릅니다 — 기존 행의 AUTO_INCREMENT id 가 바뀌고, 명시하지 않은 컬럼은 DEFAULT 로 되돌아가며, 그 행을 참조하던 ON DELETE CASCADE FK 가 연쇄 삭제될 수 있습니다.UPDATE products SET stock = 0 (WHERE 없음!)을 실행하면 전 상품 재고가 0 이 됩니다. 이런 참사를 막는 스위치가 sql_safe_updates 입니다.
SET sql_safe_updates = 1; 이면 WHERE 가 있어도 키(인덱스) 컬럼을 쓰지 않으면 막습니다(전체 스캔 = 사실상 전체 갱신 위험). 키를 쓰거나 LIMIT 을 붙이면 통과합니다.DELETE vs TRUNCATE: DELETE 는 행 단위로 지우며 WHERE·트랜잭션·롤백이 됩니다. TRUNCATE 는 테이블을 통째로 비우고 AUTO_INCREMENT 를 초기화하며 훨씬 빠릅니다.TRUNCATE 는 DDL 이라 실행하는 순간 암묵적 커밋이 일어납니다. 트랜잭션으로 감싸도 롤백되지 않습니다. "전체 삭제니까 TRUNCATE 가 빠르지" 하고 운영에서 무심코 썼다가, 롤백을 기대할 수 없어 사고가 커지는 경우가 있습니다. 되돌릴 여지가 필요하면 DELETE, 확실히 비우고 초기화할 거면 TRUNCATE.| 함수 | 설명 | 예 |
|---|---|---|
CONCAT(a, b, ...) | 문자열 이어붙이기 | ⚠️ 인자 하나라도 NULL 이면 결과 NULL |
CONCAT_WS(sep, ...) | 구분자로 이어붙이기 | NULL 인자는 건너뜀 |
SUBSTRING(s, pos, len) | 부분 문자열(1-base) | SUBSTRING('abcdef', 2, 3) → bcd |
LEFT(s, n) / RIGHT(s, n) | 앞/뒤 n글자 | |
REPLACE(s, from, to) | 부분 문자열 치환 | 대소문자 구분 |
TRIM(s) / LTRIM / RTRIM | 공백 제거 | TRIM(BOTH 'x' FROM ...) 도 가능 |
LPAD(s, len, pad) / RPAD | 채워서 고정 길이 | LPAD('7', 3, '0') → 007 |
LOCATE(sub, s) (INSTR) | 위치 찾기(없으면 0) | 1-base |
LENGTH(s) / CHAR_LENGTH(s) | 바이트 길이 / 글자 수 | ⚠️ utf8mb4 한글은 둘이 다름 |
UPPER / LOWER | 대/소문자 | |
REGEXP_REPLACE(s, pat, rep) | 정규식 치환 | 8.0+ |
| 함수 | 설명 | 예 |
|---|---|---|
ROUND(x, d) | 반올림(자릿수 d) | ROUND(3.14159, 2) → 3.14 |
CEIL(x) (CEILING) | 올림 | CEIL(3.1) → 4 |
FLOOR(x) | 내림 | FLOOR(3.9) → 3 |
TRUNCATE(x, d) | 버림(반올림 아님) | TRUNCATE(3.99, 1) → 3.9 |
MOD(a, b) (a % b) | 나머지 | MOD(10, 3) → 1 |
ABS / POWER / SQRT / SIGN | 절댓값 / 거듭제곱 / 제곱근 / 부호 |
TRUNCATE(x, d) 와 문장 TRUNCATE TABLE 은 이름만 같고 전혀 다릅니다.| 함수 | 설명 | 예 |
|---|---|---|
NOW() / CURDATE() / CURTIME() | 현재 일시 / 날짜 / 시각 | |
DATE_ADD(d, INTERVAL n unit) | 날짜 더하기 | DATE_ADD(d, INTERVAL 7 DAY) |
DATE_SUB(d, INTERVAL n unit) | 날짜 빼기 | INTERVAL 1 MONTH 등 |
DATEDIFF(a, b) | 두 날짜의 일수 차 | a - b (일 단위) |
TIMESTAMPDIFF(unit, a, b) | 단위 지정 차이 | TIMESTAMPDIFF(YEAR, 생일, NOW()) → 나이 |
DATE_FORMAT(d, fmt) | 날짜 → 문자열 | DATE_FORMAT(d, '%Y-%m') → 2024-01 |
STR_TO_DATE(s, fmt) | 문자열 → 날짜 | DATE_FORMAT 의 역 |
LAST_DAY(d) | 그 달의 마지막 날 | 월말 계산 |
YEAR/MONTH/DAY/HOUR(d) | 구성요소 추출 | |
YEARWEEK(d) / WEEK(d, mode) | 주차 | ⚠️ 주 시작 요일(mode)에 주의 |
WHERE DATE(created) = '2024-01-01' 처럼 컬럼에 함수를 씌우면 인덱스를 못 탑니다. → WHERE created >= '2024-01-01' AND created < '2024-01-02' 로 바꾸세요(§5 날짜 BETWEEN 함정과 같은 맥락).| 함수 | 설명 |
|---|---|
IF(cond, a, b) | 조건이 참이면 a, 아니면 b (MySQL 전용) |
IFNULL(a, b) | a 가 NULL 이면 b (§6 참고) |
NULLIF(a, b) | a = b 이면 NULL, 아니면 a |
COALESCE(a, b, ...) | 첫 번째 NULL 아닌 값 (표준) |
CASE WHEN ... THEN ... ELSE ... END | 다분기 조건. 집계와 결합해 조건부 집계로 자주 씀 |
CAST(x AS type) | 표준 형변환. CAST('123' AS SIGNED), AS DECIMAL(10,2), AS DATE 등 |
CONVERT(x, type) | CAST 의 MySQL 문법. CONVERT(x USING utf8mb4) 로 문자셋 변환도 |