SQL示例:分别使用 MySQL 和 Oracle 创建表,MySQL 插入数据建索引(自增主键、指定主键的区别,VARCHAR,VARCHAR2)

本文介绍了创建actor表的SQL语句实现方案,重点对比了MySQL和Oracle两种数据库的语法差异。


MySQL版本使用SMALLINT主键、VARCHAR字段和CURDATE()默认值,而Oracle使用NUMBER类型、VARCHAR2字段和SYSDATE默认值。


文章详细说明了主键约束、默认值设置和字段注释的实现方式,并提供了低版本MySQL的替代方案(使用触发器)。


同时对比了自增主键和指定主键的特点,包括定义方式、唯一性保证、插入语法等维度,最后给出了不同场景下的主键选择建议。


对于需要兼容多版本或特殊需求的场景,还提供了混合方案的实现示例。


Oracle 特有VARCHAR2,其他数据库使用 VARCHAR。


题目:创建表

SQL232 创建一个actor表,包含如下列


描述

创建一个actor表,包含如下列信息

列表 类型 是否为NULL 含义
actor_id smallint(5) not null 主键id
first_name varchar(45) not null 名字
last_name varchar(45) not null 姓氏
last_update date not null 最后更新时间,默认是系统的当前时间

解法


创建 actor 表的 MySQL 语句如下:


sql

sql 复制代码
CREATE TABLE actor (
    actor_id SMALLINT(5) NOT NULL COMMENT '主键id',
    first_name VARCHAR(45) NOT NULL COMMENT '名字',
    last_name VARCHAR(45) NOT NULL COMMENT '姓氏',
    last_update DATE NOT NULL DEFAULT (CURDATE()) COMMENT '最后更新时间,默认是系统的当前时间',
    PRIMARY KEY (actor_id)
);

说明:

  1. 主键约束actor_id 被设置为主键,确保每行数据的唯一性

  2. 默认值last_update 字段使用 DEFAULT (CURDATE()) 设置默认值为当前系统日期(年月日部分)

  3. 注释 :使用 COMMENT 为每个字段添加了中文说明

注意:

  • DEFAULT (CURDATE()) 语法要求 MySQL 8.0.13 及以上版本

  • 如果使用更低版本的 MySQL,需要使用触发器来实现默认当前日期,或者改用 DEFAULT CURRENT_DATE(但 MySQL 的 DATE 类型字段不支持直接使用 CURRENT_DATE 作为默认值)

兼容低版本 MySQL 的替代方案:

sql

sql 复制代码
CREATE TABLE actor (
    actor_id SMALLINT(5) NOT NULL COMMENT '主键id',
    first_name VARCHAR(45) NOT NULL COMMENT '名字',
    last_name VARCHAR(45) NOT NULL COMMENT '姓氏',
    last_update DATE NOT NULL COMMENT '最后更新时间',
    PRIMARY KEY (actor_id)
);

-- 创建触发器自动设置 last_update 的默认值
DELIMITER $$
CREATE TRIGGER actor_default_last_update
BEFORE INSERT ON actor
FOR EACH ROW
BEGIN
    IF NEW.last_update IS NULL THEN
        SET NEW.last_update = CURDATE();
    END IF;
END$$
DELIMITER ;

完整建表+插入数据

  • 用 INSERT INTO ... VALUES 语法
  • 多条数据用逗号分隔,每条数据用括号包裹
  • 字段顺序与 VALUES 中的值顺序对应
  • 字符串和日期时间值使用单引号包裹

错误示例

sql 复制代码
--建表
CREATE TABLE IF NOT EXISTS actor (
    actor_id smallint(5) NOT NULL PRIMARY KEY,
    first_name varchar(45) NOT NULL,
    last_name varchar(45) NOT NULL,
    last_update timestamp NOT NULL DEFAULT (datetime('now','localtime'))
);

--批量插入数据
INSERT INTO actor (actor_id, first_name, last_name, last_update) VALUES
(1, 'PENELOPE', 'GUINESS', '2006-02-15 12:34:33'),
(2, 'NICK', 'WAHLBERG', '2006-02-15 12:34:33');


--查询验证
SELECT * FROM actor;

注意

上例在 MySQL 中使用了 SQLite 的语法 datetime('now','localtime'),MySQL 不支持这种写法。


sql 复制代码
-- ❌ 错误:这是 SQLite 的语法,MySQL 不支持
last_update timestamp NOT NULL DEFAULT (datetime('now','localtime'))

-- ✅ MySQL 正确写法
last_update timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP

正确的 MySQL 代码

sql 复制代码
-- 建表
CREATE TABLE IF NOT EXISTS actor (
    actor_id smallint(5) NOT NULL PRIMARY KEY,
    first_name varchar(45) NOT NULL,
    last_name varchar(45) NOT NULL,
    last_update timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- 批量插入数据
INSERT INTO actor (actor_id, first_name, last_name, last_update) VALUES
(1, 'PENELOPE', 'GUINESS', '2006-02-15 12:34:33'),
(2, 'NICK', 'WAHLBERG', '2006-02-15 12:34:33');

-- 查询验证
SELECT * FROM actor;

如果需要 DATETIME 类型

sql 复制代码
-- MySQL 5.6.5 之后版本支持
last_update DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP

-- 或使用函数
last_update DATETIME NOT NULL DEFAULT (NOW())

使用 INSERT IGNORE 语法来实现"如果数据已经存在,请忽略"

不使用replace操作

sql 复制代码
INSERT IGNORE INTO actor (actor_id, first_name, last_name, last_update) 
VALUES (3, 'ED', 'CHASE', '2006-02-15 12:34:33');

说明

关键字 作用
INSERT IGNORE 插入时如果主键冲突,忽略该条插入,不报错

执行逻辑

  1. 表中已存在 actor_id = 3 的记录(WD, GUINESS)

  2. 执行 INSERT IGNORE 尝试插入相同 actor_id 的记录

  3. 由于主键冲突,MySQL 忽略新数据的插入

  4. 表中数据保持不变,仍为:3|WD|GUINESS|2006-02-15 12:34:33

其他替代方案(MySQL 语法对比)

语法 行为 适用场景
INSERT IGNORE 冲突时忽略,不报错 ✅ 本题要求
REPLACE 冲突时删除旧记录再插入新记录 ❌ 题目不支持
ON DUPLICATE KEY UPDATE 冲突时执行更新操作 需要更新时使用

创建一个actor_name表,并且将actor表中的所有first_name以及last_name导入该表.

actor_name表结构如下,题目最后会查询actor_name表里面的数据来对比结果输出:

列表 类型 是否为NULL 含义
first_name varchar(45) not null 名字
last_name varchar(45) not null 姓氏

创建 actor_name 表并从 actor 表中导入 first_namelast_name 数据。

正确语句

sql

sql 复制代码
-- 创建 actor_name 表
CREATE TABLE actor_name (
    first_name VARCHAR(45) NOT NULL COMMENT '名字',
    last_name VARCHAR(45) NOT NULL COMMENT '姓氏'
);

-- 将 actor 表中的 first_name 和 last_name 导入 actor_name 表
INSERT INTO actor_name (first_name, last_name)
SELECT first_name, last_name FROM actor;

一条语句完成(创建并插入)

sql

sql 复制代码
-- 创建表并同时插入数据
CREATE TABLE actor_name AS
SELECT first_name, last_name
FROM actor;

两种方式对比

方式 优点 缺点
CREATE TABLE + INSERT SELECT 可明确指定表结构(NOT NULL等约束) 需要两条语句
CREATE TABLE ... AS SELECT 一条语句完成,简洁 不会复制原表的约束(如 NOT NULL)

完整示例

sql

sql 复制代码
-- 清理并创建 actor 表(题目已执行)
DROP TABLE IF EXISTS actor;
CREATE TABLE actor (
    actor_id SMALLINT(5) NOT NULL PRIMARY KEY,
    first_name VARCHAR(45) NOT NULL,
    last_name VARCHAR(45) NOT NULL,
    last_update DATETIME NOT NULL
);
INSERT INTO actor VALUES 
(1, 'PENELOPE', 'GUINESS', '2006-02-15 12:34:33'),
(2, 'NICK', 'WAHLBERG', '2006-02-15 12:34:33');

-- 创建 actor_name 表并导入数据
CREATE TABLE actor_name (
    first_name VARCHAR(45) NOT NULL,
    last_name VARCHAR(45) NOT NULL
);

INSERT INTO actor_name (first_name, last_name)
SELECT first_name, last_name FROM actor;

-- 查询验证
SELECT * FROM actor_name;

actor 表的 first_name 创建唯一索引,为 last_name 创建普通索引


MySQL 正确语句

sql

sql 复制代码
-- 创建唯一索引
CREATE UNIQUE INDEX uniq_idx_firstname ON actor(first_name);

-- 创建普通索引
CREATE INDEX idx_lastname ON actor(last_name);

使用 ALTER TABLE 方式(MySQL 也支持)

sql

sql 复制代码
-- 创建唯一索引
ALTER TABLE actor ADD UNIQUE INDEX uniq_idx_firstname(first_name);

-- 创建普通索引
ALTER TABLE actor ADD INDEX idx_lastname(last_name);

完整示例

sql

sql 复制代码
-- 建表(题目已执行)
CREATE TABLE actor (
    actor_id SMALLINT(5) NOT NULL PRIMARY KEY,
    first_name VARCHAR(45) NOT NULL,
    last_name VARCHAR(45) NOT NULL,
    last_update DATETIME NOT NULL
);

-- 创建索引
CREATE UNIQUE INDEX uniq_idx_firstname ON actor(first_name);
CREATE INDEX idx_lastname ON actor(last_name);

验证索引是否创建成功

sql

sql 复制代码
-- 查看表中的索引
SHOW INDEX FROM actor;

两种索引的区别

索引类型 关键字 特点
唯一索引 UNIQUE INDEX 不允许重复值,可快速去重
普通索引 INDEX 允许重复值,仅提高查询速度

注意事项

数据库 是否支持
MySQL ✅ 完全支持
SQLite ❌ 不支持(题目已说明)

在 Oracle 中实现相同的 actor 表,语法如下:


方法一:使用 DEFAULT 子句(推荐)

sql

sql 复制代码
CREATE TABLE actor (
    actor_id NUMBER(5) NOT NULL,
    first_name VARCHAR2(45) NOT NULL,
    last_name VARCHAR2(45) NOT NULL,
    last_update DATE DEFAULT SYSDATE NOT NULL,
    CONSTRAINT pk_actor PRIMARY KEY (actor_id)
);

COMMENT ON TABLE actor IS '演员表';
COMMENT ON COLUMN actor.actor_id IS '主键id';
COMMENT ON COLUMN actor.first_name IS '名字';
COMMENT ON COLUMN actor.last_name IS '姓氏';
COMMENT ON COLUMN actor.last_update IS '最后更新时间,默认是系统的当前时间';

方法二:使用触发器(更灵活的默认值)

如果需要只存储日期部分(不包含时间),可以使用触发器:

sql

sql 复制代码
-- 创建表
CREATE TABLE actor (
    actor_id NUMBER(5) NOT NULL,
    first_name VARCHAR2(45) NOT NULL,
    last_name VARCHAR2(45) NOT NULL,
    last_update DATE NOT NULL,
    CONSTRAINT pk_actor PRIMARY KEY (actor_id)
);

COMMENT ON TABLE actor IS '演员表';
COMMENT ON COLUMN actor.actor_id IS '主键id';
COMMENT ON COLUMN actor.first_name IS '名字';
COMMENT ON COLUMN actor.last_name IS '姓氏';
COMMENT ON COLUMN actor.last_update IS '最后更新时间,默认是系统的当前时间';

-- 创建触发器设置默认值
CREATE OR REPLACE TRIGGER trg_actor_last_update
BEFORE INSERT ON actor
FOR EACH ROW
BEGIN
    IF :NEW.last_update IS NULL THEN
        :NEW.last_update := TRUNC(SYSDATE);  -- TRUNC 去除时间部分,只保留日期
    END IF;
END;
/

方法三:使用 identity 列(Oracle 12c+)

如果需要自增主键,可以使用 identity 列:

sql

sql 复制代码
CREATE TABLE actor (
    actor_id NUMBER(5) GENERATED BY DEFAULT AS IDENTITY NOT NULL,
    first_name VARCHAR2(45) NOT NULL,
    last_name VARCHAR2(45) NOT NULL,
    last_update DATE DEFAULT SYSDATE NOT NULL,
    CONSTRAINT pk_actor PRIMARY KEY (actor_id)
);

Oracle 与 MySQL 的主要差异:

项目 MySQL Oracle
整数类型 SMALLINT(5) NUMBER(5)
字符串类型 VARCHAR(45) VARCHAR2(45)
主键定义 PRIMARY KEY (column) CONSTRAINT pk_name PRIMARY KEY (column)
当前日期时间 CURDATE() SYSDATE
注释语法 COMMENT '注释' COMMENT ON COLUMN...
只取日期部分 CURDATE() TRUNC(SYSDATE)

建议:

  • 使用 DEFAULT SYSDATE 是最简单的方式,但会包含时间部分(00:00:00)

  • 如果严格只需要日期,使用 TRUNC(SYSDATE) 配合触发器

  • 如果需要自增主键,Oracle 12c 及以上版本使用 IDENTITY


自增主键 vs 指定主键的区别

对比维度 自增主键 (Auto-Increment) 指定主键 (Assigned Primary Key)
定义方式 MySQL: AUTO_INCREMENT Oracle: IDENTITY 或序列+触发器 直接赋值,如 id = 100
值生成时机 插入数据时由数据库自动生成 插入前由应用程序或用户手动指定
唯一性保证 数据库自动保证唯一,不会重复 需要应用层或业务逻辑保证唯一
是否可为空 通常配合 NOT NULL,插入时可省略该列 必须明确赋值,不能为 NULL
插入语句 INSERT INTO actor (name) VALUES ('Tom') (省略主键列) INSERT INTO actor (id, name) VALUES (101, 'Tom') (必须指定主键值)
值含义 仅表示记录的唯一标识,无业务含义 可能代表业务含义(如身份证号、员工工号)
分布式系统 容易产生冲突,需特殊处理(如雪花算法) 适合分布式,可预先分配ID段
数据迁移 可能产生冲突,需重置自增起始值 可保持原有ID不变,迁移更简单
性能影响 插入性能好,B+树顺序写入 插入可能产生随机IO,性能略差
预测性 无法提前预知新记录的ID 可提前规划ID范围或规则
可读性 无业务含义,仅用于内部关联 可能有含义,如地区码+序号
修改灵活性 通常不允许修改主键值 理论上可修改,但不建议
适用场景 无业务含义的代理键,绝大多数OLTP场景 有业务含义的自然键,或需要外部系统指定ID

示例对比

操作 自增主键 指定主键
建表语句 CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50)) CREATE TABLE t (id INT PRIMARY KEY, name VARCHAR(50))
插入数据 INSERT INTO t (name) VALUES ('Alice') INSERT INTO t (name) VALUES ('Bob') INSERT INTO t (id, name) VALUES (101, 'Alice') INSERT INTO t (id, name) VALUES (102, 'Bob')
连续插入后结果 id 自动为 1, 2, 3... id 保持指定的 101, 102...
插入已存在ID 不会发生(自动生成新值) 会报主键冲突错误

混合方案:默认自增 + 允许手动指定

某些数据库支持混合模式,设置默认自增但也可手动指定:

sql

sql 复制代码
-- MySQL: 允许手动指定覆盖自增值
INSERT INTO t (id, name) VALUES (100, 'Special');  -- 指定100
INSERT INTO t (name) VALUES ('Normal');            -- 自增为101

-- Oracle (12c+): 
CREATE TABLE t (id NUMBER GENERATED BY DEFAULT AS IDENTITY, name VARCHAR2(50));
-- 可手动指定或自动生成

选择建议

场景 推荐方案
无业务需求的纯粹标识 ✅ 自增主键
主键有业务含义(如订单号) ✅ 指定主键
数据来自多个系统或分库分表 ✅ 指定主键(如雪花ID)
需要频繁数据合并迁移 ✅ 指定主键
追求最高插入性能 ✅ 自增主键
希望主键对外不可预测 ✅ 指定主键(随机值)或使用UUID

VARCHAR vs VARCHAR2 区别

对比维度 VARCHAR VARCHAR2
数据库支持 MySQL、PostgreSQL 等支持 Oracle 也支持但不推荐 Oracle 特有,其他数据库不支持
标准定义 SQL 标准类型 Oracle 私有类型(非标准)
存储空间 相同(可变长度,实际长度+1~2字节) 相同(可变长度,实际长度+1~2字节)
最大长度 MySQL: 65535字节 Oracle: 4000字节 Oracle: 4000字节(12c后可达32767)
空字符串处理 空字符串 '' 等同于 NULL 空字符串 '' 也等同于 NULL(Oracle中相同)
官方推荐 Oracle 官方不推荐使用 VARCHAR Oracle 官方推荐使用 VARCHAR2

核心结论

Oracle 中两者功能完全相同,但官方强制要求使用 VARCHAR2

Oracle 官方文档明确说明:

  • VARCHAR 计划在未来版本中改变用途或废弃

  • 始终使用 VARCHAR2 确保向后兼容

跨数据库建议

数据库 推荐类型
Oracle VARCHAR2
MySQL VARCHAR
PostgreSQL VARCHAR
SQL Server VARCHAR
相关推荐
用户4217632814071 小时前
如何用java实现一个简单的并发版本控制MVCC
mysql
预测模型的开发与应用研究2 小时前
Oracle双库部署
数据库·oracle
阿维的博客日记2 小时前
求解深分页问题,last pk适合什么情况
java·mysql·深分页
__water2 小时前
【下载配置Mysql】
mysql
是桃萌萌鸭~3 小时前
oracle的隐藏虚拟列详解
运维·数据库·oracle
Geoffwo4 小时前
Oracle MySQL8.0升级8.4,无感升级数据库
数据库·oracle
Dxy12393102165 小时前
MySQL 连表查询更新:从理论到实践
数据库·mysql
阿丰资源5 小时前
基于Springboot+mysql的在线兼职平台(附源码)
spring boot·后端·mysql
怪祝浙5 小时前
从简单项目入手Java(学生系统)V6(Web版本 Spring Boot3 MySQL Vue3 MyBatis)
java·spring boot·mysql