Oracle基础查询 #

一、SELECT语句概述 #

1.1 基本语法 #

sql
-- SELECT语句基本语法
SELECT [DISTINCT] column_list
FROM table_name
[WHERE condition]
[GROUP BY column_list]
[HAVING group_condition]
[ORDER BY column_list];

1.2 示例表结构 #

sql
-- 创建示例表
CREATE TABLE employees (
    employee_id NUMBER(6) PRIMARY KEY,
    first_name VARCHAR2(50),
    last_name VARCHAR2(50),
    email VARCHAR2(100),
    phone_number VARCHAR2(20),
    hire_date DATE,
    job_id VARCHAR2(10),
    salary NUMBER(8,2),
    commission_pct NUMBER(2,2),
    manager_id NUMBER(6),
    department_id NUMBER(4)
);

-- 插入测试数据
INSERT INTO employees VALUES (1, 'John', 'Doe', 'john@example.com', '123-456-7890', 
    TO_DATE('2020-01-15', 'YYYY-MM-DD'), 'IT_PROG', 8000, NULL, 100, 10);
INSERT INTO employees VALUES (2, 'Jane', 'Smith', 'jane@example.com', '123-456-7891', 
    TO_DATE('2019-03-20', 'YYYY-MM-DD'), 'SA_REP', 9000, 0.1, 101, 20);
INSERT INTO employees VALUES (3, 'Bob', 'Johnson', 'bob@example.com', '123-456-7892', 
    TO_DATE('2021-06-10', 'YYYY-MM-DD'), 'IT_PROG', 7000, NULL, 100, 10);
INSERT INTO employees VALUES (4, 'Alice', 'Williams', 'alice@example.com', '123-456-7893', 
    TO_DATE('2018-11-05', 'YYYY-MM-DD'), 'SA_MAN', 12000, 0.2, 102, 30);
INSERT INTO employees VALUES (5, 'Charlie', 'Brown', 'charlie@example.com', '123-456-7894', 
    TO_DATE('2022-02-28', 'YYYY-MM-DD'), 'SA_REP', 6500, 0.15, 101, 20);

二、基本查询 #

2.1 查询所有列 #

sql
-- 查询所有列
SELECT * FROM employees;

-- 结果
EMPLOYEE_ID FIRST_NAME LAST_NAME  EMAIL              PHONE_NUMBER  HIRE_DATE  JOB_ID   SALARY
----------- ---------- ---------- ------------------ ------------- ---------- -------- ------
          1 John       Doe        john@example.com   123-456-7890  2020-01-15 IT_PROG   8000
          2 Jane       Smith      jane@example.com   123-456-7891  2019-03-20 SA_REP    9000
          3 Bob        Johnson    bob@example.com    123-456-7892  2021-06-10 IT_PROG   7000
          4 Alice      Williams   alice@example.com  123-456-7893  2018-11-05 SA_MAN   12000
          5 Charlie    Brown      charlie@example.com 123-456-7894  2022-02-28 SA_REP    6500

2.2 查询指定列 #

sql
-- 查询指定列
SELECT employee_id, first_name, last_name, salary
FROM employees;

-- 使用列别名
SELECT 
    employee_id AS emp_id,
    first_name AS "名",
    last_name AS "姓",
    salary AS "薪资"
FROM employees;

-- 省略AS关键字
SELECT employee_id emp_id, first_name name FROM employees;

-- 使用表达式
SELECT 
    employee_id,
    first_name || ' ' || last_name AS full_name,
    salary,
    salary * 12 AS annual_salary
FROM employees;

2.3 DISTINCT去重 #

sql
-- 查询所有部门ID(包含重复)
SELECT department_id FROM employees;

-- 查询不重复的部门ID
SELECT DISTINCT department_id FROM employees;

-- 多列去重
SELECT DISTINCT department_id, job_id FROM employees;

-- 统计去重数量
SELECT COUNT(DISTINCT department_id) AS dept_count FROM employees;

三、WHERE条件 #

3.1 比较运算符 #

sql
-- 等于
SELECT * FROM employees WHERE department_id = 10;

-- 不等于
SELECT * FROM employees WHERE department_id != 10;
SELECT * FROM employees WHERE department_id <> 10;

-- 大于、小于
SELECT * FROM employees WHERE salary > 8000;
SELECT * FROM employees WHERE salary < 8000;
SELECT * FROM employees WHERE salary >= 8000;
SELECT * FROM employees WHERE salary <= 8000;

3.2 逻辑运算符 #

sql
-- AND:同时满足多个条件
SELECT * FROM employees 
WHERE department_id = 10 AND salary > 7000;

-- OR:满足任一条件
SELECT * FROM employees 
WHERE department_id = 10 OR department_id = 20;

-- NOT:取反
SELECT * FROM employees WHERE NOT department_id = 10;

-- 组合使用(注意优先级)
SELECT * FROM employees 
WHERE (department_id = 10 OR department_id = 20) AND salary > 7000;

3.3 BETWEEN…AND #

sql
-- 范围查询(包含边界)
SELECT * FROM employees 
WHERE salary BETWEEN 7000 AND 10000;

-- 等价于
SELECT * FROM employees 
WHERE salary >= 7000 AND salary <= 10000;

-- NOT BETWEEN
SELECT * FROM employees 
WHERE salary NOT BETWEEN 7000 AND 10000;

-- 日期范围
SELECT * FROM employees 
WHERE hire_date BETWEEN TO_DATE('2020-01-01', 'YYYY-MM-DD') 
                    AND TO_DATE('2021-12-31', 'YYYY-MM-DD');

3.4 IN #

sql
-- 列表匹配
SELECT * FROM employees 
WHERE department_id IN (10, 20, 30);

-- 等价于
SELECT * FROM employees 
WHERE department_id = 10 OR department_id = 20 OR department_id = 30;

-- NOT IN
SELECT * FROM employees 
WHERE department_id NOT IN (10, 20);

-- 子查询
SELECT * FROM employees 
WHERE department_id IN (
    SELECT department_id FROM departments WHERE location_id = 1700
);

-- 注意:NOT IN与NULL
-- 如果子查询返回NULL,NOT IN不会返回任何结果
SELECT * FROM employees 
WHERE department_id NOT IN (SELECT department_id FROM temp WHERE department_id IS NOT NULL);

3.5 LIKE模糊匹配 #

sql
-- % 匹配任意多个字符
SELECT * FROM employees WHERE first_name LIKE 'J%';    -- 以J开头
SELECT * FROM employees WHERE first_name LIKE '%n';    -- 以n结尾
SELECT * FROM employees WHERE first_name LIKE '%oh%';  -- 包含oh

-- _ 匹配单个字符
SELECT * FROM employees WHERE first_name LIKE 'J_hn';  -- John

-- 组合使用
SELECT * FROM employees WHERE email LIKE '%@example.com';

-- NOT LIKE
SELECT * FROM employees WHERE first_name NOT LIKE 'J%';

-- 转义特殊字符
SELECT * FROM employees WHERE email LIKE '%\_%' ESCAPE '\';  -- 匹配包含下划线

-- 大小写敏感
SELECT * FROM employees WHERE first_name LIKE 'JOHN';       -- 大小写敏感
SELECT * FROM employees WHERE UPPER(first_name) LIKE 'JOHN'; -- 大小写不敏感

3.6 NULL值判断 #

sql
-- IS NULL
SELECT * FROM employees WHERE commission_pct IS NULL;

-- IS NOT NULL
SELECT * FROM employees WHERE commission_pct IS NOT NULL;

-- 错误写法
SELECT * FROM employees WHERE commission_pct = NULL;    -- 不会返回结果
SELECT * FROM employees WHERE commission_pct != NULL;   -- 不会返回结果

-- NVL函数处理NULL
SELECT 
    employee_id,
    salary,
    NVL(commission_pct, 0) AS commission,
    salary + salary * NVL(commission_pct, 0) AS total_income
FROM employees;

-- NVL2函数
SELECT 
    employee_id,
    NVL2(commission_pct, '有提成', '无提成') AS has_commission
FROM employees;

-- COALESCE函数
SELECT 
    employee_id,
    COALESCE(commission_pct, 0) AS commission
FROM employees;

四、ORDER BY排序 #

4.1 基本排序 #

sql
-- 升序排序(默认ASC)
SELECT * FROM employees ORDER BY salary;
SELECT * FROM employees ORDER BY salary ASC;

-- 降序排序
SELECT * FROM employees ORDER BY salary DESC;

-- 多列排序
SELECT * FROM employees 
ORDER BY department_id ASC, salary DESC;

-- 按列别名排序
SELECT employee_id, salary * 12 AS annual_salary
FROM employees
ORDER BY annual_salary DESC;

-- 按列位置排序
SELECT employee_id, first_name, salary
FROM employees
ORDER BY 3 DESC;  -- 按第3列(salary)排序

4.2 NULL值排序 #

sql
-- NULL值默认排在最后(升序)或最前(降序)
SELECT * FROM employees ORDER BY commission_pct;      -- NULL在最后
SELECT * FROM employees ORDER BY commission_pct DESC; -- NULL在最前

-- NULLS FIRST:NULL值排在最前
SELECT * FROM employees ORDER BY commission_pct NULLS FIRST;

-- NULLS LAST:NULL值排在最后
SELECT * FROM employees ORDER BY commission_pct DESC NULLS LAST;

4.3 表达式排序 #

sql
-- 按表达式排序
SELECT employee_id, salary, commission_pct
FROM employees
ORDER BY salary + NVL(salary * commission_pct, 0) DESC;

-- 按函数结果排序
SELECT * FROM employees ORDER BY LENGTH(first_name);

-- 按CASE表达式排序
SELECT * FROM employees
ORDER BY CASE job_id
    WHEN 'SA_MAN' THEN 1
    WHEN 'SA_REP' THEN 2
    WHEN 'IT_PROG' THEN 3
    ELSE 4
END;

五、ROWNUM分页 #

5.1 ROWNUM基础 #

sql
-- ROWNUM是Oracle的伪列,表示行号
SELECT ROWNUM, employee_id, first_name, salary
FROM employees;

-- ROWNUM从1开始
-- 注意:ROWNUM在ORDER BY之前分配

-- 错误:获取薪资最高的3人
SELECT ROWNUM, employee_id, salary
FROM employees
WHERE ROWNUM <= 3
ORDER BY salary DESC;  -- 先取前3行再排序,结果错误

-- 正确:使用子查询
SELECT *
FROM (
    SELECT employee_id, salary
    FROM employees
    ORDER BY salary DESC
)
WHERE ROWNUM <= 3;

5.2 分页查询 #

sql
-- 方法1:使用子查询
-- 第1页(每页3条)
SELECT *
FROM (
    SELECT a.*, ROWNUM rn
    FROM (
        SELECT * FROM employees ORDER BY employee_id
    ) a
    WHERE ROWNUM <= 3
)
WHERE rn > 0;

-- 第2页
SELECT *
FROM (
    SELECT a.*, ROWNUM rn
    FROM (
        SELECT * FROM employees ORDER BY employee_id
    ) a
    WHERE ROWNUM <= 6
)
WHERE rn > 3;

-- 通用分页公式
-- 第n页,每页m条
-- ROWNUM <= n * m
-- rn > (n - 1) * m

-- 方法2:使用ROW_NUMBER()(推荐)
SELECT *
FROM (
    SELECT 
        a.*,
        ROW_NUMBER() OVER (ORDER BY employee_id) AS rn
    FROM employees a
)
WHERE rn BETWEEN 4 AND 6;  -- 第2页,每页3条

-- 方法3:12c+使用OFFSET FETCH
SELECT * FROM employees
ORDER BY employee_id
OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY;  -- 第2页,每页3条

5.3 12c+分页语法 #

sql
-- OFFSET...FETCH语法(Oracle 12c+)

-- 跳过前3条,获取接下来的3条
SELECT * FROM employees
ORDER BY employee_id
OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY;

-- 获取前3条
SELECT * FROM employees
ORDER BY employee_id
FETCH FIRST 3 ROWS ONLY;

-- 获取前30%
SELECT * FROM employees
ORDER BY employee_id
FETCH FIRST 30 PERCENT ROWS ONLY;

-- 使用WITH TIES包含并列行
SELECT * FROM employees
ORDER BY salary DESC
FETCH FIRST 3 ROWS WITH TIES;  -- 如果第4行薪资相同,也会返回

六、聚合函数 #

6.1 常用聚合函数 #

sql
-- COUNT:计数
SELECT COUNT(*) FROM employees;                    -- 总行数
SELECT COUNT(employee_id) FROM employees;          -- 非NULL行数
SELECT COUNT(DISTINCT department_id) FROM employees; -- 去重计数
SELECT COUNT(commission_pct) FROM employees;       -- 非NULL计数

-- SUM:求和
SELECT SUM(salary) FROM employees;
SELECT SUM(salary * 12) AS annual_total FROM employees;

-- AVG:平均值
SELECT AVG(salary) FROM employees;
SELECT ROUND(AVG(salary), 2) FROM employees;  -- 保留2位小数

-- MAX/MIN:最大/最小值
SELECT MAX(salary), MIN(salary) FROM employees;
SELECT MAX(hire_date), MIN(hire_date) FROM employees;

-- 组合使用
SELECT 
    COUNT(*) AS total_count,
    SUM(salary) AS total_salary,
    ROUND(AVG(salary), 2) AS avg_salary,
    MAX(salary) AS max_salary,
    MIN(salary) AS min_salary
FROM employees;

6.2 条件聚合 #

sql
-- 使用CASE进行条件聚合
SELECT 
    COUNT(*) AS total,
    SUM(CASE WHEN department_id = 10 THEN 1 ELSE 0 END) AS dept_10_count,
    SUM(CASE WHEN department_id = 20 THEN 1 ELSE 0 END) AS dept_20_count,
    SUM(CASE WHEN department_id = 30 THEN 1 ELSE 0 END) AS dept_30_count
FROM employees;

-- 条件求和
SELECT 
    SUM(salary) AS total_salary,
    SUM(CASE WHEN department_id = 10 THEN salary ELSE 0 END) AS dept_10_salary,
    SUM(CASE WHEN department_id = 20 THEN salary ELSE 0 END) AS dept_20_salary
FROM employees;

-- 条件平均
SELECT 
    AVG(CASE WHEN department_id = 10 THEN salary END) AS dept_10_avg,
    AVG(CASE WHEN department_id = 20 THEN salary END) AS dept_20_avg
FROM employees;

七、GROUP BY分组 #

7.1 基本分组 #

sql
-- 按部门分组统计
SELECT 
    department_id,
    COUNT(*) AS employee_count,
    AVG(salary) AS avg_salary,
    MAX(salary) AS max_salary,
    MIN(salary) AS min_salary
FROM employees
GROUP BY department_id
ORDER BY department_id;

-- 多列分组
SELECT 
    department_id,
    job_id,
    COUNT(*) AS count,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id, job_id
ORDER BY department_id, job_id;

7.2 GROUP BY规则 #

sql
-- 规则:SELECT中的非聚合列必须出现在GROUP BY中

-- 正确
SELECT department_id, COUNT(*)
FROM employees
GROUP BY department_id;

-- 错误:first_name不在GROUP BY中
-- SELECT department_id, first_name, COUNT(*)
-- FROM employees
-- GROUP BY department_id;

-- 正确:使用聚合函数
SELECT 
    department_id,
    MAX(first_name) AS first_name,  -- 取最大值
    COUNT(*) AS count
FROM employees
GROUP BY department_id;

7.3 HAVING过滤 #

sql
-- HAVING:过滤分组后的结果
SELECT 
    department_id,
    COUNT(*) AS employee_count,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 1;

-- HAVING与WHERE区别
-- WHERE:过滤行,在分组前执行
-- HAVING:过滤分组,在分组后执行

SELECT 
    department_id,
    COUNT(*) AS count,
    AVG(salary) AS avg_salary
FROM employees
WHERE salary > 5000  -- 先过滤薪资>5000的员工
GROUP BY department_id
HAVING AVG(salary) > 7000  -- 再过滤平均薪资>7000的部门
ORDER BY avg_salary DESC;

7.4 GROUP BY扩展 #

sql
-- ROLLUP:生成分组小计和总计
SELECT 
    department_id,
    job_id,
    COUNT(*) AS count,
    SUM(salary) AS total_salary
FROM employees
GROUP BY ROLLUP(department_id, job_id);

-- 结果包含:
-- 1. 每个部门每个职位的统计
-- 2. 每个部门的小计
-- 3. 总计

-- CUBE:生成所有组合的小计
SELECT 
    department_id,
    job_id,
    COUNT(*) AS count
FROM employees
GROUP BY CUBE(department_id, job_id);

-- GROUPING SETS:指定分组组合
SELECT 
    department_id,
    job_id,
    COUNT(*) AS count
FROM employees
GROUP BY GROUPING SETS(
    (department_id),
    (job_id),
    (department_id, job_id),
    ()
);

-- GROUPING函数:标识是否为聚合行
SELECT 
    GROUPING(department_id) AS is_dept_total,
    department_id,
    GROUPING(job_id) AS is_job_total,
    job_id,
    COUNT(*) AS count
FROM employees
GROUP BY ROLLUP(department_id, job_id);

八、SELECT执行顺序 #

8.1 完整语法 #

sql
SELECT DISTINCT column_list
FROM table_name
[JOIN another_table ON condition]
[WHERE row_condition]
[GROUP BY column_list]
[HAVING group_condition]
[ORDER BY column_list];

8.2 执行顺序 #

text
1. FROM        → 确定数据来源
2. JOIN        → 连接表
3. ON          → 连接条件
4. WHERE       → 过滤行
5. GROUP BY    → 分组
6. HAVING      → 过滤分组
7. SELECT      → 选择列
8. DISTINCT    → 去重
9. ORDER BY    → 排序

8.3 执行顺序示例 #

sql
SELECT department_id, COUNT(*) AS count, AVG(salary) AS avg_salary
FROM employees
WHERE salary >= 5000
GROUP BY department_id
HAVING COUNT(*) > 1
ORDER BY avg_salary DESC;

-- 执行顺序:
-- 1. FROM employees          → 获取employees表数据
-- 2. WHERE salary >= 5000    → 过滤薪资>=5000的行
-- 3. GROUP BY department_id  → 按department_id分组
-- 4. HAVING COUNT(*) > 1     → 过滤分组
-- 5. SELECT ...              → 选择列并计算聚合
-- 6. ORDER BY avg_salary     → 排序

九、DUAL表 #

9.1 DUAL表介绍 #

sql
-- DUAL是Oracle提供的单行单列表
SELECT * FROM DUAL;

-- 常用于计算、获取系统信息
SELECT 1 + 1 FROM DUAL;
SELECT SYSDATE FROM DUAL;
SELECT USER FROM DUAL;

9.2 DUAL表使用 #

sql
-- 计算表达式
SELECT 10 * 5 FROM DUAL;

-- 获取系统日期
SELECT SYSDATE, SYSTIMESTAMP FROM DUAL;

-- 获取当前用户
SELECT USER FROM DUAL;

-- 类型转换测试
SELECT TO_DATE('2024-01-15', 'YYYY-MM-DD') FROM DUAL;

-- 获取序列值
SELECT seq_employee_id.NEXTVAL FROM DUAL;

-- 调用函数
SELECT 
    UPPER('hello'),
    LOWER('HELLO'),
    LENGTH('Oracle'),
    SUBSTR('Oracle', 1, 3)
FROM DUAL;

十、总结 #

查询要点:

子句 说明 示例
SELECT 选择列 SELECT name, salary
FROM 指定表 FROM employees
WHERE 过滤行 WHERE salary > 5000
GROUP BY 分组 GROUP BY department_id
HAVING 过滤分组 HAVING COUNT(*) > 1
ORDER BY 排序 ORDER BY salary DESC
ROWNUM 分页 WHERE ROWNUM <= 10

最佳实践:

  1. 只查询需要的列,避免SELECT *
  2. 使用索引优化WHERE条件
  3. 大表查询使用分页
  4. 合理使用聚合函数和分组
  5. 注意NULL值处理

下一步,让我们学习多表连接!

最后更新:2026-03-27