Oracle 快速入门:15 分钟从安装到增删改查
2026-10-12 09:40:00

适合人群:想最快用上 Oracle 的人。概念只讲”现在必须知道的”,全部命令可复制运行;原理深挖放到文末延伸。

一、30 秒认识 Oracle(可跳过)

Oracle 是老牌商用数据库,银行、电信、政务系统的常客。我们学习用官方 Free 免费版:限 2 核 CPU、2GB 内存、12GB 用户数据,个人学习绰绰有余,语法和企业版完全一致。

开跑前只记一件事:你连接的是实例(活着的进程 + 内存),数据存在数据库(磁盘上的文件),两者合起来才是常说的一套”Oracle Server”。这个区分现在不重要,以后排错时特别有用——连不上多是实例问题,数据丢了/变慢多是存储问题。

图1:Oracle 实例与数据库的关系

二、5 分钟:装好并连上

2.1 Docker 一条命令启动

1
2
3
4
5
6
7
docker run -d --name oracle-free \
-p 1521:1521 \
-e ORACLE_PASSWORD=Test_1234 \
gvenzl/oracle-free:23-slim-faststart

# 首次初始化约一两分钟,看到 DATABASE IS READY TO USE! 即可 Ctrl+C
docker logs -f oracle-free

2.2 三种连接方式,选一个

方式 适合谁
sqlplus 命令行(容器内,零配置) 跟着本文走,用这个
SQL Developer / DBeaver 图形客户端 日常写 SQL
浏览器打开 Database Actions(http://localhost:8080) 不想装任何软件

命令行直进:

1
docker exec -it oracle-free sqlplus system/Test_1234@localhost/FREEPDB1

连接串拆开看:用户名/密码@主机/服务名。服务名是 FREEPDB1,别写成别的——Oracle 12c 之后分两层:CDB(底盘)和 PDB(插上去的业务库),我们全程在 PDB 里干活,现阶段记住这个名字就够了。

连上后跑两条验证:

1
2
SELECT instance_name, status FROM v$instance;              -- 状态应为 OPEN
SELECT banner FROM v$version WHERE ROWNUM = 1; -- 版本信息

退出用 EXIT。图形工具的连接参数就四样:主机 localhost、端口 1521、服务名 FREEPDB1、用户 system / Test_1234。

三、2 分钟:建一个自己的用户

system 是管理员账号,别直接在上面建表。三条命令建一个属于自己的 frank:

1
2
3
4
5
6
7
CREATE USER frank IDENTIFIED BY Pass_1234
DEFAULT TABLESPACE users
QUOTA UNLIMITED ON users;

GRANT CONNECT, RESOURCE TO frank; -- 能登录 + 能建表

EXIT

换 frank 登录:

1
docker exec -it oracle-free sqlplus frank/Pass_1234@localhost/FREEPDB1

这里藏着和 MySQL 最大的不同:Oracle 没有”库”和 USE 切库这回事,用户就是命名空间(schema)——frank 建的表全名都是 frank.xxx,要访问别人 scott 的表就写 scott.emp。

图3:MySQL 库与 Oracle schema 命名空间对照

顺带一提 DEFAULT TABLESPACE users:表在逻辑上放进 USERS 表空间,物理上才落到 .dbf 数据文件里(中间还有段、区、块三层,入门阶段只需记住一条铁律:业务表放 USERS,永远别往 SYSTEM 放)。

图2:Oracle 逻辑存储层级

四、10 分钟:跑通增删改查

以下全部用 frank 执行。

4.1 建两张表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE TABLE users (
user_id NUMBER(10) GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
username VARCHAR2(50 CHAR) NOT NULL UNIQUE,
email VARCHAR2(120 CHAR) NOT NULL UNIQUE,
status NUMBER(1) DEFAULT 1 NOT NULL,
created_at DATE DEFAULT SYSDATE NOT NULL
);

CREATE TABLE orders (
order_id NUMBER(19) GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
user_id NUMBER(10) NOT NULL,
amount NUMBER(12,2) NOT NULL CHECK (amount >= 0),
order_status VARCHAR2(20 CHAR) DEFAULT '待支付' NOT NULL,
created_at DATE DEFAULT SYSDATE NOT NULL
);

-- 建错了想推翻重来:
-- DROP TABLE orders PURGE;

类型三句话:字符串一律 VARCHAR2(n CHAR)——必须带 CHAR,按字符计数,存中文才不会截断;数字只有 NUMBER(p,s),整数小数通吃、金额精确;时间用 DATE,自带时分秒。自增主键不叫 AUTO_INCREMENT,叫 GENERATED BY DEFAULT AS IDENTITY。

4.2 增:INSERT

1
2
3
4
5
6
7
8
9
INSERT INTO users (username, email) VALUES
('alice', 'alice@example.com');

INSERT INTO users (username, email) VALUES
('bob', 'bob@example.com');

INSERT INTO orders (user_id, amount, order_status) VALUES (1, 668.00, '已支付');
INSERT INTO orders (user_id, amount, order_status) VALUES (1, 199.00, '待支付');
INSERT INTO orders (user_id, amount, order_status) VALUES (2, 399.00, '已支付');

必须记住:Oracle 默认不自动提交。不敲 COMMIT,数据只在当前会话可见,断连就没了。

1
2
COMMIT;      -- 保存
-- ROLLBACK; -- 反悔上一次提交之后的改动

4.3 查:SELECT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 全表
SELECT * FROM users;

-- 条件 + 排序 + 前 N 条(相当于 LIMIT 10)
SELECT user_id, username, created_at
FROM users
WHERE status = 1
ORDER BY created_at DESC
FETCH FIRST 10 ROWS ONLY;

-- 关联 + 聚合:每个用户的订单数和总金额
SELECT u.username,
COUNT(o.order_id) AS order_cnt,
SUM(o.amount) AS total_amt
FROM users u
LEFT JOIN orders o ON o.user_id = u.user_id
GROUP BY u.username;

-- 没有 FROM 的查询要用虚表 dual
SELECT SYSDATE FROM dual;

4.4 改与删:UPDATE / DELETE

1
2
3
4
5
UPDATE orders SET order_status = '已支付' WHERE order_id = 2;

DELETE FROM orders WHERE order_id = 3;

COMMIT;

4.5 分页

1
2
3
-- 推荐写法(12c+),对应 MySQL 的 LIMIT 10,10
SELECT * FROM orders ORDER BY created_at DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

但老项目里满地都是 ROWNUM 写法,看懂即可:

1
2
3
4
5
SELECT * FROM (
SELECT a.*, ROWNUM rn
FROM (SELECT * FROM orders ORDER BY created_at DESC) a
WHERE ROWNUM <= 20
) WHERE rn >= 11;

ROWNUM 有个著名的坑:WHERE ROWNUM > 1 永远查不到数据——编号是行被取出那一刻才分配的,不满足条件的行被丢弃后,下一行又从 1 开始测试,全军覆没。所以必须”子查询先编号,外层再取区间”,口诀:上界在内,下界在外。

图4:ROWNUM 分页原理

五、避坑速查:和 MySQL 不一样的地方

一张表全部带走:

场景 MySQL Oracle
自增主键 AUTO_INCREMENT GENERATED BY DEFAULT AS IDENTITY
前 N 条 LIMIT 10 FETCH FIRST 10 ROWS ONLY
分页 LIMIT 10, 10 OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
切库 USE mydb 没有;用户即库,写 frank.users
当前时间 NOW() SYSDATE
字符串拼接 CONCAT(a, b) a || b
判空替换 IFNULL(x, y) NVL(x, y)
时间格式化 DATE_FORMAT(t, '%Y-%m-%d') TO_CHAR(t, 'YYYY-MM-DD')
大文本 TEXT CLOB
反引号 `col` 什么都不加
自动提交 默认开 默认关,手动 COMMIT

两个必踩的坑:

坑一,空字符串就是 NULL:

1
2
3
UPDATE users SET email = '' WHERE user_id = 1;   -- 实际效果 = SET email = NULL
SELECT * FROM users WHERE email = ''; -- 永远查不到(NULL 比较无结果)
SELECT * FROM users WHERE email IS NULL; -- 这才是对的

坑二,标识符统一按大写存储:数据字典里查到的是 USERS 不是 users;数据本身('alice' 和 'ALICE')照旧区分大小写。另外别用双引号建表——CREATE TABLE "my_table" 会强制保留小写,以后每次访问都得带引号,纯属自找麻烦。


思考与练习(10 分钟)

  1. 建一张 products 表(商品名/价格/库存),插入 3 条数据并提交。
  2. 查价格大于 200 的商品,按价格倒序,只取前 2 条。
  3. 把一条订单的 amount 改成 -1 会发生什么?(提示:CHECK 约束)
  4. 故意 UPDATE 后不提交,另开一个窗口查询,能看到改动吗?再试试 ROLLBACK。

本章核心总结

  • 连接四要素:主机 localhost、端口 1521、服务名 FREEPDB1、用户名/密码
  • 用户就是库(schema):没有 USE,访问别人的表写全名 scott.emp
  • 类型三板斧:VARCHAR2(n CHAR)、NUMBER(p,s)、DATE;自增用 IDENTITY
  • 手动 COMMIT,默认不自动提交
  • 分页用 FETCH ... OFFSET,老代码认得 ROWNUM(上界在内,下界在外)
  • 两大坑:'' 即 NULL;标识符按大写存储、远离双引号

下一步

到这里你已经是”能用 Oracle 干活”的状态了。想继续深入,按需取用:权限体系(GRANT 细粒度授权)、事务与锁(和 MySQL 的 MVCC 差异)、实例与表空间的存储原理(回看图 1、图 2 就是入口)。

上一页
2026-10-12 09:40:00
下一页