데이터베이스 Collation 정렬·비교 규칙과 인코딩 차이

개요 Collation은 문자열 정렬과 비교 규칙 정의 테이블에 저장되는 바이트 표현은 인코딩(character set)으로 결정되며, 정렬과 비교 결과는 Collation으로 결정됨 둘은 목적과 적용 지점이 다름 핵심 개념 Character set은 문자 ↔ 바이트 매핑 Collation은 특정 문자 집합 위에서 정렬 순서와 동일성 판단 규칙 정의 대소문자 민감도(cs), 대소문자 비민감도(ci), 악센트 민감도(as), 악센트 비민감도(ai) 같은 속성으로 비교 엄격도 제어 언어별 규칙 존재하며 같은 인코딩이라도 Collation에 따라 결과 달라짐 동작 원리와 적용 범위 적용 레벨 계층 구조: 서버 기본값 → 데이터베이스 → 테이블 → 컬럼 → 표현식 단위 오버라이드 가능 영향 받는 연산: ORDER BY, GROUP BY, DISTINCT, LIKE, = 등 문자열 비교 연산 전반 인덱스는 해당 컬럼의 Collation 기준으로 구성되며 정렬 규칙이 범위 검색과 정렬 최적화에 직접 영향 다국어 환경에선 ICU 기반 Collation이나 언어 특화 Collation 선택 필요 ...

January 8, 2026

VARCHAR(n) 길이 기준 정리 — 글자 수인가 바이트 수인가

개념/배경 VARCHAR(n)에서 n을 글자 수로 볼지 바이트 수로 볼지 혼동 많음 표준 SQL의 character varying(n)은 최대 글자 수 의미이나, 실제 구현은 DBMS와 문자셋 설정에 따라 달라짐 멀티바이트 문자셋에서는 저장 바이트 수와 글자 수가 다름. 길이 제한은 글자 수 기준일 수 있으나 내부 저장은 바이트 단위로 이뤄짐 DBMS별 동작 MySQL VARCHAR(n)에서 n은 글자 수 의미 utf8mb4 사용 시 글자 하나가 최대 4바이트까지 소요. 행 크기 제한 등으로 인해 저장 가능 여부는 바이트 한계에도 영향 받음 PostgreSQL character varying(n)에서 n은 글자 수 의미 저장은 바이트 단위이나 제약은 글자 수 기준으로 평가 SQL Server varchar(n)은 n이 바이트 수. 멀티바이트 문자 사용 시 같은 n이라도 담을 수 있는 글자 수 감소 nvarchar(n)은 n이 글자 수. 유니코드 2바이트 단위 저장. 글자 수 기준 제약 필요 시 nvarchar 사용 권장 Oracle VARCHAR2(n)은 기본이 바이트 기준. 세션/시스템에서 CHAR semantics 또는 컬럼 정의 시 VARCHAR2(n CHAR)로 명시하면 글자 수 기준 실무 팁 한글 100자, 영어 100자 모두 허용 기대라면 글자 수 기준 타입 필요 MySQL VARCHAR(100), PostgreSQL varchar(100), SQL Server에서는 nvarchar(100), Oracle에서는 VARCHAR2(100 CHAR) 선택 저장 바이트 한계 고려 필요. MySQL은 행 크기 한계, Oracle/SQL Server도 페이지 크기 등 제약 존재 길이 함수 차이 주의. 바이트 길이와 글자 길이 함수가 다른 경우 존재. 예를 들어 글자 길이 검증은 문자 길이 함수 사용 권장 이모지, 결합 문자 등 특수 유니코드 조합은 사용자 체감 글자 수와 코드 포인트 수가 다를 수 있음. 제품 요구사항에 맞는 길이 기준 정의 필요 정리 VARCHAR(n)이 항상 바이트 무관이라는 주장은 오해 많은 DBMS에서 n은 글자 수지만, SQL Server의 varchar처럼 바이트 기준인 구현 존재 문자셋과 저장 한계를 함께 고려해야 안정적인 길이 설계 가능 한글도 100글자, 영어도 100글자라는 기대를 보장하려면 글자 수 기준 타입과 설정을 명시적으로 선택할 것 참고자료 https://dev.mysql.com/doc/refman/8.0/en/char.html https://www.postgresql.org/docs/current/datatype-character.html https://learn.microsoft.com/sql/t-sql/data-types/char-and-varchar-transact-sql https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Data-Types.html#GUID-3B0B0A24-FA05-4A1F-902E-2E6D0BF85673

December 5, 2025

MySQL 테이블 이름 변경 RENAME TABLE vs ALTER TABLE 정리

개요 테이블 이름 변경은 ALTER TABLE로도 가능하지만, RENAME TABLE을 쓰면 다수 테이블을 한 번에 처리 가능하며 같은 서버 내 다른 데이터베이스로 이동까지 가능함 핵심 차이 ALTER TABLE RENAME은 단일 테이블 대상 RENAME TABLE은 여러 테이블을 한 문장으로 변경 가능 스키마 간 이동 지원 current_db.table에서 other_db.table로 변경 가능 동일 트랜잭션처럼 동작하는 원자성 제공, 다중 변경 중 하나라도 실패 시 전체 미적용 권한 요구 사항 존재, 원본 테이블에 ALTER와 DROP, 대상 스키마에 CREATE 권한 필요 트리거와 외래키 메타데이터는 함께 유지되나, 뷰나 저장 프로시저의 하드코딩된 참조는 자동 갱신되지 않음 사용법 단일 테이블 이름 변경 RENAME TABLE old_table TO new_table; 단일 테이블 이름 변경 ALTER 사용 ALTER TABLE old_table RENAME new_table; 다수 테이블 이름 일괄 변경 RENAME TABLE old_table1 TO new_table1, old_table2 TO new_table2, old_table3 TO new_table3; 다른 데이터베이스로 이동 같은 서버 내 RENAME TABLE current_db.table_name TO other_db.table_name;주의 사항 대상 이름이 이미 존재하면 실패 교차 서버 이동 불가, 같은 서버 인스턴스 내 스키마 간 이동만 가능 실행 시 메타데이터 락 획득, 짧은 구간 동안 읽기나 쓰기 대기 가능 배치 변경 전 사전 검증 권장, 이름 충돌 여부와 권한 확인 뷰나 프로시저 참조는 수동 점검 필요, 린트나 간단한 탐색 쿼리로 영향 범위 확인 권장 정리 단일 변경만 필요하면 ALTER로 충분하나, 다수 변경이나 스키마 이동까지 고려하면 RENAME TABLE이 더 실용적 선택 원자적 일괄 변경과 스키마 간 이동을 활용하되, 권한과 의존성 영향 검증을 선행할 것 ...

November 24, 2025

Prisma에는 왜 JOIN이 없을까: ORM 패턴, 스키마, 내부 동작 정리

개요 Prisma로 관계형 데이터베이스를 다루다 보면 자연스럽게 드는 질문이 있음 Prisma에서는 JOIN이 어디로 갔나 하는 질문임 개발자가 작성하는 Prisma Client API에는 JOIN이 없고, 서브쿼리도 보이지 않음 정말로 없는지, 없다면 왜 그런지, 어떤 트레이드오프가 있는지 정리함 ORM이란 무엇인가 ORM은 객체와 관계형 데이터베이스 간의 매핑을 제공하는 아이디어이자 구현체 집합을 의미함 애플리케이션에서 모델을 통해 데이터베이스 테이블을 간접 제어하는 추상화 계층 제공 SQL을 직접 작성하지 않고 데이터 접근 로직을 일관된 API로 수행 가능 데이터베이스 의존성 완화 효과 기대 ...

November 23, 2025

Prisma findUnique에서 where와 include 제대로 쓰기

개요 findUnique로 단일 레코드 조회하면서 관련된 데이터까지 한 번에 가져오고 싶을 때 where와 include를 어떻게 조합해야 하는지 정리함 관계 필터링을 where에 넣을 수 있는지, include에서 필터가 가능한지 헷갈리기 쉬운 지점 정리 핵심 개념 findUnique는 유니크 키로 정확히 하나의 레코드를 찾는 용도 findUnique의 where는 유니크 필드만 허용됨, 관계 필터나 일반 조건 결합 불가 include는 조회된 레코드에 대한 연관 레코드를 함께 가져오는 옵션 to-many 관계에 한해 include 내부에서 where 사용 가능, to-one 관계는 where 불가 where 사용 패턴 잘못된 예시 // findUnique에 관계 필터 결합 시도 → 타입 에러 // (User(1) → Post(N) → Metadata(1) 관계) await db.user.findUnique({ where: { id: userId, // 'posts'는 유니크 필드가 아니므로 'where'에서 관계 필터링 불가 posts: { some: { metadata: { editorEmail: email } } }, }, }); 올바른 최소 조건 await db.user.findUnique({ where: { id: userId }, // 'id'는 유니크 필드 }); 관계 조건이 필요하면 findFirst 또는 findMany 사용 // 'findUnique'가 아닌 'findFirst'를 사용하면 // 'where'에 관계 필터를 포함할 수 있음 await db.user.findFirst({ where: { id: userId, posts: { some: { metadata: { editorEmail: email } } }, }, });요약하면 findUnique에는 유니크 조건만, 관계 기반 필터는 findFirst 또는 findMany로 처리함 ...

November 12, 2025

Prisma cursor 기반 페이지네이션 동작 원리와 skip: 1의 의미

개요 Prisma에서 cursor는 특정 레코드 지점부터 결과를 읽기 시작하는 기준점으로 동작함 skip: 1은 해당 cursor 레코드를 결과에서 제외하기 위한 옵션으로, 페이지 간 중복을 제거하는 데 사용함 핵심 동작 cursor는 그 지점부터 시작 await prisma.user.findMany({ cursor: { id: 100 }, take: 5, orderBy: { id: "asc" }, }); // 결과: 100부터 시작해 5개 반환 skip: 1은 cursor에 해당하는 레코드를 건너뜀 await prisma.user.findMany({ cursor: { id: 100 }, skip: 1, take: 5, orderBy: { id: "asc" }, }); // 결과: 101부터 5개 반환 예시로 보는 차이 데이터가 아래와 같다고 가정 ...

October 29, 2025

Prisma findMany 가이드: where, select/include, 정렬·페이징, in/비교 연산자

개요 Prisma의 findMany는 다중 레코드 조회용 메서드 기본값은 대상 모델의 모든 레코드 반환 where 필터, select/include, 정렬, 페이징, 중복 제거 등 옵션 지원 옵션 조합으로 조건 기반 조회를 간결하게 구성 가능 기본 사용법 가장 단순한 호출 형태 const users = await prisma.user.findMany();주요 옵션 옵션은 필요한 것만 선택적으로 사용 where: 조건 필터링 select: 필드 서브셋 선택 include: 관계 데이터 로드 orderBy: 정렬 기준 지정 skip, take: 오프셋 기반 페이징 distinct: 특정 필드 기준 중복 제거 where로 조건 필터링 단일 조건부터 복합 조건까지 표현 가능 ...

October 28, 2025

SQL LIMIT와 OFFSET 사용법 정리: 행 개수 제한과 페이징

기본 문법 LIMIT는 SELECT 결과에서 반환할 행 수를 제한하는 구문 WHERE와 ORDER BY 뒤, 즉 SELECT 문 끝에 위치 두 가지 형태 사용 SELECT 컬럼명 FROM 테이블명 LIMIT 개수 SELECT 컬럼명 FROM 테이블명 LIMIT 개수 OFFSET 시작_인덱스일부 dialect에서는 아래 축약형도 존재 -- MySQL, SQLite SELECT 컬럼명 FROM 테이블명 LIMIT 시작_인덱스, 개수1. LIMIT 개수 LIMIT 뒤 숫자는 인덱스가 아닌 개수 SELECT * FROM Book LIMIT 1현재 정렬 기준에서 맨 위 행 1개만 반환 ORDER BY가 없으면 반환되는 행은 비결정적일 수 있음 2. LIMIT 개수 OFFSET 시작 OFFSET은 0부터 시작 다음 예시는 4번째 행부터 1개 반환 ...

October 20, 2025