数据库设计

Mattakushi4322026-10-25查看 1409复制 674

何时激活:模式设计、规范化、ERD、软删除、时态表、审计日志、数据库建模

Download (.md)

📘如何使用这个技能

  1. 1
    复制技能内容·点击上方「Copy SKILL.md」复制全部内容,或 Download 下载到本地
  2. 2
    打开你的 AI 客户端·支持 Claude Code、Trae、Cursor、ChatGPT、DeepSeek、Kimi 等任意 AI 助手
  3. 3
    粘贴为系统提示/知识文件·Claude Code:放入 .claude/skills/ 目录;普通对话:作为首条消息发送并说「请严格按这份技能文档执行」
  4. 4
    直接提出你的需求·AI 会按技能定义的工作流、格式和约束来处理,无需再解释背景

💡 提示:技能生效后,每次对话只需一句需求,AI 会按 SKILL.md 的流程执行。

SKILL.md4380 chars
---
name: database-design
description: "When to activate: schema design, normalization, ERD, soft delete, temporal tables, audit log, database modeling"
origin: zakr
metadata:
  namespace: zakr/devtools-pack
  version: 1.0.0
  category: databases
  language: sql
  platforms: [claude-code, codex, gemini-cli, cursor, aider, windsurf]
---
# Database Design Patterns

## Normalization vs Denormalization

```sql
-- 3NF: separate concerns, eliminate redundancy
CREATE TABLE users    (id BIGSERIAL PRIMARY KEY, name TEXT, email TEXT UNIQUE);
CREATE TABLE products (id BIGSERIAL PRIMARY KEY, name TEXT, price DECIMAL(10,2));
CREATE TABLE orders   (id BIGSERIAL PRIMARY KEY, user_id BIGINT REFERENCES users, created_at TIMESTAMPTZ);
CREATE TABLE order_items (order_id BIGINT REFERENCES orders, product_id BIGINT REFERENCES products,
                          qty INT, unit_price DECIMAL(10,2));

-- Denormalize for read performance (snapshot price at order time — already above)
-- Materialized: store user_name on orders for fast listing without JOIN
ALTER TABLE orders ADD COLUMN user_name TEXT;  -- updated via trigger or app
```

## Soft Delete

```sql
-- Pattern 1: deleted_at timestamp
ALTER TABLE users ADD COLUMN deleted_at TIMESTAMPTZ;
CREATE INDEX idx_users_active ON users (id) WHERE deleted_at IS NULL;

-- All queries must include WHERE deleted_at IS NULL
-- Use a view to enforce
CREATE VIEW active_users AS SELECT * FROM users WHERE deleted_at IS NULL;

-- Soft delete
UPDATE users SET deleted_at = NOW() WHERE id = $1;

-- Pattern 2: is_deleted boolean (simpler, less info)
ALTER TABLE users ADD COLUMN is_deleted BOOLEAN NOT NULL DEFAULT FALSE;
```

## Temporal Tables (Audit History)

```sql
-- System-period temporal table (valid time via trigger)
CREATE TABLE products (
  id         BIGSERIAL PRIMARY KEY,
  name       TEXT NOT NULL,
  price      DECIMAL(10,2),
  valid_from TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  valid_to   TIMESTAMPTZ NOT NULL DEFAULT 'infinity'
);

CREATE TABLE products_history (LIKE products INCLUDING ALL);

-- Trigger to move old version to history on update
CREATE OR REPLACE FUNCTION products_history_trigger() RETURNS trigger AS $$
BEGIN
  INSERT INTO products_history SELECT OLD.*;
  NEW.valid_from := NOW();
  RETURN NEW;
END $$ LANGUAGE plpgsql;

CREATE TRIGGER products_versioning
BEFORE UPDATE ON products
FOR EACH ROW EXECUTE FUNCTION products_history_trigger();

-- Query at a point in time
SELECT * FROM products_history
WHERE id = 42 AND valid_from <= '2024-06-01' AND valid_to > '2024-06-01';
```

## Audit Log Pattern

```sql
CREATE TABLE audit_log (
  id          BIGSERIAL PRIMARY KEY,
  table_name  TEXT NOT NULL,
  record_id   BIGINT NOT NULL,
  action      TEXT NOT NULL CHECK (action IN ('INSERT','UPDATE','DELETE')),
  old_data    JSONB,
  new_data    JSONB,
  changed_by  BIGINT,  -- user id
  changed_at  TIMESTAMPTZ DEFAULT NOW()
);

CREATE OR REPLACE FUNCTION audit_trigger() RETURNS trigger AS $$
BEGIN
  INSERT INTO audit_log (table_name, record_id, action, old_data, new_data)
  VALUES (TG_TABLE_NAME,
          COALESCE(NEW.id, OLD.id),
          TG_OP,
          CASE WHEN TG_OP != 'INSERT' THEN to_jsonb(OLD) END,
          CASE WHEN TG_OP != 'DELETE' THEN to_jsonb(NEW) END);
  RETURN NEW;
END $$ LANGUAGE plpgsql;
```

## Common Schema Patterns

```sql
-- UUID primary keys (globally unique, safe for distributed systems)
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE TABLE events (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  ...
);

-- Polymorphic association (with type discriminator)
CREATE TABLE attachments (
  id            BIGSERIAL PRIMARY KEY,
  resource_type TEXT NOT NULL,   -- 'Post', 'Comment', 'User'
  resource_id   BIGINT NOT NULL,
  url           TEXT NOT NULL,
  UNIQUE (resource_type, resource_id, url)
);
CREATE INDEX ON attachments (resource_type, resource_id);

-- ULID-style sortable IDs (time-ordered UUIDs)
-- Use pg_ulid extension or encode timestamp into UUID v7
```

## Design Checklist

- [ ] Every table has `id`, `created_at`, `updated_at`
- [ ] Foreign keys have indexes
- [ ] Soft delete uses partial index on `deleted_at IS NULL`
- [ ] Enums as `CHECK` constraints or lookup tables, not magic strings
- [ ] Monetary values as `DECIMAL(19,4)`, never `FLOAT`
- [ ] Time stored as `TIMESTAMPTZ` (UTC), not `TIMESTAMP`
- [ ] Audit log on all business-critical tables
来源:skillsdirectory

评论0

登录后参与评论

还没有评论,来抢沙发吧