未记录事实核验 · 状态:维护中

记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。

warning 适用边界与维护说明

本文适合作为表结构设计模式与工程评审思路的参考,不是可直接部署的完整项目;各章节 SQL 为独立示例,同名表存在不同定义,不能整篇连续执行。主体采用 MySQL 风格,CHECK 约束的实际执行以 MySQL 8.0.16 起版本为前提;PostgreSQL 索引及行级安全片段须另配匹配的语法、表结构与运行环境。覆盖索引仍引用正文 orders 未定义的 status、item_count,不能直接用于现有表结构。多租户建表仅提供租户标识与索引,表内哈希分区不等同于分库分片或访问权限隔离。日统计触发器仅演示新增订单的局部汇总,不处理金额修改、删除、退款,也不维护客户数及分类统计;数据质量视图应搭配企业案例中的客户与订单表定义使用。此次仅作全文结构审阅,未进行 SQL 执行测试或外部事实核验。

表结构设计原则

数据库表结构设计是数据建模的核心环节,直接影响系统的性能、可维护性和数据完整性。本文从设计理论到企业实践,全面解析表结构设计的核心原则与方法论。

理论基础与设计原则

数据建模理论

概念模型(Conceptual Model)

  • 实体关系模型(ER Model):描述业务实体及其关系
  • 领域建模:基于业务领域的概念抽象
  • 数据字典定义:标准化的元数据管理

逻辑模型(Logical Model)

  • 关系模型:基于关系代数的数据组织方式
  • 对象关系模型:结合面向对象特性的数据模型
  • 文档模型:适用于半结构化数据的建模方式

物理模型(Physical Model)

  • 存储引擎适配:针对具体数据库引擎的优化
  • 分区策略:数据在物理存储层面的组织
  • 索引设计:查询性能的物理优化手段

核心设计原则

1. 数据完整性原则

实体完整性(Entity Integrity)

-- 主键约束确保实体唯一性
CREATE TABLE customers (
    customer_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    customer_uuid CHAR(36) NOT NULL UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

参照完整性(Referential Integrity)

-- 外键约束维护数据关系
CREATE TABLE orders (
    order_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    customer_id BIGINT NOT NULL,
    order_status ENUM('pending', 'confirmed', 'shipped', 'delivered', 'cancelled'),
    total_amount DECIMAL(12,2) NOT NULL DEFAULT 0.00,
    
    CONSTRAINT fk_orders_customer 
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
    ON DELETE RESTRICT ON UPDATE CASCADE,
    
    CONSTRAINT chk_amount CHECK (total_amount >= 0)
);

域完整性(Domain Integrity)

-- 数据类型和约束确保字段值的有效性
CREATE TABLE products (
    product_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    sku VARCHAR(64) NOT NULL UNIQUE,
    product_name VARCHAR(255) NOT NULL,
    category_id INT NOT NULL,
    price DECIMAL(10,2) NOT NULL,
    stock_quantity INT DEFAULT 0,
    is_active BOOLEAN DEFAULT TRUE,
    
    -- 复合约束
    CONSTRAINT chk_price CHECK (price > 0),
    CONSTRAINT chk_stock CHECK (stock_quantity >= 0),
    CONSTRAINT chk_sku_format CHECK (sku REGEXP '^[A-Z]{2}[0-9]{6}$')
);

2. 范式化与反范式化策略

第一范式(1NF):原子性

-- 违反1NF的设计
CREATE TABLE bad_orders (
    order_id INT,
    product_list TEXT  -- 存储多个产品ID,违反原子性
);

-- 符合1NF的设计
CREATE TABLE order_items (
    order_item_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    quantity INT NOT NULL DEFAULT 1,
    unit_price DECIMAL(10,2) NOT NULL
);

第二范式(2NF):消除部分函数依赖

-- 符合2NF的订单明细设计
CREATE TABLE order_line_items (
    order_id BIGINT,
    product_id BIGINT,
    quantity INT NOT NULL,
    unit_price DECIMAL(10,2) NOT NULL,
    line_total DECIMAL(12,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
    
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (order_id) REFERENCES orders(order_id),
    FOREIGN KEY (product_id) REFERENCES products(product_id)
);

第三范式(3NF):消除传递依赖

-- 分离客户信息和地址信息
CREATE TABLE customer_addresses (
    address_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    customer_id BIGINT NOT NULL,
    address_type ENUM('billing', 'shipping', 'both') NOT NULL,
    street_address VARCHAR(255) NOT NULL,
    city VARCHAR(100) NOT NULL,
    state_province VARCHAR(100),
    postal_code VARCHAR(20),
    country_code CHAR(2) NOT NULL DEFAULT 'CN',
    
    UNIQUE KEY uk_customer_address_type (customer_id, address_type),
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

反范式化的应用场景

-- 为了查询性能的反范式化设计
CREATE TABLE order_summary (
    order_id BIGINT PRIMARY KEY,
    customer_id BIGINT NOT NULL,
    customer_name VARCHAR(100) NOT NULL,  -- 冗余字段
    customer_email VARCHAR(100) NOT NULL, -- 冗余字段
    order_date DATE NOT NULL,
    total_amount DECIMAL(12,2) NOT NULL,
    item_count INT NOT NULL,               -- 预计算字段
    
    INDEX idx_customer_date (customer_id, order_date),
    INDEX idx_order_date (order_date)
);

高级设计模式

1. 时间维度建模

历史数据追踪(Slowly Changing Dimensions)

-- SCD Type 2: 保留历史版本
CREATE TABLE customer_history (
    customer_history_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    customer_id BIGINT NOT NULL,
    customer_name VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL,
    phone VARCHAR(20),
    
    -- 时间维度字段
    effective_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    expiry_date DATETIME DEFAULT '9999-12-31 23:59:59',
    is_current BOOLEAN DEFAULT TRUE,
    version_number INT DEFAULT 1,
    
    INDEX idx_customer_effective (customer_id, effective_date),
    INDEX idx_customer_current (customer_id, is_current)
);

审计日志设计

CREATE TABLE audit_log (
    log_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    table_name VARCHAR(64) NOT NULL,
    record_id BIGINT NOT NULL,
    operation_type ENUM('INSERT', 'UPDATE', 'DELETE') NOT NULL,
    old_values JSON,
    new_values JSON,
    changed_by BIGINT NOT NULL,
    changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    INDEX idx_table_record (table_name, record_id),
    INDEX idx_changed_at (changed_at)
);

2. 分区与分片策略

水平分区(Range Partitioning)

-- 按时间分区的订单表
CREATE TABLE orders (
    order_id BIGINT AUTO_INCREMENT,
    customer_id BIGINT NOT NULL,
    order_date DATE NOT NULL,
    total_amount DECIMAL(12,2) NOT NULL,
    
    PRIMARY KEY (order_id, order_date)
) PARTITION BY RANGE (YEAR(order_date)) (
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION p2024 VALUES LESS THAN (2025),
    PARTITION p2025 VALUES LESS THAN (2026),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

哈希分区

-- 按客户ID哈希分区
CREATE TABLE customer_activities (
    activity_id BIGINT AUTO_INCREMENT,
    customer_id BIGINT NOT NULL,
    activity_type VARCHAR(50) NOT NULL,
    activity_data JSON,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    PRIMARY KEY (activity_id, customer_id)
) PARTITION BY HASH(customer_id) PARTITIONS 8;

3. 索引设计策略

复合索引优化

-- 基于查询模式的索引设计
CREATE TABLE user_orders (
    order_id BIGINT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    status VARCHAR(20) NOT NULL,
    order_date DATETIME NOT NULL,
    total_amount DECIMAL(12,2),
    
    -- 针对常用查询的复合索引
    INDEX idx_user_status_date (user_id, status, order_date DESC),
    INDEX idx_date_amount (order_date, total_amount),
    
    -- 部分索引(PostgreSQL)
    -- CREATE INDEX idx_active_orders ON user_orders (user_id, order_date) 
    -- WHERE status IN ('pending', 'confirmed');
);

覆盖索引设计

-- 包含所需列的覆盖索引
CREATE INDEX idx_order_summary 
ON orders (customer_id, order_date, status) 
INCLUDE (total_amount, item_count);

企业级设计案例

电商订单系统设计

-- 1. 客户主表
CREATE TABLE customers (
    customer_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    customer_uuid CHAR(36) NOT NULL UNIQUE,
    customer_type ENUM('individual', 'corporate') NOT NULL DEFAULT 'individual',
    email VARCHAR(100) NOT NULL UNIQUE,
    phone VARCHAR(20),
    registration_date DATETIME DEFAULT CURRENT_TIMESTAMP,
    last_login_at DATETIME,
    customer_status ENUM('active', 'inactive', 'suspended') DEFAULT 'active',
    
    -- 业务字段
    credit_limit DECIMAL(12,2) DEFAULT 0,
    total_orders INT DEFAULT 0,
    total_spent DECIMAL(15,2) DEFAULT 0,
    
    -- 审计字段
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    created_by BIGINT,
    updated_by BIGINT,
    
    INDEX idx_email (email),
    INDEX idx_phone (phone),
    INDEX idx_status_type (customer_status, customer_type)
);

-- 2. 订单主表
CREATE TABLE orders (
    order_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_number VARCHAR(32) NOT NULL UNIQUE,
    customer_id BIGINT NOT NULL,
    
    -- 订单状态管理
    order_status ENUM('draft', 'pending', 'confirmed', 'processing', 
                     'shipped', 'delivered', 'cancelled', 'refunded') DEFAULT 'draft',
    payment_status ENUM('unpaid', 'partial', 'paid', 'refunded') DEFAULT 'unpaid',
    fulfillment_status ENUM('unfulfilled', 'partial', 'fulfilled') DEFAULT 'unfulfilled',
    
    -- 金额字段
    subtotal DECIMAL(12,2) NOT NULL DEFAULT 0,
    tax_amount DECIMAL(12,2) NOT NULL DEFAULT 0,
    shipping_amount DECIMAL(12,2) NOT NULL DEFAULT 0,
    discount_amount DECIMAL(12,2) NOT NULL DEFAULT 0,
    total_amount DECIMAL(12,2) NOT NULL DEFAULT 0,
    
    -- 时间字段
    order_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    required_date DATE,
    shipped_date DATETIME,
    delivered_date DATETIME,
    
    -- 地址信息
    billing_address_id BIGINT,
    shipping_address_id BIGINT,
    
    -- 其他业务字段
    currency_code CHAR(3) DEFAULT 'CNY',
    exchange_rate DECIMAL(10,4) DEFAULT 1.0000,
    sales_channel VARCHAR(50) DEFAULT 'website',
    referral_source VARCHAR(100),
    
    -- 审计字段
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    -- 约束
    CONSTRAINT chk_amounts CHECK (
        subtotal >= 0 AND tax_amount >= 0 AND 
        shipping_amount >= 0 AND discount_amount >= 0 AND 
        total_amount >= 0
    ),
    CONSTRAINT chk_dates CHECK (
        (required_date IS NULL OR required_date >= DATE(order_date)) AND
        (shipped_date IS NULL OR shipped_date >= order_date) AND
        (delivered_date IS NULL OR delivered_date >= COALESCE(shipped_date, order_date))
    ),
    
    -- 外键
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id),
    FOREIGN KEY (billing_address_id) REFERENCES customer_addresses(address_id),
    FOREIGN KEY (shipping_address_id) REFERENCES customer_addresses(address_id),
    
    -- 索引
    INDEX idx_customer_date (customer_id, order_date DESC),
    INDEX idx_status_date (order_status, order_date),
    INDEX idx_order_number (order_number),
    INDEX idx_total_amount (total_amount)
);

-- 3. 订单明细表
CREATE TABLE order_items (
    order_item_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    variant_id BIGINT,
    
    -- 商品信息快照
    product_sku VARCHAR(64) NOT NULL,
    product_name VARCHAR(255) NOT NULL,
    variant_name VARCHAR(100),
    
    -- 数量和价格
    quantity INT NOT NULL DEFAULT 1,
    unit_price DECIMAL(10,2) NOT NULL,
    total_price DECIMAL(12,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
    
    -- 折扣信息
    discount_percentage DECIMAL(5,2) DEFAULT 0,
    discount_amount DECIMAL(10,2) DEFAULT 0,
    
    -- 履约信息
    fulfilled_quantity INT DEFAULT 0,
    shipped_quantity INT DEFAULT 0,
    
    FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products(product_id),
    
    CONSTRAINT chk_quantity CHECK (quantity > 0),
    CONSTRAINT chk_price CHECK (unit_price >= 0),
    CONSTRAINT chk_fulfillment CHECK (
        fulfilled_quantity <= quantity AND shipped_quantity <= fulfilled_quantity
    ),
    
    INDEX idx_order_product (order_id, product_id),
    INDEX idx_product (product_id)
);

多租户数据隔离设计

-- 带租户标识的表结构;下方 PostgreSQL 注释仅演示行级安全语法,建表本身不实现访问隔离
CREATE TABLE tenant_data (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    tenant_id BIGINT NOT NULL,
    data_type VARCHAR(50) NOT NULL,
    data_content JSON NOT NULL,
    
    -- 租户隔离索引
    INDEX idx_tenant_type (tenant_id, data_type)
    
    -- 行级安全策略 (PostgreSQL)
    -- ALTER TABLE tenant_data ENABLE ROW LEVEL SECURITY;
    -- CREATE POLICY tenant_policy ON tenant_data
    -- FOR ALL TO application_user
    -- USING (tenant_id = current_setting('app.tenant_id')::BIGINT);
);

-- 按租户ID进行表内哈希分区(不等同于分库分片或访问权限隔离)
CREATE TABLE tenant_orders (
    order_id BIGINT AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    customer_id BIGINT NOT NULL,
    order_data JSON NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    PRIMARY KEY (tenant_id, order_id),
    INDEX idx_order_id (order_id),
    INDEX idx_customer (tenant_id, customer_id),
    INDEX idx_created (tenant_id, created_at)
) PARTITION BY HASH(tenant_id) PARTITIONS 16;

性能优化最佳实践

查询优化导向的设计

-- 基于访问模式的预聚合表
CREATE TABLE daily_order_stats (
    stat_date DATE PRIMARY KEY,
    total_orders INT DEFAULT 0,
    total_revenue DECIMAL(15,2) DEFAULT 0,
    avg_order_value DECIMAL(10,2) DEFAULT 0,
    unique_customers INT DEFAULT 0,
    
    -- 分类统计
    mobile_orders INT DEFAULT 0,
    web_orders INT DEFAULT 0,
    new_customer_orders INT DEFAULT 0,
    
    -- 更新时间戳
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    INDEX idx_date_revenue (stat_date, total_revenue)
);

-- 实时更新触发器
DELIMITER //
CREATE TRIGGER update_daily_stats 
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
    INSERT INTO daily_order_stats (stat_date, total_orders, total_revenue, avg_order_value)
    VALUES (DATE(NEW.order_date), 1, NEW.total_amount, NEW.total_amount)
    ON DUPLICATE KEY UPDATE 
        total_orders = total_orders + 1,
        total_revenue = total_revenue + NEW.total_amount,
        avg_order_value = total_revenue / total_orders,
        last_updated = CURRENT_TIMESTAMP;
END//
DELIMITER ;

数据类型优化

-- 优化的数据类型选择
CREATE TABLE optimized_table (
    -- 使用适当大小的整数类型
    small_id SMALLINT UNSIGNED,  -- 0-65535
    medium_id MEDIUMINT UNSIGNED, -- 0-16777215
    big_id BIGINT UNSIGNED,       -- 大范围ID
    
    -- 固定长度字符串
    country_code CHAR(2) NOT NULL,
    currency_code CHAR(3) NOT NULL,
    
    -- 变长字符串使用合适长度
    email VARCHAR(100) NOT NULL,   -- 而不是VARCHAR(255)
    name VARCHAR(50) NOT NULL,     -- 而不是TEXT
    
    -- 枚举类型替代字符串
    status ENUM('active', 'inactive', 'pending') NOT NULL DEFAULT 'pending',
    
    -- 位字段用于布尔标志
    flags BIT(8) DEFAULT b'00000000',
    
    -- 精确的小数类型
    price DECIMAL(10,2) NOT NULL,  -- 而不是FLOAT
    
    -- 优化的时间类型
    created_date DATE NOT NULL,             -- 只需要日期
    created_timestamp TIMESTAMP NOT NULL,   -- 需要精确时间
    
    INDEX idx_status_date (status, created_date)
);

企业级设计模板

标准审计字段模板

-- 创建可重用的审计字段结构
CREATE TABLE audit_fields_template (
    -- 审计字段标准模板
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    created_by BIGINT NOT NULL COMMENT 'FK to users.user_id',
    updated_by BIGINT COMMENT 'FK to users.user_id',
    
    -- 软删除支持
    is_deleted BOOLEAN DEFAULT FALSE NOT NULL,
    deleted_at TIMESTAMP NULL,
    deleted_by BIGINT NULL COMMENT 'FK to users.user_id',
    
    -- 版本控制
    version_number INT DEFAULT 1 NOT NULL,
    
    -- 数据源标识
    source_system VARCHAR(50) DEFAULT 'application' NOT NULL,
    import_batch_id BIGINT NULL COMMENT 'FK to import_batches.batch_id',
    
    INDEX idx_created_at (created_at),
    INDEX idx_updated_at (updated_at),
    INDEX idx_deleted (is_deleted, deleted_at)
);

配置表设计模式

-- 灵活的系统配置表
CREATE TABLE system_configurations (
    config_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    config_key VARCHAR(100) NOT NULL UNIQUE,
    config_value TEXT NOT NULL,
    config_type ENUM('string', 'number', 'boolean', 'json') NOT NULL DEFAULT 'string',
    
    -- 分组和分类
    config_group VARCHAR(50) NOT NULL DEFAULT 'general',
    config_category VARCHAR(50) NOT NULL DEFAULT 'system',
    
    -- 元数据
    description TEXT,
    default_value TEXT,
    validation_rule JSON COMMENT 'JSON schema for validation',
    
    -- 访问控制
    is_public BOOLEAN DEFAULT FALSE,
    required_permission VARCHAR(100),
    
    -- 环境隔离
    environment ENUM('development', 'staging', 'production') NOT NULL DEFAULT 'production',
    
    -- 标准审计字段
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    INDEX idx_group_category (config_group, config_category),
    INDEX idx_environment (environment),
    INDEX idx_public (is_public)
);

设计验证与维护

数据质量检查

-- 数据完整性检查视图
CREATE VIEW data_quality_report AS
SELECT 
    'customers' AS table_name,
    COUNT(*) AS total_records,
    COUNT(CASE WHEN email IS NULL OR email = '' THEN 1 END) AS missing_email,
    COUNT(CASE WHEN phone IS NULL OR phone = '' THEN 1 END) AS missing_phone,
    COUNT(CASE WHEN customer_status NOT IN ('active', 'inactive', 'suspended') THEN 1 END) AS invalid_status
FROM customers

UNION ALL

SELECT 
    'orders' AS table_name,
    COUNT(*) AS total_records,
    COUNT(CASE WHEN customer_id IS NULL THEN 1 END) AS missing_customer,
    COUNT(CASE WHEN total_amount < 0 THEN 1 END) AS negative_amounts,
    COUNT(CASE WHEN order_status NOT IN ('draft', 'pending', 'confirmed', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded') THEN 1 END) AS invalid_status
FROM orders;

性能监控

-- 创建性能监控表
CREATE TABLE query_performance_log (
    log_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    query_hash VARCHAR(64) NOT NULL,
    query_text TEXT NOT NULL,
    execution_time_ms INT NOT NULL,
    rows_examined BIGINT,
    rows_returned BIGINT,
    index_usage JSON,
    execution_plan JSON,
    database_name VARCHAR(64) NOT NULL,
    executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    INDEX idx_hash_time (query_hash, execution_time_ms),
    INDEX idx_executed_at (executed_at)
);

总结

表结构设计是数据库系统的基石,需要综合考虑业务需求、性能要求、维护成本等多个维度。通过遵循规范化理论、应用合适的设计模式、优化数据类型选择,可以构建出既满足业务需求又具备良好性能的数据库系统。

在企业级应用中,还需要特别关注可扩展性、数据安全性、审计要求等方面,通过标准化的设计模板和持续的质量监控,确保数据库系统能够稳定高效地支撑业务发展。

知识连接

  • 数据类型 - 了解各种数据类型的特性和使用场景
  • 范式理论 - 深入理解数据库范式化设计原则
  • 索引优化 - 掌握索引设计和优化技巧
  • 数据建模 - 学习完整的数据建模方法论
  • 分区与分片 - 了解大规模数据的分区策略

#SQL #数据建模 #数据库设计 #企业级开发