入职第一天:把老板的订单表变成库

入职第一天,老板发来订单导出文件:「明天告诉我昨天卖了多少。」你环顾四周--公司连个数据库都没有。

学 40 min
练 65 min
盘 15 min
共 120 分钟

学 · 40 min

  1. 01什么是数据库 / 表 / 行 / 列:和 Excel 的对应关系

    场景老板丢来一个订单导出文件,你只会用 Excel 打开它。今天起,同样的数据要换一种存放方式。

    对应关系一句话:数据库 = 整个工作簿文件,表 = 一个 sheet,行 = 一条订单记录,列 = 一个字段(id、金额、时间……)。本质区别在约束和访问方式:Excel 的单元格里塞什么都可以,数据库的每一列有类型(金额必须是数字)和可空性(支付时间可以没有),访问统一走 SQL 而不是鼠标点选。

    -- 表在数据库里的样子:列有类型,行是一条条订单
    select id, user_id, status, total_amount, created_at
    from orders
    limit 3;

    易错Excel 里「没填」和「填了 0」长得差不多;数据库里 NULL(没发生)和 0(发生了、值为零)是两个世界,D3 整天都在讲它。

  2. 02Docker 起 PostgreSQL 16,psql 连进去

    场景公司连个数据库都没有,你今天要自己搭一个--但不能往笔记本上裸装软件。

    Docker 一条命令起一个干净的 PostgreSQL 16 容器,删掉重来毫无负担。psql 是 PG 官方命令行客户端,连进去就是你的操作台。装完先做三件事:进容器、开 psql、建库。

    docker run -d --name pg16 -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres:16
    docker exec -it pg16 psql -U postgres
    CREATE DATABASE shop;
    \c shop

    易错容器删了数据就没了。要持久化就加 -v pgdata:/var/lib/postgresql/data 挂个卷;练习期无所谓,崩了重灌反而干净。

  3. 036 个元命令:\l \dt \d \x \timing

    场景进了 psql 就是一块黑屏--先学会「环顾四周」。

    元命令是 psql 客户端的功能,不是 SQL:\l 列出所有库、\dt 列出当前库的表、\d 表名 看表结构(字段、类型、可空、默认值)、\x 把宽结果竖排显示、\timing on 显示每条 SQL 的耗时(W6 优化周全靠它)。

    \dt          -- 库里有哪些表
    \d orders    -- orders 的完整结构
    \timing on   -- 之后每条 SQL 都带耗时

    易错元命令以反斜杠开头、不带分号,只在 psql 里有效。换任何别的客户端(代码里连库)它们都不存在。

  4. 04CREATE TABLE:orders 的字段类型你来定

    场景建表没有标准答案,字段类型是业务判断题:这张表在你眼里是什么。

    每个字段回答两个问题:什么类型、能不能为空。id 用 uuid(导出文件里就是无序编号);金额用 numeric(10,2) 定点小数,不是 float;status 用 int 存状态码;created_at 必填,paid_at 允许 NULL--可空性是业务事实:没支付的单就是没有支付时间。

    CREATE TABLE orders (
      id           uuid PRIMARY KEY DEFAULT gen_random_uuid(),
      user_id      uuid NOT NULL,
      status       integer,
      total_amount numeric(10,2),
      created_at   timestamp NOT NULL,
      paid_at      timestamp
    );

    易错金额用 float / double 会丢精度(二进制存不了 0.1),报表对不上账。钱永远 numeric / decimal。

  5. 05跑 seed.sql 的「D01」段:灌入 5 万行(里面埋了脏数据)

    场景空表什么都查不了,把老板的订单灌进去。

    psql -f 文件 或进去之后 \i 文件 执行整个脚本;今天只跑 §A 段两条语句(先 INSERT 5 万行,再 UPDATE 埋脏数据)。灌完 select count(*) 核对行数,数据里埋着负金额、时间倒挂、空状态--都是本周的练习素材。

    -- 方式一:psql -f 整个文件,或编辑后只留 §A 段
    -- 方式二:psql 里逐条粘贴执行
    select count(*) from orders;   -- 50000

    易错同一段 INSERT 跑两遍 = 数据翻倍(或主键冲突)。发现数字不对劲,先怀疑自己重复灌过。

练 · 65 min

  1. Docker 起 pg16,建库 shop 并连进去
    参考答案

    psql 的元命令以反斜杠开头,只有 psql 认;CREATE DATABASE 是 SQL。

    docker run -d --name pg16 -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres:16
    docker exec -it pg16 psql -U postgres
    CREATE DATABASE shop;
    \c shop
  2. 建 orders 表:字段类型自己定,定完对照本页下方的结构检查
    参考答案

    status 可空(业务上就是有脏数据)、paid_at 可空(没支付的单没有支付时间)--可空性是业务事实,别全写 NOT NULL。

    CREATE TABLE orders (
      id           uuid PRIMARY KEY DEFAULT gen_random_uuid(),
      user_id      uuid NOT NULL,
      status       integer,
      total_amount numeric(10,2),
      created_at   timestamp NOT NULL,
      paid_at      timestamp
    );
  3. 跑 seed.sql 的「D01」段灌入 5 万行
    参考答案

    答案不是抄 SQL,是把文件里 §A 的两条语句找出来跑掉;第 2 条 UPDATE 依赖第 1 条的结果。

    -- seed.sql 的 §A 段共两条:先 INSERT 5 万行,再 UPDATE 埋 20 单时间倒挂
    -- 在 psql 里逐条执行,或 psql -f 只跑这两条
  4. \dt\d orders 看结构,输出贴进 notes.md
    参考答案

    \dt 列出库里的表(此刻只有 orders);\d orders 列出字段、类型、可空性、默认值和主键。把输出贴进 notes.md。

  5. \timing,跑 select count(*) from orders,记下行数与耗时
    参考答案

    第一次跑会几毫秒到几十毫秒(全表扫描);把行数和耗时记下来,第 6 周百万行时回来对比。

    \timing on
    select count(*) from orders;   -- 50000 行
过关标准 库里有且只有 orders 一张表、5 万行;能不看结构说出每个字段的业务含义。
今天只建一张表 · orders
orders(id, user_id, status, total_amount, created_at, paid_at)  -- D01 老板发来的订单导出,全课程第一张表

公司此刻的全部家当就是老板发来的这张订单表,其余 6 张会在后面几周随业务逐张上线(见 总览 · 环境与数据集 )。字段类型自己定--选类型本身就是今天的功课。