erd.sketch

SQL 结构检查:哪里有问题

下面不是编出来的清单,而是左边这份结构的真实检查报告。编辑器的「检查」标签里看到的是同样的结果,其中不少还能一键修复。

输入schema.sql
CREATE TABLE user (
    id          BIGSERIAL PRIMARY KEY,
    email       VARCHAR(255) NOT NULL,
    balance     FLOAT,
    created_at  TIMESTAMP NOT NULL
);

CREATE TABLE orders (
    order_id    INTEGER NOT NULL,
    user_id     INTEGER REFERENCES user (id),
    total       FLOAT NOT NULL,
    placedAt    TIMESTAMP
);
判断为
PostgreSQL
错误 1警告 8提示 2
打开编辑器

脚本留在浏览器里:解析和生成都在您的机器上进行,不会上传到服务器。

发现了什么

  • 关系两端类型不同

    orders.user_id

    列与它引用的目标声明方式不同。有的数据库直接拒绝建键,其余则在每次连接时静默转换类型。

  • 表没有主键

    orders

    这样的行无法可靠定位:既不能只更新两条相同行中的一条,也无法被别处引用。复制和 ORM 也都假定存在主键。

  • 外键没有索引

    orders

    连接查询和删除父行时的检查都要走这一列。没有索引,每一次都是全表扫描。

  • 外键允许为 NULL

    orders.user_id

    有时这是有意的:关系可选。但更常见的是忘了写 NOT NULL,于是出现没有父行的数据。

  • 名字是保留字

    user

    每条查询里都得给它加引号。改名比记住这个例外更省事。

  • 金额用了近似类型

    user.balanceorders.total

    FLOAT 和 REAL 只能近似存储数值:第一次除法之后金额就对不上了。金额应当用 NUMERIC 或 DECIMAL。

  • TIMESTAMP 没有时区

    user.created_atorders.placedAt

    在 PostgreSQL 里这种列不保存时区,会被默默当作本地时间。服务器迁移或夏令时切换后数值就会错位——通常需要的是 timestamptz。

  • 命名风格不统一

    orders.placedAt

    模式里 snake_case 和 camelCase 并存。这里列出的是少数派风格的名字:统一它们比记住例外更省心。

  • 没有写 ON DELETE

    orders

    删除父行时子行怎么办交由数据库决定,默认情况下它会拒绝删除。如果这不是本意,最好明确写出来。