Spanner表操作 #

一、表概述 #

1.1 表结构 #

text
Spanner表结构
├── 表名
├── 列定义
│   ├── 列名
│   ├── 数据类型
│   ├── 是否允许NULL
│   └── 默认值
│
├── 主键
│   ├── 必须定义
│   ├── 可包含多列
│   └── 决定数据分布
│
└── 约束条件
    ├── 外键约束
    ├── 检查约束
    └── 交错关系

1.2 表限制 #

限制项
每个数据库最大表数 无限制
每个表最大列数 1024
主键最大列数 16
单行最大大小 10GB
单列最大大小 10GB

二、创建表 #

2.1 基本语法 #

sql
CREATE TABLE table_name (
    column1 data_type [NOT NULL] [DEFAULT default_value],
    column2 data_type [NOT NULL] [DEFAULT default_value],
    ...
) PRIMARY KEY (key_columns);

2.2 创建简单表 #

sql
-- 用户表
CREATE TABLE users (
    user_id INT64 NOT NULL,
    name STRING(100) NOT NULL,
    email STRING(255),
    age INT64,
    is_active BOOL DEFAULT TRUE,
    created_at TIMESTAMP NOT NULL DEFAULT (CURRENT_TIMESTAMP())
) PRIMARY KEY (user_id);

-- 产品表
CREATE TABLE products (
    product_id INT64 NOT NULL,
    name STRING(200) NOT NULL,
    description STRING(MAX),
    price FLOAT64 NOT NULL,
    stock INT64 DEFAULT 0,
    category STRING(50)
) PRIMARY KEY (product_id);

2.3 创建复合主键表 #

sql
-- 订单表(复合主键)
CREATE TABLE orders (
    user_id INT64 NOT NULL,
    order_id INT64 NOT NULL,
    order_date DATE NOT NULL,
    total_amount FLOAT64,
    status STRING(20) DEFAULT 'pending'
) PRIMARY KEY (user_id, order_id);

-- 访问日志(时间戳作为主键一部分)
CREATE TABLE access_logs (
    user_id INT64 NOT NULL,
    access_time TIMESTAMP NOT NULL,
    action STRING(50),
    ip_address STRING(45)
) PRIMARY KEY (user_id, access_time);

2.4 使用默认值 #

sql
CREATE TABLE posts (
    post_id INT64 NOT NULL,
    title STRING(200) NOT NULL,
    content STRING(MAX),
    status STRING(20) DEFAULT 'draft',
    view_count INT64 DEFAULT 0,
    created_at TIMESTAMP DEFAULT (CURRENT_TIMESTAMP()),
    updated_at TIMESTAMP DEFAULT (CURRENT_TIMESTAMP())
) PRIMARY KEY (post_id);

三、主键设计 #

3.1 主键原则 #

text
主键设计原则:
├── 唯一性: 每行必须有唯一主键
├── 不可变: 主键值不应被修改
├── 简洁性: 主键应尽量简短
├── 有序性: 有利于范围查询
└── 分散性: 避免热点问题

3.2 避免热点 #

sql
-- 不推荐: 单调递增主键会产生热点
CREATE TABLE bad_design (
    id INT64 NOT NULL,  -- 单调递增
    name STRING(100)
) PRIMARY KEY (id);

-- 推荐: 使用位反转或UUID分散写入
CREATE TABLE good_design (
    id INT64 NOT NULL,  -- 使用位反转
    name STRING(100)
) PRIMARY KEY (id);

-- 推荐: 使用复合主键分散
CREATE TABLE distributed_design (
    shard_id INT64 NOT NULL,  -- 分片ID
    id INT64 NOT NULL,
    name STRING(100)
) PRIMARY KEY (shard_id, id);

3.3 主键设计模式 #

sql
-- 模式1: 简单整数主键
CREATE TABLE simple (
    id INT64 NOT NULL,
    data STRING(100)
) PRIMARY KEY (id);

-- 模式2: 字符串主键
CREATE TABLE by_name (
    name STRING(100) NOT NULL,
    data STRING(100)
) PRIMARY KEY (name);

-- 模式3: 复合主键(层级关系)
CREATE TABLE hierarchical (
    parent_id INT64 NOT NULL,
    child_id INT64 NOT NULL,
    data STRING(100)
) PRIMARY KEY (parent_id, child_id);

-- 模式4: 时间戳主键(时序数据)
CREATE TABLE time_series (
    device_id INT64 NOT NULL,
    timestamp TIMESTAMP NOT NULL,
    value FLOAT64
) PRIMARY KEY (device_id, timestamp);

四、交错表 #

4.1 交错表概念 #

text
交错表(Interleaved Table):
├── 子表数据物理存储在父表数据旁边
├── 提高父子表JOIN性能
├── 支持级联删除
└── 主键必须包含父表主键

4.2 创建交错表 #

sql
-- 父表: 用户
CREATE TABLE users (
    user_id INT64 NOT NULL,
    name STRING(100) NOT NULL,
    email STRING(255)
) PRIMARY KEY (user_id);

-- 子表: 订单(交错在users下)
CREATE TABLE orders (
    user_id INT64 NOT NULL,      -- 父表主键
    order_id INT64 NOT NULL,     -- 子表主键
    order_date DATE NOT NULL,
    total_amount FLOAT64
) PRIMARY KEY (user_id, order_id)
  INTERLEAVE IN PARENT users ON DELETE CASCADE;

-- 孙表: 订单项(交错在orders下)
CREATE TABLE order_items (
    user_id INT64 NOT NULL,      -- 祖父表主键
    order_id INT64 NOT NULL,     -- 父表主键
    item_id INT64 NOT NULL,      -- 本表主键
    product_id INT64 NOT NULL,
    quantity INT64,
    price FLOAT64
) PRIMARY KEY (user_id, order_id, item_id)
  INTERLEAVE IN PARENT orders ON DELETE CASCADE;

4.3 交错层级结构 #

text
交错层级结构:
users (Level 1)
├── user_id: 1
│   ├── orders (Level 2)
│   │   ├── order_id: 100
│   │   │   ├── order_items (Level 3)
│   │   │   │   ├── item_id: 1
│   │   │   │   └── item_id: 2
│   │   └── order_id: 101
│   └── orders (Level 2)
│       └── order_id: 102
└── user_id: 2
    └── orders (Level 2)
        └── order_id: 200

最大交错深度: 7层

4.4 级联删除选项 #

sql
-- ON DELETE CASCADE: 删除父行时自动删除子行
CREATE TABLE orders_cascade (
    user_id INT64 NOT NULL,
    order_id INT64 NOT NULL
) PRIMARY KEY (user_id, order_id)
  INTERLEAVE IN PARENT users ON DELETE CASCADE;

-- ON DELETE NO ACTION: 删除父行时检查子行(默认)
CREATE TABLE orders_no_action (
    user_id INT64 NOT NULL,
    order_id INT64 NOT NULL
) PRIMARY KEY (user_id, order_id)
  INTERLEAVE IN PARENT users ON DELETE NO ACTION;

五、约束条件 #

5.1 NOT NULL约束 #

sql
CREATE TABLE products (
    product_id INT64 NOT NULL,      -- 必须有值
    name STRING(100) NOT NULL,      -- 必须有值
    description STRING(MAX),        -- 可以为NULL
    price FLOAT64                   -- 可以为NULL
) PRIMARY KEY (product_id);

5.2 CHECK约束 #

sql
CREATE TABLE products (
    product_id INT64 NOT NULL,
    name STRING(100) NOT NULL,
    price FLOAT64 NOT NULL,
    stock INT64 DEFAULT 0,
    CONSTRAINT chk_price_positive CHECK (price > 0),
    CONSTRAINT chk_stock_non_negative CHECK (stock >= 0)
) PRIMARY KEY (product_id);

-- 添加CHECK约束
ALTER TABLE products ADD CONSTRAINT chk_name_length 
    CHECK (LENGTH(name) >= 2);

-- 删除CHECK约束
ALTER TABLE products DROP CONSTRAINT chk_name_length;

5.3 外键约束 #

sql
-- 创建主表
CREATE TABLE departments (
    dept_id INT64 NOT NULL,
    dept_name STRING(100) NOT NULL
) PRIMARY KEY (dept_id);

-- 创建从表(带外键)
CREATE TABLE employees (
    emp_id INT64 NOT NULL,
    name STRING(100) NOT NULL,
    dept_id INT64 NOT NULL,
    CONSTRAINT fk_dept FOREIGN KEY (dept_id) 
        REFERENCES departments (dept_id)
) PRIMARY KEY (emp_id);

-- 外键选项
CREATE TABLE employees_full (
    emp_id INT64 NOT NULL,
    name STRING(100) NOT NULL,
    dept_id INT64,
    CONSTRAINT fk_dept FOREIGN KEY (dept_id) 
        REFERENCES departments (dept_id)
        ON DELETE SET NULL  -- 删除时设为NULL
) PRIMARY KEY (emp_id);

5.4 唯一约束 #

sql
-- 通过唯一索引实现唯一约束
CREATE UNIQUE INDEX idx_users_email ON users(email);

-- 复合唯一约束
CREATE UNIQUE INDEX idx_orders_user_date ON orders(user_id, order_date);

六、修改表 #

6.1 添加列 #

sql
-- 添加单列
ALTER TABLE users ADD COLUMN phone STRING(20);

-- 添加多列
ALTER TABLE users 
    ADD COLUMN address STRING(200),
    ADD COLUMN city STRING(50);

-- 添加带默认值的列
ALTER TABLE users ADD COLUMN country STRING(50) DEFAULT 'USA';

6.2 删除列 #

sql
-- 删除列
ALTER TABLE users DROP COLUMN phone;

-- 注意: 删除列是异步操作
-- 删除后列数据仍占用存储空间,直到垃圾回收

6.3 修改列 #

sql
-- Spanner不支持直接修改列类型
-- 需要创建新列、迁移数据、删除旧列

-- 步骤1: 添加新列
ALTER TABLE users ADD COLUMN name_new STRING(200);

-- 步骤2: 迁移数据
UPDATE users SET name_new = name;

-- 步骤3: 删除旧列
ALTER TABLE users DROP COLUMN name;

-- 步骤4: 重命名(通过创建新列)
ALTER TABLE users ADD COLUMN name STRING(200);
UPDATE users SET name = name_new;
ALTER TABLE users DROP COLUMN name_new;

6.4 添加约束 #

sql
-- 添加CHECK约束
ALTER TABLE products ADD CONSTRAINT chk_price 
    CHECK (price >= 0);

-- 添加外键约束
ALTER TABLE orders ADD CONSTRAINT fk_user 
    FOREIGN KEY (user_id) REFERENCES users(user_id);

6.5 删除约束 #

sql
-- 删除约束
ALTER TABLE orders DROP CONSTRAINT fk_user;

七、删除表 #

7.1 删除表 #

sql
-- 删除表
DROP TABLE table_name;

-- 如果存在则删除
DROP TABLE IF EXISTS table_name;

7.2 删除注意事项 #

text
删除表注意事项:
├── 删除操作不可逆
├── 有交错子表时需要先删除子表
├── 有外键引用时需要先删除约束
├── 删除后数据仍占用存储空间(垃圾回收)
└── 建议先备份重要数据

7.3 删除顺序 #

sql
-- 错误顺序: 先删除父表会失败
DROP TABLE users;  -- ERROR: 有交错子表

-- 正确顺序: 先删除子表
DROP TABLE order_items;
DROP TABLE orders;
DROP TABLE users;

八、表选项 #

8.1 设置表选项 #

sql
CREATE TABLE users (
    user_id INT64 NOT NULL,
    name STRING(100)
) PRIMARY KEY (user_id)
OPTIONS (
    allow_commit_timestamp = true  -- 允许提交时间戳
);

8.2 提交时间戳 #

sql
-- 启用提交时间戳
CREATE TABLE logs (
    log_id INT64 NOT NULL,
    message STRING(MAX),
    commit_ts TIMESTAMP OPTIONS (allow_commit_timestamp = true)
) PRIMARY KEY (log_id);

-- 插入时使用提交时间戳
INSERT INTO logs (log_id, message, commit_ts)
VALUES (1, 'Test message', PENDING_COMMIT_TIMESTAMP());

九、临时表 #

9.1 创建临时表 #

sql
-- 创建临时表(会话级别)
CREATE TEMP TABLE temp_results (
    id INT64 NOT NULL,
    value STRING(100)
) PRIMARY KEY (id);

-- 临时表在会话结束后自动删除

9.2 使用临时表 #

sql
-- 在存储过程中使用临时表
CREATE TEMP TABLE temp_orders AS
SELECT * FROM orders WHERE order_date = CURRENT_DATE();

-- 对临时表进行操作
SELECT * FROM temp_orders WHERE total_amount > 100;

十、系统表 #

10.1 INFORMATION_SCHEMA #

sql
-- 查看所有表
SELECT table_name, table_type
FROM INFORMATION_SCHEMA.TABLES;

-- 查看列信息
SELECT column_name, data_type, is_nullable
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'users';

-- 查看索引信息
SELECT index_name, table_name
FROM INFORMATION_SCHEMA.INDEXES;

-- 查看外键信息
SELECT constraint_name, table_name
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE constraint_type = 'FOREIGN KEY';

10.2 统计信息表 #

sql
-- 查看表统计
SELECT * FROM INFORMATION_SCHEMA.TABLE_STATS;

-- 查看列统计
SELECT * FROM INFORMATION_SCHEMA.COLUMN_STATS;

-- 查看索引统计
SELECT * FROM INFORMATION_SCHEMA.INDEX_STATS;

十一、Schema设计最佳实践 #

11.1 主键设计 #

text
主键设计建议:
├── 使用有意义的业务键
├── 避免单调递增(防止热点)
├── 考虑查询模式
├── 保持主键简短
└── 考虑使用位反转

11.2 交错表使用 #

text
交错表使用场景:
├── 父子关系明确
├── 经常一起查询
├── 需要级联删除
└── 层级不超过7层

不适合交错表:
├── 多父关系
├── 独立查询为主
└── 需要灵活关联

11.3 列设计 #

text
列设计建议:
├── 选择合适的数据类型
├── 合理设置字符串长度
├── 使用NOT NULL约束
├── 设置合理的默认值
└── 避免过多列(建议<100)

十二、总结 #

表操作要点:

操作 语法
创建表 CREATE TABLE … PRIMARY KEY (…)
添加列 ALTER TABLE … ADD COLUMN …
删除列 ALTER TABLE … DROP COLUMN …
添加约束 ALTER TABLE … ADD CONSTRAINT …
删除约束 ALTER TABLE … DROP CONSTRAINT …
删除表 DROP TABLE …

主键设计原则:

text
1. 唯一性
   └── 每行必须有唯一标识

2. 稳定性
   └── 主键值不应改变

3. 分散性
   └── 避免写入热点

4. 简洁性
   └── 主键应尽量简短

5. 有序性
   └── 支持范围查询

下一步,让我们学习数据插入操作!

最后更新:2026-03-27