옵션
집 Skill 데이터베이스 관리 sql-database-assistant

sql-database-assistant

alirezarezvani/claude-skills alirezarezvani/claude-skills

자연어를 SQL 쿼리로 변환하고, 데이터베이스 성능을 최적화하며, 마이그레이션을 생성하고, 스키마를 탐색하며, PostgreSQL, MySQL, SQLite 및 SQL Server에서 ORM을 활용할 수 있습니다.

...모든 것을 확장하십시오
1
업데이트 된 시간 2026년 9월 2일

SQL 데이터베이스 어시스턴트 - POWERFUL 티어 스킬

개요

데이터베이스 설계의 운영 지원자입니다. 데이터베이스 설계자는 스키마 아키텍처에 중점을 두고, 데이터베이스 스키마 설계자는 ERD 모델링을 담당하는 반면, 이 기술은 쿼리 작성, 성능 최적화, 마이그레이션 생성, 애플리케이션 코드와 데이터베이스 엔진 간의 격차 해소 등 일상적인 업무를 다룹니다.

핵심 역량

  • 자연어에서 SQL로 변환 — 요구 사항을 정확하고 성능이 뛰어난 쿼리로 변환
  • 스키마 탐색 — PostgreSQL, MySQL, SQLite, SQL Server 등 다양한 데이터베이스의 실행 중인 상태를 분석
  • 쿼리 최적화 — EXPLAIN 분석, 인덱스 권장 사항, N+1 문제 탐지, 쿼리 재작성 패턴
  • 마이그레이션 생성 — 업그레이드/다운그레이드 스크립트, 무중단 전략, 롤백 계획
  • ORM 통합 — Prisma, Drizzle, TypeORM, SQLAlchemy 패턴 및 비상 대책
  • 다중 데이터베이스 지원 — 호환성 지침이 포함된 방언 인식 SQL

도구

스크립트 목적
scripts/query_optimizer.py 성능 문제를 파악하기 위한 SQL 쿼리의 정적 분석
scripts/migration_generator.py 변경 내역 설명을 바탕으로 마이그레이션 파일 템플릿 생성
scripts/schema_explorer.py 인트로스펙션 쿼리를 기반으로 스키마 문서 생성

자연어에서 SQL로

변환 패턴

요구 사항을 SQL로 변환할 때는 다음 순서를 따르십시오:

  1. 엔티티 식별 — 명사를 테이블에 매핑
  2. 관계 식별 — 동사를 JOIN 또는 하위 쿼리에 매핑
  3. 필터 식별 — 형용사/조건을 WHERE 절에 매핑
  4. 집계 식별 — “합계”, “평균”, “개수”를 GROUP BY에 매핑
  5. 정렬 기준 식별 — “top”, “latest”, “highest”를 ORDER BY + LIMIT에 매핑

일반적인 쿼리 템플릿

그룹별 상위 N개 (윈도우 함수)

SELECT * FROM (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rn
  FROM employees
) ranked WHERE rn <= 3;

누적 합계

SELECT date, amount,
  SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM transactions;

갭 감지

SELECT curr.id, curr.seq_num, prev.seq_num AS prev_seq
FROM records curr
LEFT JOIN records prev ON prev.seq_num = curr.seq_num - 1
WHERE prev.id IS NULL AND curr.seq_num > 1;

UPSERT (PostgreSQL)

INSERT INTO settings (key, value, updated_at)
VALUES ('theme', 'dark', NOW())
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = EXCLUDED.updated_at;

UPSERT (MySQL)

INSERT INTO settings (key_name, value, updated_at)
VALUES ('theme', 'dark', NOW())
ON DUPLICATE KEY UPDATE value = VALUES(value), updated_at = VALUES(updated_at);

JOIN, CTE, 윈도우 함수, JSON 연산 등에 대한 자세한 내용은 references/query_patterns.md를 참조하십시오.

스키마 탐색

자기 반성 쿼리

PostgreSQL — 테이블 및 열 나열

SELECT table_name, column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public'
ORDER BY table_name, ordinal_position;

PostgreSQL — 외래 키

SELECT tc.table_name, kcu.column_name,
  ccu.table_name AS foreign_table, ccu.column_name AS foreign_column
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY';

MySQL — 테이블 크기

SELECT table_name, table_rows,
  ROUND(data_length / 1024 / 1024, 2) AS data_mb,
  ROUND(index_length / 1024 / 1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY data_length DESC;

SQLite — 스키마 덤프

SELECT name, sql FROM sqlite_master WHERE type = 'table' ORDER BY name;

SQL Server — 데이터 유형이 지정된 열

SELECT t.name AS table_name, c.name AS column_name,
  ty.name AS data_type, c.max_length, c.is_nullable
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
ORDER BY t.name, c.column_id;

스키마에서 문서 생성

scripts/schema_explorer.py를 사용하여 마크다운 또는 JSON 형식의 문서를 생성합니다:

python scripts/schema_explorer.py --dialect postgres --tables all --format md
python scripts/schema_explorer.py --dialect mysql --tables users,orders --format json --json

쿼리 최적화

EXPLAIN 분석 워크플로우

  1. EXPLAIN ANALYZE (PostgreSQL) 또는 EXPLAIN FORMAT=JSON (MySQL)실행
  2. 가장 비용이 많이 드는 노드 식별 — 대용량 테이블에 대한 순차 스캔(Seq Scan), 높은 행 수 추정치가 있는 중첩 루프(Nested Loop)
  3. 누락된 인덱스 확인 — 필터링된 열에 대한 순차 스캔
  4. 추정 오류 확인 — 계획된 행 수와 실제 행 수의 차이가 통계 정보의 갱신이 필요함을 시사합니다
  5. JOIN 순서 평가 — 결과 집합이 가장 작은 조인이 먼저 수행되도록 확인

인덱스 권장 사항 체크리스트

  • 선택성이 높은 WHERE 절 내의 열
  • JOIN 조건에 포함된 열(외래 키)
  • LIMIT과 함께 사용되는 ORDER BY 절의 열
  • 다중 열 WHERE 조건과 일치하는 복합 인덱스(선택성이 가장 높은 열을 먼저)
  • 상수 필터가 포함된 쿼리를 위한 부분 인덱스(예: WHERE status = 'active')
  • 읽기 위주 쿼리에서 테이블 조회(lookup)를 피하기 위한 커버링 인덱스

쿼리 재작성 패턴

반패턴 재작성
SELECT * FROM orders SELECT id, status, total FROM orders (명시적 열)
WHERE YEAR(created_at) = 2025 WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01' (SARG 가능)
SELECT 절 내의 상관 하위 쿼리 집계 기능이 포함된 LEFT JOIN
NULL이 포함된NOT IN (SELECT ...) NOT EXISTS (SELECT 1 ...)
필요하지 않을 때UNION (중복 제거) UNION ALL
LIKE '%search%' 전체 텍스트 검색 인덱스 (GIN/FULLTEXT)
ORDER BY RAND() 애플리케이션 측 무작위 표본 추출 또는 TABLESAMPLE

N+1 탐지

증상:

  • 부모 행마다 하나의 쿼리를 실행하는 애플리케이션 루프
  • 루프 내에서 ORM이 관련 엔티티를 지연 로딩함
  • 쿼리 로그에 ID만 다른 수백 개의 동일한 SELECT 패턴이 표시됨

해결 방법:

  • 이거어 로딩(Prisma의include, SQLAlchemy의 joinedload ) 사용
  • WHERE id IN (...)을 사용하여 쿼리를 일괄 처리
  • GraphQL 리졸버에 DataLoader 패턴 사용

정적 분석 도구

python scripts/query_optimizer.py --query "SELECT * FROM orders WHERE status = 'pending'" --dialect postgres
python scripts/query_optimizer.py --query queries.sql --dialect mysql --json

EXPLAIN 실행 계획 해석, 인덱스 유형 및 연결 풀링에 대해서는 references/optimization_guide.md를 참조하십시오.

마이그레이션 생성

다운타임 없는 마이그레이션 패턴

열 추가 (안전)

-- 상향 마이그레이션
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- 하향 마이그레이션
ALTER TABLE users DROP COLUMN phone;

열 이름 변경 (확장-축소)

-- 1단계: 새 열 추가
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
-- 2단계: 데이터 백필
UPDATE users SET full_name = name;
-- 3단계: 두 열 모두를 읽는 앱 배포
-- 4단계: 새 열만 쓰는 앱 배포
-- 5단계: 기존 열 삭제
ALTER TABLE users DROP COLUMN name;

NOT NULL 열 추가 (안전한 순서)

-- 1단계: NULL 허용 열 추가
ALTER TABLE orders ADD COLUMN region VARCHAR(50);
-- 2단계: 기본값으로 데이터 보충
UPDATE orders SET region = 'unknown' WHERE region IS NULL;
-- 3단계: 제약 조건 추가
ALTER TABLE orders ALTER COLUMN region SET NOT NULL;
ALTER TABLE orders ALTER COLUMN region SET DEFAULT 'unknown';

인덱스 생성 (비차단, PostgreSQL)

CREATE INDEX CONCURRENTLY idx_orders_status ON orders (status);

데이터 백필 전략

  • 일괄 업데이트 — 잠금 경합을 피하기 위해 1,000~10,000행 단위로 처리
  • 백그라운드 작업 — 진행 상황 추적과 함께 백필을 비동기적으로 실행
  • 이중 기록 — 전환 기간 동안 기존 열과 새 열에 동시에 기록
  • 검증 쿼리 — 각 배치 처리 후 행 수와 데이터 무결성을 확인

롤백 전략

모든 마이그레이션에는 되돌릴 수 있는 다운 스크립트가 반드시 있어야 합니다. 되돌릴 수 없는 변경의 경우:

  1. 실행 전 백업 — 영향을 받는 테이블에 대해 pg_dump를 수행
  2. 기능 플래그 — 애플리케이션에서 기존/새로운 스키마 읽기 모드를 전환할 수 있음
  3. 섀도 테이블 — 마이그레이션 기간 동안 원본 테이블의 복사본을 유지

마이그레이션 생성기 도구

python scripts/migration_generator.py --change "users 테이블에 email_verified 부울형 열 추가" --dialect postgres --format sql
python scripts/migration_generator.py --change "customers 테이블의 name 열을 full_name으로 이름 변경" --dialect mysql --format alembic --json

다중 데이터베이스 지원

방언 차이

기능 PostgreSQL MySQL SQLite SQL Server
UPSERT 충돌 시 업데이트 중복 키 시 업데이트 ON CONFLICT DO UPDATE 병합
부울 네이티브 BOOLEAN TINYINT(1) INTEGER BIT
자동 증가 SERIAL / GENERATED AUTO_INCREMENT 정수 기본 키 IDENTITY
JSON JSONB (인덱싱됨) JSON 텍스트 (ext) NVARCHAR(MAX)
배열 네이티브 ARRAY 지원되지 않음 지원되지 않음 지원되지 않음
CTE (재귀형) 완전 지원 8.0 이상 3.8.3 이상 완전 지원
윈도우 함수 완전 지원 8.0 이상 3.25.0 이상 완전 지원
전체 텍스트 검색 tsvector + GIN FULLTEXT 인덱스 FTS5 확장 전체 텍스트 카탈로그
LIMIT/OFFSET LIMIT n OFFSET m LIMIT n OFFSET m LIMIT n OFFSET m OFFSET m ROWS FETCH NEXT n ROWS ONLY

호환성 관련 팁

  • 항상 매개변수화된 쿼리를 사용하십시오 — 모든 방언에서 SQL 인젝션을 방지합니다
  • 공유 코드에서는 방언별 함수 사용을 피하십시오 — 어댑터 계층으로 감싸십시오
  • 대상 엔진에서 마이그레이션을 테스트하십시오information_schema는 엔진마다 다릅니다
  • ISO 날짜 형식을 사용하십시오'YYYY-MM-DD'는 모든 곳에서 작동합니다
  • 식별자는 따옴표로 묶으세요 — 큰따옴표(SQL 표준) 또는 백틱(MySQL)을 사용하세요

ORM 패턴

Prisma

스키마 정의

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
}

model Post {
  id       Int    @id @default(autoincrement())
  title    String
  author   User   @relation(fields: [authorId], references: [id])
  authorId Int
}

마이그레이션: npx prisma migrate dev --name add_user_email 쿼리 API: prisma.user.findMany({ where: { email: { contains: '@' } }, include: { posts: true } }) 원시 SQL 이스케이프 해치: prisma.$queryRaw\SELECT * FROM users WHERE id = ${userId}``

Drizzle

스키마 우선 정의

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  name: text('name'),
  createdAt: timestamp('created_at').defaultNow(),
});

쿼리 빌더: db.select().from(users).where(eq(users.email, email)) 마이그레이션: npx drizzle-kit generate:pg, 그 다음 npx drizzle-kit push:pg

TypeORM

엔티티 데코레이터

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ unique: true })
  email: string;

  @OneToMany(() => Post, post => post.author)
  posts: Post[];
}

리포지토리 패턴: userRepo.find({ where: { email }, relations: ['posts'] }) 마이그레이션: npx typeorm migration:generate -n AddUserEmail

SQLAlchemy

선언적 모델

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    email = Column(String(255), unique=True, nullable=False)
    name = Column(String(255))
    posts = relationship('Post', back_populates='author')

세션 관리: 항상 with Session()을 세션 컨텍스트 매니저 사용 Alembic 마이그레이션: alembic revision --autogenerate -m "add user email"

ORM별 나란히 비교 및 마이그레이션 워크플로는 references/orm_patterns.md를 참조하십시오.

데이터 무결성

제약 조건 전략

  • 주 키 — 모든 테이블에는 반드시 하나씩 있어야 하며, 대리 키(serial/UUID)를 우선적으로 사용하십시오
  • 외래 키 — 참조 무결성을 강제 적용하고, ON DELETE 동작을 명시적으로 정의하십시오
  • UNIQUE 제약 조건 — 비즈니스 수준의 고유성 확보를 위해 사용(이메일, 슬러그, API 키)
  • CHECK 제약 조건 — DB 수준에서 범위, 열거형 및 비즈니스 규칙을 검증합니다
  • NOT NULL — 기본적으로 NOT NULL로 설정하고, 진정으로 선택 사항인 경우에만 NULL 허용

트랜잭션 격리 수준

수준 더티 읽기 비반복 가능 읽기 팬텀 읽기 사용 사례
읽기 미확정 절대 권장하지 않음
커밋된 읽기 아니요 PostgreSQL의 기본값, 일반 OLTP
반복 가능 읽기 아니요 아니요 예 (InnoDB: 아니요) 금융 계산
SERIALIZABLE 아니요 아니요 아니요 중요 일관성 (청구, 재고)

교착 상태 방지

  1. 일관된 잠금 순서 — 항상 동일한 테이블/행 순서로 잠금을 획득
  2. 짧은 트랜잭션 — 첫 번째 잠금 획득부터 커밋까지의 시간을 최소화
  3. 권고용 잠금 — 애플리케이션 수준 조정을 위해 pg_advisory_lock() 사용
  4. 재시도 로직 — 교착 상태 오류를 감지하고 지수적 백오프를 적용하여 재시도

백업 및 복원

PostgreSQL

# 전체 백업
pg_dump -Fc --no-owner dbname > backup.dump
# 복원
pg_restore -d dbname --clean --no-owner backup.dump
# 특정 시점 복구: WAL 아카이빙 + restore_command 구성

MySQL

# 전체 백업
mysqldump --single-transaction --routines --triggers dbname > backup.sql
# 복원
mysql dbname < backup.sql
# PITR용 바이너리 로그: mysqlbinlog --start-datetime="2025-01-01 00:00:00" binlog.000001

SQLite

# 백업 (동시 읽기 시에도 안전)
sqlite3 dbname ".backup backup.db"

백업 모범 사례

  • 자동화 — cron 또는 systemd 타이머를 사용하며, 절대 수동으로만 수행하지 마십시오
  • 복원 테스트 — 테스트되지 않은 백업은 백업이 아닙니다
  • 오프사이트 사본 — S3, GCS 또는 별도의 리전
  • 보존 정책 — 매일 7일, 매주 4주, 매월 12개월
  • 백업 용량 및 소요 시간 모니터링 — 갑작스러운 변화는 문제의 신호

반대 사례

반패턴 문제 해결 방법
SELECT * 불필요한 데이터를 전송하며, 스키마 변경 시 오류가 발생합니다 명시적 열 목록
외래 키(FK) 열에 인덱스가 없음 JOIN 및 캐스케이딩 삭제 성능 저하 모든 외래 키에 인덱스 추가
N+1 쿼리 데이터베이스와의 1 + N회 왕복 통신 이저 로딩 또는 일괄 쿼리
암시적 형 변환 WHERE id = '123'은 인덱스 사용을 방해합니다 술어 내의 데이터형 일치
연결 풀링 없음 부하 시 연결 소진 PgBouncer, ProxySQL 또는 ORM 풀
제한 없는 쿼리 LIMIT가 없으면 수백만 개의 행이 반환될 위험이 있음 항상 페이지 분할을 적용하십시오
금액을 FLOAT 형식으로 저장 반올림 오류 DECIMAL(19,4) 또는 정수 단위의 센트 사용
'God 테이블' 50개 이상의 열을 가진 단일 테이블 정규화하거나 수직 분할을 사용하십시오
모든 곳에서 소프트 삭제 적용 WHERE deleted_at IS NULL 조건으로 인해 모든 쿼리가 복잡해짐 테이블 아카이빙 또는 이벤트 소싱
원시 문자열 연결 SQL 인젝션 항상 매개변수화된 쿼리 사용

상호 참조

기술 관계
데이터베이스 설계자 스키마 아키텍처, 정규화 분석, ERD 생성
데이터베이스 스키마 설계자 시각적 ERD 모델링, 관계 매핑
마이그레이션 아키텍트 복잡한 다단계 마이그레이션 오케스트레이션
API 설계 검토자 API 엔드포인트가 쿼리 패턴과 일치하도록 보장
가시성 플랫폼 쿼리 성능 모니터링, 느린 쿼리 알림
GitHub에서 보기
---
name: sql-database-assistant
description: Translate natural language into SQL queries, optimize database performance, generate migrations, explore schemas, and work with ORMs across PostgreSQL, MySQL, SQLite, and SQL Server.
---

# SQL Database Assistant - POWERFUL Tier Skill

## Overview

The operational companion to database design. While **database-designer** focuses on schema architecture and **database-schema-designer** handles ERD modeling, this skill covers the day-to-day: writing queries, optimizing performance, generating migrations, and bridging the gap between application code and database engines.

### Core Capabilities

- **Natural Language to SQL** — translate requirements into correct, performant queries
- **Schema Exploration** — introspect live databases across PostgreSQL, MySQL, SQLite, SQL Server
- **Query Optimization** — EXPLAIN analysis, index recommendations, N+1 detection, rewrite patterns
- **Migration Generation** — up/down scripts, zero-downtime strategies, rollback plans
- **ORM Integration** — Prisma, Drizzle, TypeORM, SQLAlchemy patterns and escape hatches
- **Multi-Database Support** — dialect-aware SQL with compatibility guidance

### Tools

| Script | Purpose |
|--------|---------|
| `scripts/query_optimizer.py` | Static analysis of SQL queries for performance issues |
| `scripts/migration_generator.py` | Generate migration file templates from change descriptions |
| `scripts/schema_explorer.py` | Generate schema documentation from introspection queries |

---

## Natural Language to SQL

### Translation Patterns

When converting requirements to SQL, follow this sequence:

1. **Identify entities** — map nouns to tables
2. **Identify relationships** — map verbs to JOINs or subqueries
3. **Identify filters** — map adjectives/conditions to WHERE clauses
4. **Identify aggregations** — map "total", "average", "count" to GROUP BY
5. **Identify ordering** — map "top", "latest", "highest" to ORDER BY + LIMIT

### Common Query Templates

**Top-N per group (window function)**
```sql
SELECT * FROM (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rn
  FROM employees
) ranked WHERE rn <= 3;
```

**Running totals**
```sql
SELECT date, amount,
  SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM transactions;
```

**Gap detection**
```sql
SELECT curr.id, curr.seq_num, prev.seq_num AS prev_seq
FROM records curr
LEFT JOIN records prev ON prev.seq_num = curr.seq_num - 1
WHERE prev.id IS NULL AND curr.seq_num > 1;
```

**UPSERT (PostgreSQL)**
```sql
INSERT INTO settings (key, value, updated_at)
VALUES ('theme', 'dark', NOW())
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = EXCLUDED.updated_at;
```

**UPSERT (MySQL)**
```sql
INSERT INTO settings (key_name, value, updated_at)
VALUES ('theme', 'dark', NOW())
ON DUPLICATE KEY UPDATE value = VALUES(value), updated_at = VALUES(updated_at);
```

> See references/query_patterns.md for JOINs, CTEs, window functions, JSON operations, and more.

---

## Schema Exploration

### Introspection Queries

**PostgreSQL — list tables and columns**
```sql
SELECT table_name, column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public'
ORDER BY table_name, ordinal_position;
```

**PostgreSQL — foreign keys**
```sql
SELECT tc.table_name, kcu.column_name,
  ccu.table_name AS foreign_table, ccu.column_name AS foreign_column
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY';
```

**MySQL — table sizes**
```sql
SELECT table_name, table_rows,
  ROUND(data_length / 1024 / 1024, 2) AS data_mb,
  ROUND(index_length / 1024 / 1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY data_length DESC;
```

**SQLite — schema dump**
```sql
SELECT name, sql FROM sqlite_master WHERE type = 'table' ORDER BY name;
```

**SQL Server — columns with types**
```sql
SELECT t.name AS table_name, c.name AS column_name,
  ty.name AS data_type, c.max_length, c.is_nullable
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
ORDER BY t.name, c.column_id;
```

### Generating Documentation from Schema

Use `scripts/schema_explorer.py` to produce markdown or JSON documentation:

```bash
python scripts/schema_explorer.py --dialect postgres --tables all --format md
python scripts/schema_explorer.py --dialect mysql --tables users,orders --format json --json
```

---

## Query Optimization

### EXPLAIN Analysis Workflow

1. **Run EXPLAIN ANALYZE** (PostgreSQL) or **EXPLAIN FORMAT=JSON** (MySQL)
2. **Identify the costliest node** — Seq Scan on large tables, Nested Loop with high row estimates
3. **Check for missing indexes** — sequential scans on filtered columns
4. **Look for estimation errors** — planned vs actual rows divergence signals stale statistics
5. **Evaluate JOIN order** — ensure the smallest result set drives the join

### Index Recommendation Checklist

- Columns in WHERE clauses with high selectivity
- Columns in JOIN conditions (foreign keys)
- Columns in ORDER BY when combined with LIMIT
- Composite indexes matching multi-column WHERE predicates (most selective column first)
- Partial indexes for queries with constant filters (e.g., `WHERE status = 'active'`)
- Covering indexes to avoid table lookups for read-heavy queries

### Query Rewriting Patterns

| Anti-Pattern | Rewrite |
|-------------|---------|
| `SELECT * FROM orders` | `SELECT id, status, total FROM orders` (explicit columns) |
| `WHERE YEAR(created_at) = 2025` | `WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01'` (sargable) |
| Correlated subquery in SELECT | LEFT JOIN with aggregation |
| `NOT IN (SELECT ...)` with NULLs | `NOT EXISTS (SELECT 1 ...)` |
| `UNION` (dedup) when not needed | `UNION ALL` |
| `LIKE '%search%'` | Full-text search index (GIN/FULLTEXT) |
| `ORDER BY RAND()` | Application-side random sampling or `TABLESAMPLE` |

### N+1 Detection

**Symptoms:**
- Application loop that executes one query per parent row
- ORM lazy-loading related entities inside a loop
- Query log shows hundreds of identical SELECT patterns with different IDs

**Fixes:**
- Use eager loading (`include` in Prisma, `joinedload` in SQLAlchemy)
- Batch queries with `WHERE id IN (...)`
- Use DataLoader pattern for GraphQL resolvers

### Static Analysis Tool

```bash
python scripts/query_optimizer.py --query "SELECT * FROM orders WHERE status = 'pending'" --dialect postgres
python scripts/query_optimizer.py --query queries.sql --dialect mysql --json
```

> See references/optimization_guide.md for EXPLAIN plan reading, index types, and connection pooling.

---

## Migration Generation

### Zero-Downtime Migration Patterns

**Adding a column (safe)**
```sql
-- Up
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- Down
ALTER TABLE users DROP COLUMN phone;
```

**Renaming a column (expand-contract)**
```sql
-- Step 1: Add new column
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
-- Step 2: Backfill
UPDATE users SET full_name = name;
-- Step 3: Deploy app reading both columns
-- Step 4: Deploy app writing only new column
-- Step 5: Drop old column
ALTER TABLE users DROP COLUMN name;
```

**Adding a NOT NULL column (safe sequence)**
```sql
-- Step 1: Add nullable
ALTER TABLE orders ADD COLUMN region VARCHAR(50);
-- Step 2: Backfill with default
UPDATE orders SET region = 'unknown' WHERE region IS NULL;
-- Step 3: Add constraint
ALTER TABLE orders ALTER COLUMN region SET NOT NULL;
ALTER TABLE orders ALTER COLUMN region SET DEFAULT 'unknown';
```

**Index creation (non-blocking, PostgreSQL)**
```sql
CREATE INDEX CONCURRENTLY idx_orders_status ON orders (status);
```

### Data Backfill Strategies

- **Batch updates** — process in chunks of 1000-10000 rows to avoid lock contention
- **Background jobs** — run backfills asynchronously with progress tracking
- **Dual-write** — write to old and new columns during transition period
- **Validation queries** — verify row counts and data integrity after each batch

### Rollback Strategies

Every migration must have a reversible down script. For irreversible changes:

1. **Backup before execution** — `pg_dump` the affected tables
2. **Feature flags** — application can switch between old/new schema reads
3. **Shadow tables** — keep a copy of the original table during migration window

### Migration Generator Tool

```bash
python scripts/migration_generator.py --change "add email_verified boolean to users" --dialect postgres --format sql
python scripts/migration_generator.py --change "rename column name to full_name in customers" --dialect mysql --format alembic --json
```

---

## Multi-Database Support

### Dialect Differences

| Feature | PostgreSQL | MySQL | SQLite | SQL Server |
|---------|-----------|-------|--------|------------|
| UPSERT | `ON CONFLICT DO UPDATE` | `ON DUPLICATE KEY UPDATE` | `ON CONFLICT DO UPDATE` | `MERGE` |
| Boolean | Native `BOOLEAN` | `TINYINT(1)` | `INTEGER` | `BIT` |
| Auto-increment | `SERIAL` / `GENERATED` | `AUTO_INCREMENT` | `INTEGER PRIMARY KEY` | `IDENTITY` |
| JSON | `JSONB` (indexed) | `JSON` | Text (ext) | `NVARCHAR(MAX)` |
| Array | Native `ARRAY` | Not supported | Not supported | Not supported |
| CTE (recursive) | Full support | 8.0+ | 3.8.3+ | Full support |
| Window functions | Full support | 8.0+ | 3.25.0+ | Full support |
| Full-text search | `tsvector` + GIN | `FULLTEXT` index | FTS5 extension | Full-text catalog |
| LIMIT/OFFSET | `LIMIT n OFFSET m` | `LIMIT n OFFSET m` | `LIMIT n OFFSET m` | `OFFSET m ROWS FETCH NEXT n ROWS ONLY` |

### Compatibility Tips

- **Always use parameterized queries** — prevents SQL injection across all dialects
- **Avoid dialect-specific functions in shared code** — wrap in adapter layer
- **Test migrations on target engine** — `information_schema` varies between engines
- **Use ISO date format** — `'YYYY-MM-DD'` works everywhere
- **Quote identifiers** — use double quotes (SQL standard) or backticks (MySQL)

---

## ORM Patterns

### Prisma

**Schema definition**
```prisma
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
}

model Post {
  id       Int    @id @default(autoincrement())
  title    String
  author   User   @relation(fields: [authorId], references: [id])
  authorId Int
}
```

**Migrations**: `npx prisma migrate dev --name add_user_email`
**Query API**: `prisma.user.findMany({ where: { email: { contains: '@' } }, include: { posts: true } })`
**Raw SQL escape hatch**: `prisma.$queryRaw\`SELECT * FROM users WHERE id = ${userId}\``

### Drizzle

**Schema-first definition**
```typescript
export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  name: text('name'),
  createdAt: timestamp('created_at').defaultNow(),
});
```

**Query builder**: `db.select().from(users).where(eq(users.email, email))`
**Migrations**: `npx drizzle-kit generate:pg` then `npx drizzle-kit push:pg`

### TypeORM

**Entity decorators**
```typescript
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ unique: true })
  email: string;

  @OneToMany(() => Post, post => post.author)
  posts: Post[];
}
```

**Repository pattern**: `userRepo.find({ where: { email }, relations: ['posts'] })`
**Migrations**: `npx typeorm migration:generate -n AddUserEmail`

### SQLAlchemy

**Declarative models**
```python
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    email = Column(String(255), unique=True, nullable=False)
    name = Column(String(255))
    posts = relationship('Post', back_populates='author')
```

**Session management**: Always use `with Session() as session:` context manager
**Alembic migrations**: `alembic revision --autogenerate -m "add user email"`

> See references/orm_patterns.md for side-by-side comparisons and migration workflows per ORM.

---

## Data Integrity

### Constraint Strategy

- **Primary keys** — every table must have one; prefer surrogate keys (serial/UUID)
- **Foreign keys** — enforce referential integrity; define ON DELETE behavior explicitly
- **UNIQUE constraints** — for business-level uniqueness (email, slug, API key)
- **CHECK constraints** — validate ranges, enums, and business rules at the DB level
- **NOT NULL** — default to NOT NULL; make nullable only when genuinely optional

### Transaction Isolation Levels

| Level | Dirty Read | Non-Repeatable Read | Phantom Read | Use Case |
|-------|-----------|-------------------|-------------|----------|
| READ UNCOMMITTED | Yes | Yes | Yes | Never recommended |
| READ COMMITTED | No | Yes | Yes | Default for PostgreSQL, general OLTP |
| REPEATABLE READ | No | No | Yes (InnoDB: No) | Financial calculations |
| SERIALIZABLE | No | No | No | Critical consistency (billing, inventory) |

### Deadlock Prevention

1. **Consistent lock ordering** — always acquire locks in the same table/row order
2. **Short transactions** — minimize time between first lock and commit
3. **Advisory locks** — use `pg_advisory_lock()` for application-level coordination
4. **Retry logic** — catch deadlock errors and retry with exponential backoff

---

## Backup & Restore

### PostgreSQL
```bash
# Full backup
pg_dump -Fc --no-owner dbname > backup.dump
# Restore
pg_restore -d dbname --clean --no-owner backup.dump
# Point-in-time recovery: configure WAL archiving + restore_command
```

### MySQL
```bash
# Full backup
mysqldump --single-transaction --routines --triggers dbname > backup.sql
# Restore
mysql dbname < backup.sql
# Binary log for PITR: mysqlbinlog --start-datetime="2025-01-01 00:00:00" binlog.000001
```

### SQLite
```bash
# Backup (safe with concurrent reads)
sqlite3 dbname ".backup backup.db"
```

### Backup Best Practices
- **Automate** — cron or systemd timer, never manual-only
- **Test restores** — untested backups are not backups
- **Offsite copies** — S3, GCS, or separate region
- **Retention policy** — daily for 7 days, weekly for 4 weeks, monthly for 12 months
- **Monitor backup size and duration** — sudden changes signal issues

---

## Anti-Patterns

| Anti-Pattern | Problem | Fix |
|-------------|---------|-----|
| `SELECT *` | Transfers unnecessary data, breaks on schema changes | Explicit column list |
| Missing indexes on FK columns | Slow JOINs and cascading deletes | Add indexes on all foreign keys |
| N+1 queries | 1 + N round trips to database | Eager loading or batch queries |
| Implicit type coercion | `WHERE id = '123'` prevents index use | Match types in predicates |
| No connection pooling | Exhausts connections under load | PgBouncer, ProxySQL, or ORM pool |
| Unbounded queries | No LIMIT risks returning millions of rows | Always paginate |
| Storing money as FLOAT | Rounding errors | Use `DECIMAL(19,4)` or integer cents |
| God tables | One table with 50+ columns | Normalize or use vertical partitioning |
| Soft deletes everywhere | Complicates every query with `WHERE deleted_at IS NULL` | Archive tables or event sourcing |
| Raw string concatenation | SQL injection | Parameterized queries always |

---

## Cross-References

| Skill | Relationship |
|-------|-------------|
| **database-designer** | Schema architecture, normalization analysis, ERD generation |
| **database-schema-designer** | Visual ERD modeling, relationship mapping |
| **migration-architect** | Complex multi-step migration orchestration |
| **api-design-reviewer** | Ensuring API endpoints align with query patterns |
| **observability-platform** | Query performance monitoring, slow query alerts |

모든 파일

0개 파일

sql-database-assistant 설치

스킬 파일을 다운로드하여 .claude/skills/ 디렉터리에 압축을 풀어주세요.

ZIP 다운로드

저장소를 클론하고 스킬 파일을 프로젝트에 복사하세요.

git clone https://github.com/alirezarezvani/claude-skills/tree/main/engineering/skills/sql-database-assistant # Copy SKILL.md to your .claude/skills/ directory

복사 복사
빠른 설정: 스킬 폴더를 .claude/skills/로 복사하세요. Claude가 해당 스킬을 자동으로 감지하여 사용할 것입니다.

관련 스킬

microservices-patterns
업데이트 된 시간 2026년 6월 29일
jpa-patterns
업데이트 된 시간 2026년 6월 30일
fabric-lakehouse
업데이트 된 시간 2026년 6월 30일
prisma-expert
업데이트 된 시간 2026년 6월 29일
OR