SQL 핵심 문법 완벽 가이드

SQL(Structured Query Language)은 관계형 데이터베이스 관리 시스템(RDBMS)에서 데이터를 관리하기 위해 설계된 표준 언어입니다. ANSI SQL이 표준이지만, 각 DBMS(예: MySQL, PostgreSQL, Oracle)는 고유한 확장 기능(PL/SQL, T-SQL 등)을 제공합니다. 이 문서에서는 데이터 정의, 조작, 쿼리 및 제어에 필요한 핵심 SQL 문법을 예제와 함께 설명합니다.

1. 데이터베이스 및 테이블 기본

데이터베이스와 테이블은 데이터를 구조화하는 기본 단위입니다. 테이블은 행과 열로 구성되며, 각 테이블에는 데이터를 고유하게 식별하는 기본 키(Primary Key)가 있어야 합니다. 기본 키 값은 수정하거나 재사용할 수 없습니다. SQL은 대소문자를 구분하지 않지만, 테이블명, 컬럼명은 DBMS 설정에 따라 달라질 수 있습니다.

주석

-- 한 줄 주석
/*
  여러 줄 주석
*/
SELECT * FROM users; -- 인라인 주석

데이터베이스 생성 및 선택

CREATE DATABASE shop;
USE shop;

2. 테이블 생성 (CREATE TABLE)

CREATE TABLE 문을 사용하여 새 테이블을 정의합니다. 각 컬럼의 데이터 타입, NULL 허용 여부, 기본값, 자동 증가 등을 지정할 수 있습니다.

CREATE TABLE products (
    product_id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    price DECIMAL(10,2) DEFAULT 0.00,
    created_at DATE NULL,
    PRIMARY KEY (product_id)
);

3. 테이블 구조 변경 (ALTER TABLE)

이미 존재하는 테이블에 컬럼을 추가, 삭제하거나 테이블 자체를 제거합니다.

-- 컬럼 추가
ALTER TABLE products ADD quantity INT NOT NULL DEFAULT 0;

-- 컬럼 삭제
ALTER TABLE products DROP COLUMN quantity;

-- 테이블 삭제
DROP TABLE products;

4. 데이터 삽입 (INSERT)

새로운 데이터를 테이블에 추가합니다.

-- 단일 행 삽입
INSERT INTO products (name, price) VALUES ('노트북', 1500000);

-- 다른 테이블의 데이터를 조회하여 삽입
INSERT INTO discounted_products (product_id, name)
SELECT product_id, name FROM products WHERE price > 1000000;

-- 테이블 구조와 데이터를 복사하여 새 테이블 생성
CREATE TABLE products_backup AS SELECT * FROM products;

5. 데이터 갱신 (UPDATE)

기존 데이터를 수정합니다. 항상 WHERE 절을 사용하여 대상 행을 명확히 지정해야 합니다. 그렇지 않으면 모든 행이 수정됩니다.

UPDATE products
SET price = 1400000, name = '울트라북'
WHERE product_id = 1;

6. 데이터 삭제 (DELETE & TRUNCATE)

데이터를 삭제하는 두 가지 방법이 있습니다.

-- 조건에 맞는 특정 행 삭제
DELETE FROM products WHERE product_id = 5;

-- 테이블의 모든 행을 빠르게 삭제 (롤백 불가능)
TRUNCATE TABLE products;

7. 데이터 조회 (SELECT)

가장 자주 사용되는 명령어입니다. DISTINCT는 중복을 제거하고, LIMIT는 결과 행 수를 제한합니다.

-- 특정 컬럼 조회
SELECT name, price FROM products;

-- 중복 제거 (모든 선택된 컬럼 값이 동일해야 함)
SELECT DISTINCT category FROM products;

-- 결과 제한 (LIMIT)
-- 처음 5개 행
SELECT * FROM products LIMIT 5;

-- 3번째 행부터 2개 행 조회 (0부터 시작)
SELECT * FROM products LIMIT 2 OFFSET 2;
-- 또는 MySQL 단축 문법
SELECT * FROM products LIMIT 2, 2;

8. 정렬 (ORDER BY)

하나 이상의 컬럼을 기준으로 결과를 정렬합니다. ASC(오름차순, 기본값) 또는 DESC(내림차순)를 사용합니다.

SELECT * FROM products
ORDER BY price DESC, name ASC;

9. 데이터 필터링 (WHERE)

특정 조건을 만족하는 행만 선택합니다. 가능하면 모든 데이터를 가져온 후 애플리케이션에서 필터링하는 것보다 SQL 단계에서 필터링하는 것이 효율적입니다.

SELECT * FROM products
WHERE price BETWEEN 500000 AND 2000000;

-- NULL 체크
SELECT * FROM products WHERE description IS NULL;

-- 여러 조건 결합 (AND, OR, NOT)
SELECT * FROM products
WHERE (category = '전자기기' OR category = '컴퓨터')
  AND price > 1000000;

10. 패턴 매칭 (LIKE & 와일드카드)

텍스트 필드에서 복잡한 패턴을 검색할 때 사용합니다. 성능 저하를 유발할 수 있으므로, 특히 패턴이 문자열 앞에 오는 경우 주의해야 합니다.

  • % : 0개 이상의 임의 문자
  • _ : 정확히 1개의 임의 문자
-- 'A'로 시작하는 모든 상품
SELECT * FROM products WHERE name LIKE 'A%';

-- 두 번째 글자가 'b'인 모든 상품
SELECT * FROM products WHERE name LIKE '_b%';

-- 'S' 또는 'T'로 시작하는 상품
SELECT * FROM products WHERE name LIKE '[ST]%';

11. 계산 필드 (Concatenation, Aliases)

서버 측에서 데이터를 가공하면 클라이언트의 부하를 줄이고 네트워크 트래픽을 감소시킵니다. AS 키워드로 별칭을 부여합니다.

-- 수학 연산
SELECT product_id, price * quantity AS total_value FROM products;

-- 문자열 연결
SELECT CONCAT(name, ' (', category, ')') AS product_info FROM products;

12. 내장 함수

DBMS별로 함수가 다르므로 이식성을 고려해야 합니다. 여기서는 MySQL 기준으로 설명합니다.

집계 함수

함수설명
COUNT()행의 개수
SUM()합계
AVG()평균
MAX()최대값
MIN()최소값

COUNT(*)는 NULL을 포함한 모든 행을 세고, COUNT(column)은 NULL이 아닌 값만 셉니다. AVG()는 NULL 행을 무시합니다.

SELECT AVG(DISTINCT price) AS avg_distinct_price FROM products;

문자열 함수

함수설명
LEFT() / RIGHT()왼쪽/오른쪽에서 문자 추출
LOWER() / UPPER()소문자/대문자 변환
LENGTH()문자열 길이 반환
TRIM()앞뒤 공백 제거
SELECT * FROM products WHERE SOUNDEX(name) = SOUNDEX('appple');

날짜 및 시간 함수

함수설명
NOW()현재 날짜와 시간
DATE()날짜 부분 추출
YEAR() / MONTH() / DAY()각 구성 요소 추출
DATEDIFF()두 날짜 간 차이
DATE_ADD()날짜 더하기

13. 데이터 그룹화 (GROUP BY & HAVING)

GROUP BY는 동일한 값을 가진 행을 그룹으로 묶고, HAVING은 그룹화된 결과에 대한 필터 조건을 지정합니다. WHERE는 그룹화 에 개별 행을 필터링합니다.

SELECT category, COUNT(*) AS product_count, AVG(price) AS avg_price
FROM products
WHERE price > 0
GROUP BY category
HAVING product_count >= 3
ORDER BY avg_price DESC;

14. 서브쿼리 (Subquery)

하나의 SQL 문 안에 다른 SQL 문을 중첩하여 사용합니다. 서브쿼리는 일반적으로 단일 컬럼과 단일 행 또는 여러 행을 반환합니다.

-- WHERE 절에서 사용
SELECT * FROM products
WHERE category_id IN (SELECT category_id FROM categories WHERE active = 1);

-- SELECT 절에서 사용 (스칼라 서브쿼리)
SELECT name,
       (SELECT AVG(price) FROM products) AS avg_price
FROM products;

15. 테이블 조인 (JOIN)

여러 테이블을 연결하여 데이터를 검색합니다. ON 절을 사용하여 조인 조건을 지정합니다. 적절한 인덱스 없이 조인을 많이 사용하면 성능이 저하될 수 있습니다.

내부 조인 (INNER JOIN)

SELECT p.name, c.name AS category_name
FROM products p
INNER JOIN categories c ON p.category_id = c.category_id;

자체 조인 (Self-Join)

-- 같은 부서에 있는 직원 찾기
SELECT e1.name AS employee, e2.name AS colleague
FROM employees e1
INNER JOIN employees e2 ON e1.department_id = e2.department_id AND e1.employee_id != e2.employee_id;

자연 조인 (NATURAL JOIN)

-- 동일한 이름을 가진 컬럼을 자동으로 연결
SELECT p.name, c.name FROM products p NATURAL JOIN categories c;

외부 조인 (OUTER JOIN)

-- 왼쪽 테이블의 모든 행을 유지 (짝이 없는 오른쪽 테이블 값은 NULL)
SELECT c.name, o.order_id
FROM customers c
LEFT OUTER JOIN orders o ON c.cust_id = o.cust_id;

16. 집합 연산 (UNION)

두 개 이상의 SELECT 결과를 하나로 결합합니다. 각 쿼리는 동일한 개수의 컬럼을 가져야 합니다. UNION ALL은 중복을 제거하지 않습니다.

SELECT name, 'VIP' AS status FROM vip_customers
UNION ALL
SELECT name, 'Regular' FROM regular_customers;

17. 뷰 (View)

저장된 쿼리 결과를 가상의 테이블처럼 사용합니다. 데이터를 물리적으로 저장하지 않으며, 복잡한 쿼리를 단순화하고 보안을 강화하는 데 유용합니다.

CREATE VIEW product_summary AS
SELECT p.name, c.name AS category_name, p.price
FROM products p
JOIN categories c ON p.category_id = c.category_id
WHERE p.active = 1;

18. 저장 프로시저 (Stored Procedure)

하나의 이름으로 저장된 SQL 문들의 집합입니다. 미리 컴파일되어 실행 속도가 빠르고, 코드 재사용성과 보안을 높일 수 있습니다.

DELIMITER //
CREATE PROCEDURE GetProductCount(OUT total INT)
BEGIN
    SELECT COUNT(*) INTO total FROM products;
END //
DELIMITER ;

CALL GetProductCount(@count);
SELECT @count;

19. 커서 (Cursor)

저장 프로시저 내에서 쿼리 결과 집합을 한 행씩 처리할 때 사용합니다. 명시적으로 선언, 열기, 인출, 닫기 과정을 거쳐야 합니다.

DELIMITER //
CREATE PROCEDURE ProcessProducts()
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE p_name VARCHAR(100);
    DECLARE cur CURSOR FOR SELECT name FROM products;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    OPEN cur;
    read_loop: LOOP
        FETCH cur INTO p_name;
        IF done THEN
            LEAVE read_loop;
        END IF;
        -- 각 상품 이름을 처리
    END LOOP;
    CLOSE cur;
END //
DELIMITER ;

20. 트리거 (Trigger)

특정 테이블에서 INSERT, UPDATE, DELETE 이벤트가 발생할 때 자동으로 실행되는 코드입니다. 데이터 무결성 유지, 감사 로그 기록 등에 사용됩니다.

CREATE TRIGGER before_product_update
BEFORE UPDATE ON products
FOR EACH ROW
SET NEW.updated_at = NOW();

21. 트랜잭션 (Transaction)

하나의 논리적 작업 단위를 구성하는 SQL 문의 그룹입니다. COMMIT으로 영구 저장하고, ROLLBACK으로 작업을 취소할 수 있습니다. SAVEPOINT를 사용하면 트랜잭션 내 특정 지점으로만 롤백할 수 있습니다.

START TRANSACTION;
UPDATE accounts SET balance = balance - 1000 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE user_id = 2;
COMMIT;
-- 문제 발생 시: ROLLBACK;

22. 문자 집합 및 데이터 정렬 (Charset & Collation)

문자 집합은 데이터를 표현하는 문자들의 집합이며, 데이터 정렬(Collation)은 문자 비교 및 정렬 규칙을 정의합니다. 테이블이나 컬럼 레벨에서 설정할 수 있습니다.

CREATE TABLE articles (
    title VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
) DEFAULT CHARACTER SET utf8mb4;

23. 사용자 및 권한 관리

데이터베이스 보안을 위해 사용자 계정을 생성하고 권한을 부여하거나 제거합니다.

-- 사용자 생성
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'secure_pass';

-- 권한 부여
GRANT SELECT, INSERT, UPDATE ON shop.* TO 'app_user'@'localhost';

-- 권한 제거
REVOKE DELETE ON shop.* FROM 'app_user'@'localhost';

-- 권한 확인
SHOW GRANTS FOR 'app_user'@'localhost';

-- 비밀번호 변경
SET PASSWORD FOR 'app_user'@'localhost' = 'new_secure_pass';

태그: SQL 데이터베이스 MySQL CRUD JOIN

6월 10일 21:49에 게시됨