【Oracle】DDL语言

在这里插入图片描述

个人主页:Guiat
归属专栏:Oracle

在这里插入图片描述

文章目录

  • 1. DDL概述
    • 1.1 什么是DDL?
    • 1.2 DDL的核心功能
  • 2. 表管理
    • 2.1 创建表
    • 2.2 修改表结构
    • 2.3 复杂表操作示例
      • 2.3.1 电商订单系统表设计
      • 2.3.2 金融交易系统表设计
  • 3. 索引管理
    • 3.1 索引类型和创建
    • 3.2 索引维护和优化
    • 3.3 高级索引应用
  • 4. 视图管理
    • 4.1 视图创建和类型
    • 4.2 视图的高级应用
  • 5. 序列管理
    • 5.1 序列创建和使用
    • 5.2 序列的高级应用
  • 6. 存储过程和函数
    • 6.1 存储过程创建
    • 6.2 函数创建
  • 7. 触发器管理
    • 7.1 触发器类型和创建
    • 7.2 触发器管理
  • 8. 表空间管理
    • 8.1 表空间创建和管理
    • 8.2 表空间监控

正文
DDL(Data Definition Language)是Oracle数据库的"建筑师",负责设计和构建数据库的结构。如果说数据库是一座大厦,那DDL就是绘制蓝图、搭建框架的工具。它不仅能创建表、索引、视图等对象,还能修改和删除它们,就像装修房子一样灵活!

1. DDL概述

1.1 什么是DDL?

DDL就像是数据库的"工程师",专门负责数据库结构的设计和管理。它定义了数据如何存储、如何组织,以及各种数据库对象之间的关系。在Oracle这个数据王国里,DDL就是制定规则和建造基础设施的核心工具。

DDL数据定义语言
CREATE创建
ALTER修改
DROP删除
TRUNCATE清空
COMMENT注释
表/视图/索引
结构修改
对象删除
数据清空
添加说明

1.2 DDL的核心功能

Oracle DDL的功能体系就像一个完整的建筑工具箱:

Oracle DDL功能体系
表管理
索引管理
视图管理
序列管理
存储过程管理
触发器管理
表空间管理
创建表结构
修改表结构
删除表
创建索引
重建索引
删除索引
创建视图
修改视图
删除视图
创建序列
修改序列
删除序列
创建存储过程
编译过程
删除过程
创建触发器
启用/禁用
删除触发器
创建表空间
修改表空间
删除表空间

2. 表管理

2.1 创建表

创建表就像设计房间的格局,需要考虑每个字段的类型、大小和约束:

-- 基础表创建
CREATE TABLE employees (
    employee_id    NUMBER(6) PRIMARY KEY,
    first_name     VARCHAR2(20) NOT NULL,
    last_name      VARCHAR2(25) NOT NULL,
    email          VARCHAR2(25) UNIQUE,
    phone_number   VARCHAR2(20),
    hire_date      DATE DEFAULT SYSDATE,
    job_id         VARCHAR2(10) NOT NULL,
    salary         NUMBER(8,2) CHECK (salary > 0),
    commission_pct NUMBER(2,2),
    manager_id     NUMBER(6),
    department_id  NUMBER(4)
);

-- 带详细约束的表创建
CREATE TABLE products (
    product_id     NUMBER(10) GENERATED ALWAYS AS IDENTITY,
    product_name   VARCHAR2(100) NOT NULL,
    description    CLOB,
    category_id    NUMBER(6) NOT NULL,
    supplier_id    NUMBER(6),
    unit_price     NUMBER(10,2) DEFAULT 0 CHECK (unit_price >= 0),
    units_in_stock NUMBER(5) DEFAULT 0 CHECK (units_in_stock >= 0),
    reorder_level  NUMBER(5) DEFAULT 0,
    discontinued   CHAR(1) DEFAULT 'N' CHECK (discontinued IN ('Y', 'N')),
    created_date   TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    modified_date  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    CONSTRAINT pk_products PRIMARY KEY (product_id),
    CONSTRAINT fk_products_category 
        FOREIGN KEY (category_id) REFERENCES categories(category_id),
    CONSTRAINT fk_products_supplier 
        FOREIGN KEY (supplier_id) REFERENCES suppliers(supplier_id)
);

-- 分区表创建
CREATE TABLE sales_data (
    sale_id        NUMBER(12),
    sale_date      DATE NOT NULL,
    customer_id    NUMBER(8) NOT NULL,
    product_id     NUMBER(10) NOT NULL,
    quantity       NUMBER(6) NOT NULL,
    unit_price     NUMBER(10,2) NOT NULL,
    total_amount   NUMBER(12,2) NOT NULL,
    sales_rep_id   NUMBER(6),
    region_id      NUMBER(4)
)
PARTITION BY RANGE (sale_date) (
    PARTITION sales_2023_q1 VALUES LESS THAN (DATE '2023-04-01'),
    PARTITION sales_2023_q2 VALUES LESS THAN (DATE '2023-07-01'),
    PARTITION sales_2023_q3 VALUES LESS THAN (DATE '2023-10-01'),
    PARTITION sales_2023_q4 VALUES LESS THAN (DATE '2024-01-01'),
    PARTITION sales_future VALUES LESS THAN (MAXVALUE)
);

-- 临时表创建
CREATE GLOBAL TEMPORARY TABLE temp_calculations (
    session_id     NUMBER,
    calculation_id NUMBER,
    input_value    NUMBER,
    result_value   NUMBER,
    created_time   TIMESTAMP
) ON COMMIT DELETE ROWS;

-- 外部表创建
CREATE TABLE external_sales_data (
    sale_date      DATE,
    customer_name  VARCHAR2(100),
    product_name   VARCHAR2(100),
    quantity       NUMBER,
    amount         NUMBER(10,2)
)
ORGANIZATION EXTERNAL (
    TYPE ORACLE_LOADER
    DEFAULT DIRECTORY data_dir
    ACCESS PARAMETERS (
        RECORDS DELIMITED BY NEWLINE
        FIELDS TERMINATED BY ','
        OPTIONALLY ENCLOSED BY '"'
    )
    LOCATION ('sales_data.csv')
);

2.2 修改表结构

修改表就像装修房子,可以加房间、改格局、换装饰:

-- 添加新列
ALTER TABLE employees ADD (
    middle_name VARCHAR2(20),
    birth_date DATE,
    emergency_contact VARCHAR2(100)
);

-- 修改列定义
ALTER TABLE employees MODIFY (
    phone_number VARCHAR2(30),
    salary NUMBER(10,2)
);

-- 重命名列
ALTER TABLE employees RENAME COLUMN phone_number TO contact_phone;

-- 删除列
ALTER TABLE employees DROP COLUMN middle_name;

-- 添加约束
ALTER TABLE employees ADD CONSTRAINT chk_hire_date 
    CHECK (hire_date >= DATE '1980-01-01');

-- 删除约束
ALTER TABLE employees DROP CONSTRAINT chk_hire_date;

-- 启用/禁用约束
ALTER TABLE employees DISABLE CONSTRAINT fk_emp_dept;
ALTER TABLE employees ENABLE CONSTRAINT fk_emp_dept;

-- 重命名表
ALTER TABLE employees RENAME TO staff_members;

-- 移动表到不同表空间
ALTER TABLE employees MOVE TABLESPACE new_tablespace;

-- 添加分区
ALTER TABLE sales_data ADD PARTITION sales_2024_q1 
    VALUES LESS THAN (DATE '2024-04-01');

-- 分割分区
ALTER TABLE sales_data SPLIT PARTITION sales_future 
    AT (DATE '2024-07-01') 
    INTO (PARTITION sales_2024_q2, PARTITION sales_future);

2.3 复杂表操作示例

2.3.1 电商订单系统表设计

-- 客户表
CREATE TABLE customers (
    customer_id    NUMBER(10) GENERATED ALWAYS AS IDENTITY,
    customer_code  VARCHAR2(20) NOT NULL UNIQUE,
    company_name   VARCHAR2(100),
    contact_name   VARCHAR2(50) NOT NULL,
    contact_title  VARCHAR2(30),
    address        VARCHAR2(200),
    city           VARCHAR2(50),
    region         VARCHAR2(50),
    postal_code    VARCHAR2(20),
    country        VARCHAR2(50) DEFAULT 'China',
    phone          VARCHAR2(30),
    email          VARCHAR2(100),
    credit_limit   NUMBER(12,2) DEFAULT 0,
    account_status VARCHAR2(20) DEFAULT 'Active' 
                   CHECK (account_status IN ('Active', 'Inactive', 'Suspended')),
    created_date   TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    modified_date  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    CONSTRAINT pk_customers PRIMARY KEY (customer_id),
    CONSTRAINT uk_customers_code UNIQUE (customer_code),
    CONSTRAINT uk_customers_email UNIQUE (email)
);

-- 订单表(按年分区)
CREATE TABLE orders (
    order_id       NUMBER(12) GENERATED ALWAYS AS IDENTITY,
    order_number   VARCHAR2(20) NOT NULL,
    customer_id    NUMBER(10) NOT NULL,
    employee_id    NUMBER(6),
    order_date     DATE DEFAULT SYSDATE,
    required_date  DATE,
    shipped_date   DATE,
    ship_via       NUMBER(4),
    freight        NUMBER(10,2) DEFAULT 0,
    ship_name      VARCHAR2(100),
    ship_address   VARCHAR2(200),
    ship_city      VARCHAR2(50),
    ship_region    VARCHAR2(50),
    ship_postal_code VARCHAR2(20),
    ship_country   VARCHAR2(50),
    order_status   VARCHAR2(20) DEFAULT 'Pending'
                   CHECK (order_status IN ('Pending', 'Processing', 'Shipped', 'Delivered', 'Cancelled')),
    total_amount   NUMBER(12,2) DEFAULT 0,
    
    CONSTRAINT pk_orders PRIMARY KEY (order_id, order_date),
    CONSTRAINT fk_orders_customer 
        FOREIGN KEY (customer_id) REFERENCES customers(customer_id),
    CONSTRAINT uk_orders_number UNIQUE (order_number),
    CONSTRAINT chk_orders_dates CHECK (required_date >= order_date)
)
PARTITION BY RANGE (order_date) (
    PARTITION orders_2022 VALUES LESS THAN (DATE '2023-01-01'),
    PARTITION orders_2023 VALUES LESS THAN (DATE '2024-01-01'),
    PARTITION orders_2024 VALUES LESS THAN (DATE '2025-01-01'),
    PARTITION orders_future VALUES LESS THAN (MAXVALUE)
);

-- 订单明细表
CREATE TABLE order_details (
    order_id       NUMBER(12),
    order_date     DATE,
    product_id     NUMBER(10),
    unit_price     NUMBER(10,2) NOT NULL,
    quantity       NUMBER(6) NOT NULL CHECK (quantity > 0),
    discount       NUMBER(4,3) DEFAULT 0 CHECK (discount BETWEEN 0 AND 1),
    line_total     NUMBER(12,2) GENERATED ALWAYS AS (
                       ROUND(unit_price * quantity * (1 - discount), 2)
                   ),
    
    CONSTRAINT pk_order_details PRIMARY KEY (order_id, order_date, product_id),
    CONSTRAINT fk_order_details_order 
        FOREIGN KEY (order_id, order_date) REFERENCES orders(order_id, order_date),
    CONSTRAINT fk_order_details_product 
        FOREIGN KEY (product_id) REFERENCES products(product_id)
)
PARTITION BY RANGE (order_date) (
    PARTITION order_details_2022 VALUES LESS THAN (DATE '2023-01-01'),
    PARTITION order_details_2023 VALUES LESS THAN (DATE '2024-01-01'),
    PARTITION order_details_2024 VALUES LESS THAN (DATE '2025-01-01'),
    PARTITION order_details_future VALUES LESS THAN (MAXVALUE)
);

2.3.2 金融交易系统表设计

-- 账户表
CREATE TABLE accounts (
    account_id     NUMBER(12) GENERATED ALWAYS AS IDENTITY,
    account_number VARCHAR2(20) NOT NULL UNIQUE,
    customer_id    NUMBER(10) NOT NULL,
    account_type   VARCHAR2(20) NOT NULL 
                   CHECK (account_type IN ('Savings', 'Checking', 'Credit', 'Investment')),
    currency_code  CHAR(3) DEFAULT 'CNY',
    balance        NUMBER(15,2) DEFAULT 0,
    available_balance NUMBER(15,2) DEFAULT 0,
    credit_limit   NUMBER(15,2) DEFAULT 0,
    interest_rate  NUMBER(5,4) DEFAULT 0,
    account_status VARCHAR2(20) DEFAULT 'Active'
                   CHECK (account_status IN ('Active', 'Inactive', 'Frozen', 'Closed')),
    opened_date    DATE DEFAULT SYSDATE,
    closed_date    DATE,
    last_transaction_date DATE,
    
    CONSTRAINT pk_accounts PRIMARY KEY (account_id),
    CONSTRAINT fk_accounts_customer 
        FOREIGN KEY (customer_id) REFERENCES customers(customer_id),
    CONSTRAINT chk_accounts_balance CHECK (
        CASE account_type 
            WHEN 'Credit' THEN balance >= -credit_limit
            ELSE balance >= 0
        END = 1
    ),
    CONSTRAINT chk_accounts_dates CHECK (closed_date >= opened_date)
);

-- 交易表(按月分区,包含子分区)
CREATE TABLE transactions (
    transaction_id   NUMBER(15) GENERATED ALWAYS AS IDENTITY,
    account_id       NUMBER(12) NOT NULL,
    transaction_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    transaction_type VARCHAR2(20) NOT NULL
                     CHECK (transaction_type IN ('Deposit', 'Withdrawal', 'Transfer', 'Payment', 'Fee')),
    amount           NUMBER(15,2) NOT NULL CHECK (amount != 0),
    balance_after    NUMBER(15,2) NOT NULL,
    description      VARCHAR2(200),
    reference_number VARCHAR2(50),
    related_account_id NUMBER(12),
    channel          VARCHAR2(20) DEFAULT 'Branch'
                     CHECK (channel IN ('Branch', 'ATM', 'Online', 'Mobile', 'Phone')),
    status           VARCHAR2(20) DEFAULT 'Completed'
                     CHECK (status IN ('Pending', 'Completed', 'Failed', 'Reversed')),
    created_by       VARCHAR2(30) DEFAULT USER,
    
    CONSTRAINT pk_transactions PRIMARY KEY (transaction_id, transaction_date),
    CONSTRAINT fk_transactions_account 
        FOREIGN KEY (account_id) REFERENCES accounts(account_id),
    CONSTRAINT fk_transactions_related 
        FOREIGN KEY (related_account_id) REFERENCES accounts(account_id)
)
PARTITION BY RANGE (transaction_date)
SUBPARTITION BY HASH (account_id) SUBPARTITIONS 4 (
    PARTITION trans_2024_01 VALUES LESS THAN (TIMESTAMP '2024-02-01 00:00:00'),
    PARTITION trans_2024_02 VALUES LESS THAN (TIMESTAMP '2024-03-01 00:00:00'),
    PARTITION trans_2024_03 VALUES LESS THAN (TIMESTAMP '2024-04-01 00:00:00'),
    PARTITION trans_future VALUES LESS THAN (MAXVALUE)
);

3. 索引管理

3.1 索引类型和创建

索引就像书的目录,帮助快速找到需要的信息:

Oracle索引类型
B-Tree索引
位图索引
函数索引
分区索引
复合索引
唯一索引
默认索引类型
适合高选择性
适合低选择性
数据仓库场景
基于表达式
支持函数查询
本地分区索引
全局分区索引
多列组合
优化复合查询
保证唯一性
主键外键
-- 基本B-Tree索引
CREATE INDEX idx_employees_last_name ON employees(last_name);

-- 复合索引
CREATE INDEX idx_employees_dept_job ON employees(department_id, job_id);

-- 唯一索引
CREATE UNIQUE INDEX idx_employees_email ON employees(email);

-- 函数索引
CREATE INDEX idx_employees_upper_name ON employees(UPPER(last_name));

-- 部分索引(带WHERE条件)
CREATE INDEX idx_active_employees ON employees(employee_id) 
WHERE status = 'ACTIVE';

-- 降序索引
CREATE INDEX idx_employees_salary_desc ON employees(salary DESC);

-- 位图索引(适合数据仓库)
CREATE BITMAP INDEX idx_products_category ON products(category_id);

-- 分区索引
CREATE INDEX idx_sales_date ON sales_data(sale_date) LOCAL;

-- 全局分区索引
CREATE INDEX idx_sales_customer ON sales_data(customer_id)
GLOBAL PARTITION BY RANGE (customer_id) (
    PARTITION idx_cust_1_1000 VALUES LESS THAN (1001),
    PARTITION idx_cust_1001_2000 VALUES LESS THAN (2001),
    PARTITION idx_cust_others VALUES LESS THAN (MAXVALUE)
);

-- 反向键索引(避免热块)
CREATE INDEX idx_orders_id_reverse ON orders(order_id) REVERSE;

-- 压缩索引
CREATE INDEX idx_employees_dept_job_comp ON employees(department_id, job_id)
COMPRESS 1;

3.2 索引维护和优化

-- 重建索引
ALTER INDEX idx_employees_last_name REBUILD;

-- 重建索引到新表空间
ALTER INDEX idx_employees_last_name REBUILD TABLESPACE new_index_ts;

-- 在线重建索引
ALTER INDEX idx_employees_last_name REBUILD ONLINE;

-- 收集索引统计信息
ANALYZE INDEX idx_employees_last_name COMPUTE STATISTICS;

-- 或使用DBMS_STATS
EXEC DBMS_STATS.GATHER_INDEX_STATS('HR', 'IDX_EMPLOYEES_LAST_NAME');

-- 监控索引使用情况
ALTER INDEX idx_employees_last_name MONITORING USAGE;

-- 查看索引使用情况
SELECT * FROM v$object_usage WHERE index_name = 'IDX_EMPLOYEES_LAST_NAME';

-- 检查索引碎片
SELECT index_name, blevel, leaf_blocks, distinct_keys, clustering_factor
FROM user_indexes
WHERE table_name = 'EMPLOYEES';

-- 删除未使用的索引
DROP INDEX idx_unused_index;

-- 使索引不可见(测试性能影响)
ALTER INDEX idx_employees_last_name INVISIBLE;
ALTER INDEX idx_employees_last_name VISIBLE;

3.3 高级索引应用

-- 虚拟列索引
ALTER TABLE products ADD (
    price_category VARCHAR2(10) GENERATED ALWAYS AS (
        CASE 
            WHEN unit_price < 10 THEN 'Low'
            WHEN unit_price < 100 THEN 'Medium'
            ELSE 'High'
        END
    )
);

CREATE INDEX idx_products_price_cat ON products(price_category);

-- JSON数据索引
CREATE TABLE json_documents (
    id NUMBER PRIMARY KEY,
    doc_data CLOB CHECK (doc_data IS JSON)
);

CREATE INDEX idx_json_name ON json_documents(
    JSON_VALUE(doc_data, '$.name' RETURNING VARCHAR2(100))
);

-- 全文索引
CREATE INDEX idx_products_text ON products(description)
INDEXTYPE IS CTXSYS.CONTEXT;

-- 空间索引
CREATE INDEX idx_locations_spatial ON locations(geometry)
INDEXTYPE IS MDSYS.SPATIAL_INDEX;

4. 视图管理

4.1 视图创建和类型

视图就像是数据的"窗口",提供不同的观察角度:

-- 简单视图
CREATE VIEW employee_summary AS
SELECT employee_id, first_name, last_name, department_id, salary
FROM employees
WHERE status = 'ACTIVE';

-- 复杂视图(多表连接)
CREATE VIEW employee_details AS
SELECT 
    e.employee_id,
    e.first_name || ' ' || e.last_name AS full_name,
    e.email,
    e.hire_date,
    d.department_name,
    j.job_title,
    e.salary,
    m.first_name || ' ' || m.last_name AS manager_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
LEFT JOIN jobs j ON e.job_id = j.job_id
LEFT JOIN employees m ON e.manager_id = m.employee_id
WHERE e.status = 'ACTIVE';

-- 聚合视图
CREATE VIEW department_statistics AS
SELECT 
    d.department_id,
    d.department_name,
    COUNT(e.employee_id) AS employee_count,
    AVG(e.salary) AS avg_salary,
    MIN(e.salary) AS min_salary,
    MAX(e.salary) AS max_salary,
    SUM(e.salary) AS total_salary
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name;

-- 可更新视图
CREATE VIEW active_employees AS
SELECT employee_id, first_name, last_name, email, salary, department_id
FROM employees
WHERE status = 'ACTIVE'
WITH CHECK OPTION;

-- 只读视图
CREATE VIEW salary_report AS
SELECT 
    department_id,
    job_id,
    COUNT(*) AS emp_count,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id, job_id
WITH READ ONLY;

-- 物化视图
CREATE MATERIALIZED VIEW mv_monthly_sales
BUILD IMMEDIATE
REFRESH COMPLETE ON DEMAND
AS
SELECT 
    EXTRACT(YEAR FROM order_date) AS year,
    EXTRACT(MONTH FROM order_date) AS month,
    customer_id,
    SUM(total_amount) AS monthly_total,
    COUNT(*) AS order_count
FROM orders
WHERE order_date >= ADD_MONTHS(SYSDATE, -24)
GROUP BY EXTRACT(YEAR FROM order_date), EXTRACT(MONTH FROM order_date), customer_id;

4.2 视图的高级应用

-- 分层查询视图
CREATE VIEW employee_hierarchy AS
SELECT 
    employee_id,
    first_name || ' ' || last_name AS full_name,
    manager_id,
    LEVEL AS hierarchy_level,
    SYS_CONNECT_BY_PATH(first_name || ' ' || last_name, ' -> ') AS path
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR employee_id = manager_id
ORDER SIBLINGS BY last_name;

-- 分析函数视图
CREATE VIEW employee_rankings AS
SELECT 
    employee_id,
    first_name,
    last_name,
    department_id,
    salary,
    RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_salary_rank,
    DENSE_RANK() OVER (ORDER BY salary DESC) AS overall_salary_rank,
    NTILE(4) OVER (ORDER BY salary) AS salary_quartile,
    LAG(salary) OVER (PARTITION BY department_id ORDER BY hire_date) AS prev_salary,
    salary - LAG(salary) OVER (PARTITION BY department_id ORDER BY hire_date) AS salary_diff
FROM employees;

-- 透视视图
CREATE VIEW sales_pivot AS
SELECT *
FROM (
    SELECT 
        EXTRACT(YEAR FROM order_date) AS year,
        EXTRACT(MONTH FROM order_date) AS month,
        total_amount
    FROM orders
)
PIVOT (
    SUM(total_amount)
    FOR month IN (1 AS Jan, 2 AS Feb, 3 AS Mar, 4 AS Apr, 5 AS May, 6 AS Jun,
                  7 AS Jul, 8 AS Aug, 9 AS Sep, 10 AS Oct, 11 AS Nov, 12 AS Dec)
);

-- 递归视图(Oracle 11g R2+)
CREATE VIEW category_tree AS
WITH category_hierarchy (category_id, category_name, parent_id, level_num, path) AS (
    -- 根节点
    SELECT category_id, category_name, parent_id, 1, category_name
    FROM categories
    WHERE parent_id IS NULL
    
    UNION ALL
    
    -- 递归部分
    SELECT c.category_id, c.category_name, c.parent_id, ch.level_num + 1,
           ch.path || ' -> ' || c.category_name
    FROM categories c
    JOIN category_hierarchy ch ON c.parent_id = ch.category_id
)
SELECT * FROM category_hierarchy;

5. 序列管理

5.1 序列创建和使用

序列就像是自动编号机,为表提供唯一的数字:

-- 基本序列创建
CREATE SEQUENCE emp_seq
START WITH 1000
INCREMENT BY 1
MAXVALUE 999999
MINVALUE 1
NOCYCLE
CACHE 20;

-- 复杂序列配置
CREATE SEQUENCE order_seq
START WITH 100000
INCREMENT BY 1
MAXVALUE 9999999999
MINVALUE 100000
CYCLE
CACHE 100
ORDER;

-- 使用序列
INSERT INTO employees (employee_id, first_name, last_name, hire_date)
VALUES (emp_seq.NEXTVAL, 'John', 'Doe', SYSDATE);

-- 查看序列当前值
SELECT emp_seq.CURRVAL FROM dual;

-- 修改序列
ALTER SEQUENCE emp_seq
INCREMENT BY 5
MAXVALUE 9999999
CACHE 50;

-- 重置序列(通过修改实现)
ALTER SEQUENCE emp_seq RESTART START WITH 1;

-- 删除序列
DROP SEQUENCE emp_seq;

5.2 序列的高级应用

-- 为不同表创建专用序列
CREATE SEQUENCE customer_id_seq START WITH 10000 INCREMENT BY 1;
CREATE SEQUENCE order_id_seq START WITH 100000 INCREMENT BY 1;
CREATE SEQUENCE product_id_seq START WITH 1000 INCREMENT BY 1;

-- 创建带前缀的序列号生成函数
CREATE OR REPLACE FUNCTION generate_order_number
RETURN VARCHAR2
IS
    seq_val NUMBER;
BEGIN
    SELECT order_seq.NEXTVAL INTO seq_val FROM dual;
    RETURN 'ORD' || TO_CHAR(SYSDATE, 'YYYYMMDD') || LPAD(seq_val, 6, '0');
END;
/

-- 使用函数生成订单号
INSERT INTO orders (order_id, order_number, customer_id, order_date)
VALUES (order_id_seq.NEXTVAL, generate_order_number(), 12345, SYSDATE);

-- 查看序列信息
SELECT sequence_name, min_value, max_value, increment_by, 
       cycle_flag, cache_size, last_number
FROM user_sequences;

6. 存储过程和函数

6.1 存储过程创建

存储过程就像是预先写好的"脚本",可以重复执行复杂的业务逻辑:

-- 简单存储过程
CREATE OR REPLACE PROCEDURE update_employee_salary(
    p_employee_id IN NUMBER,
    p_new_salary IN NUMBER
)
IS
BEGIN
    UPDATE employees 
    SET salary = p_new_salary,
        modified_date = SYSDATE
    WHERE employee_id = p_employee_id;
    
    IF SQL%ROWCOUNT = 0 THEN
        RAISE_APPLICATION_ERROR(-20001, 'Employee not found');
    END IF;
    
    COMMIT;
END;
/

-- 复杂存储过程(带异常处理)
CREATE OR REPLACE PROCEDURE process_monthly_payroll(
    p_department_id IN NUMBER DEFAULT NULL,
    p_process_date IN DATE DEFAULT SYSDATE
)
IS
    v_total_processed NUMBER := 0;
    v_total_amount NUMBER := 0;
    v_error_count NUMBER := 0;
    
    CURSOR emp_cursor IS
        SELECT employee_id, salary, commission_pct
        FROM employees
        WHERE (p_department_id IS NULL OR department_id = p_department_id)
          AND status = 'ACTIVE';
          
    TYPE emp_array IS TABLE OF emp_cursor%ROWTYPE;
    emp_list emp_array;
    
BEGIN
    -- 批量获取员工数据
    OPEN emp_cursor;
    FETCH emp_cursor BULK COLLECT INTO emp_list;
    CLOSE emp_cursor;
    
    -- 处理每个员工
    FOR i IN 1..emp_list.COUNT LOOP
        BEGIN
            -- 计算薪资
            DECLARE
                v_gross_pay NUMBER;
                v_net_pay NUMBER;
            BEGIN
                v_gross_pay := emp_list(i).salary + 
                              NVL(emp_list(i).commission_pct * emp_list(i).salary, 0);
                v_net_pay := v_gross_pay * 0.8; -- 简化的税收计算
                
                -- 插入薪资记录
                INSERT INTO payroll_history (
                    employee_id, pay_date, gross_pay, net_pay, created_date
                ) VALUES (
                    emp_list(i).employee_id, p_process_date, 
                    v_gross_pay, v_net_pay, SYSDATE
                );
                
                v_total_processed := v_total_processed + 1;
                v_total_amount := v_total_amount + v_net_pay;
            END;
            
        EXCEPTION
            WHEN OTHERS THEN
                v_error_count := v_error_count + 1;
                -- 记录错误日志
                INSERT INTO error_log (
                    error_date, error_message, employee_id
                ) VALUES (
                    SYSDATE, SQLERRM, emp_list(i).employee_id
                );
        END;
    END LOOP;
    
    -- 记录处理摘要
    INSERT INTO payroll_summary (
        process_date, employees_processed, total_amount, error_count
    ) VALUES (
        p_process_date, v_total_processed, v_total_amount, v_error_count
    );
    
    COMMIT;
    
    DBMS_OUTPUT.PUT_LINE('Payroll processed: ' || v_total_processed || ' employees');
    DBMS_OUTPUT.PUT_LINE('Total amount: ' || v_total_amount);
    DBMS_OUTPUT.PUT_LINE('Errors: ' || v_error_count);
    
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE_APPLICATION_ERROR(-20002, 'Payroll processing failed: ' || SQLERRM);
END;
/

6.2 函数创建

-- 简单函数
CREATE OR REPLACE FUNCTION get_employee_age(
    p_employee_id IN NUMBER
) RETURN NUMBER
IS
    v_birth_date DATE;
    v_age NUMBER;
BEGIN
    SELECT birth_date INTO v_birth_date
    FROM employees
    WHERE employee_id = p_employee_id;
    
    v_age := FLOOR(MONTHS_BETWEEN(SYSDATE, v_birth_date) / 12);
    
    RETURN v_age;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        RETURN NULL;
END;
/

-- 表函数(返回结果集)
CREATE OR REPLACE FUNCTION get_department_employees(
    p_department_id IN NUMBER
) RETURN SYS_REFCURSOR
IS
    emp_cursor SYS_REFCURSOR;
BEGIN
    OPEN emp_cursor FOR
        SELECT employee_id, first_name, last_name, salary
        FROM employees
        WHERE department_id = p_department_id
        ORDER BY last_name;
    
    RETURN emp_cursor;
END;
/

-- 管道表函数
CREATE OR REPLACE TYPE emp_summary_type AS OBJECT (
    department_id NUMBER,
    department_name VARCHAR2(100),
    employee_count NUMBER,
    avg_salary NUMBER
);
/

CREATE OR REPLACE TYPE emp_summary_table AS TABLE OF emp_summary_type;
/

CREATE OR REPLACE FUNCTION get_department_summary
RETURN emp_summary_table PIPELINED
IS
    v_summary emp_summary_type;
BEGIN
    FOR rec IN (
        SELECT d.department_id, d.department_name,
               COUNT(e.employee_id) as emp_count,
               AVG(e.salary) as avg_sal
        FROM departments d
        LEFT JOIN employees e ON d.department_id = e.department_id
        GROUP BY d.department_id, d.department_name
    ) LOOP
        v_summary := emp_summary_type(
            rec.department_id, rec.department_name, 
            rec.emp_count, rec.avg_sal
        );
        PIPE ROW(v_summary);
    END LOOP;
    
    RETURN;
END;
/

-- 使用管道表函数
SELECT * FROM TABLE(get_department_summary());

7. 触发器管理

7.1 触发器类型和创建

触发器就像是数据库的"守卫",在特定事件发生时自动执行:

Oracle触发器类型
DML触发器
DDL触发器
系统触发器
BEFORE触发器
AFTER触发器
INSTEAD OF触发器
CREATE触发器
ALTER触发器
DROP触发器
LOGON触发器
LOGOFF触发器
STARTUP触发器
-- 审计触发器
CREATE OR REPLACE TRIGGER trg_employees_audit
    AFTER INSERT OR UPDATE OR DELETE ON employees
    FOR EACH ROW
DECLARE
    v_action VARCHAR2(10);
BEGIN
    CASE
        WHEN INSERTING THEN v_action := 'INSERT';
        WHEN UPDATING THEN v_action := 'UPDATE';
        WHEN DELETING THEN v_action := 'DELETE';
    END CASE;
    
    INSERT INTO audit_log (
        table_name, action_type, record_id, 
        old_values, new_values, action_date, action_user
    ) VALUES (
        'EMPLOYEES', v_action, 
        COALESCE(:NEW.employee_id, :OLD.employee_id),
        CASE WHEN DELETING THEN 
            :OLD.first_name || '|' || :OLD.last_name || '|' || :OLD.salary
        END,
        CASE WHEN NOT DELETING THEN 
            :NEW.first_name || '|' || :NEW.last_name || '|' || :NEW.salary
        END,
        SYSDATE, USER
    );
END;
/

-- 自动更新时间戳触发器
CREATE OR REPLACE TRIGGER trg_employees_timestamp
    BEFORE UPDATE ON employees
    FOR EACH ROW
BEGIN
    :NEW.modified_date := SYSDATE;
    :NEW.modified_by := USER;
END;
/

-- 业务规则触发器
CREATE OR REPLACE TRIGGER trg_salary_validation
    BEFORE INSERT OR UPDATE OF salary ON employees
    FOR EACH ROW
DECLARE
    v_max_salary NUMBER;
    v_min_salary NUMBER;
BEGIN
    -- 获取职位的薪资范围
    SELECT min_salary, max_salary
    INTO v_min_salary, v_max_salary
    FROM jobs
    WHERE job_id = :NEW.job_id;
    
    -- 验证薪资范围
    IF :NEW.salary < v_min_salary OR :NEW.salary > v_max_salary THEN
        RAISE_APPLICATION_ERROR(-20003, 
            'Salary must be between ' || v_min_salary || ' and ' || v_max_salary);
    END IF;
    
    -- 验证薪资增长不超过20%
    IF UPDATING AND :NEW.salary > :OLD.salary * 1.2 THEN
        RAISE_APPLICATION_ERROR(-20004, 
            'Salary increase cannot exceed 20%');
    END IF;
END;
/

-- 复合触发器(Oracle 11g+)
CREATE OR REPLACE TRIGGER trg_order_processing
    FOR INSERT OR UPDATE OR DELETE ON orders
    COMPOUND TRIGGER
    
    -- 声明变量
    TYPE order_id_array IS TABLE OF NUMBER;
    g_order_ids order_id_array := order_id_array();
    
    -- BEFORE STATEMENT
    BEFORE STATEMENT IS
    BEGIN
        DBMS_OUTPUT.PUT_LINE('Starting order processing...');
    END BEFORE STATEMENT;
    
    -- BEFORE EACH ROW
    BEFORE EACH ROW IS
    BEGIN
        IF INSERTING THEN
            :NEW.order_number := 'ORD' || TO_CHAR(SYSDATE, 'YYYYMMDD') || 
                                LPAD(order_seq.NEXTVAL, 6, '0');
            :NEW.created_date := SYSDATE;
        ELSIF UPDATING THEN
            :NEW.modified_date := SYSDATE;
        END IF;
    END BEFORE EACH ROW;
    
    -- AFTER EACH ROW
    AFTER EACH ROW IS
    BEGIN
        g_order_ids.EXTEND;
        g_order_ids(g_order_ids.COUNT) := 
            COALESCE(:NEW.order_id, :OLD.order_id);
    END AFTER EACH ROW;
    
    -- AFTER STATEMENT
    AFTER STATEMENT IS
    BEGIN
        -- 批量更新相关统计
        FOR i IN 1..g_order_ids.COUNT LOOP
            update_customer_statistics(g_order_ids(i));
        END LOOP;
        
        DBMS_OUTPUT.PUT_LINE('Processed ' || g_order_ids.COUNT || ' orders');
    END AFTER STATEMENT;
    
END trg_order_processing;
/

-- DDL触发器
CREATE OR REPLACE TRIGGER trg_ddl_audit
    AFTER CREATE OR ALTER OR DROP ON SCHEMA
BEGIN
    INSERT INTO ddl_audit_log (
        event_type, object_type, object_name, 
        sql_text, event_date, username
    ) VALUES (
        SYS.SYSEVENT, SYS.DICTIONARY_OBJ_TYPE, SYS.DICTIONARY_OBJ_NAME,
        SYS.SQL_TEXT, SYSDATE, USER
    );
END;
/

7.2 触发器管理

-- 禁用触发器
ALTER TRIGGER trg_employees_audit DISABLE;

-- 启用触发器
ALTER TRIGGER trg_employees_audit ENABLE;

-- 禁用表上所有触发器
ALTER TABLE employees DISABLE ALL TRIGGERS;

-- 启用表上所有触发器
ALTER TABLE employees ENABLE ALL TRIGGERS;

-- 删除触发器
DROP TRIGGER trg_employees_audit;

-- 查看触发器信息
SELECT trigger_name, trigger_type, triggering_event, status
FROM user_triggers
WHERE table_name = 'EMPLOYEES';

-- 查看触发器源码
SELECT trigger_body
FROM user_triggers
WHERE trigger_name = 'TRG_EMPLOYEES_AUDIT';

8. 表空间管理

8.1 表空间创建和管理

表空间就像是数据库的"仓库",存储各种数据库对象:

-- 创建永久表空间
CREATE TABLESPACE sales_data
DATAFILE '/u01/app/oracle/oradata/orcl/sales_data01.dbf' SIZE 100M
AUTOEXTEND ON NEXT 10M MAXSIZE 1G
EXTENT MANAGEMENT LOCAL
SEGMENT SPACE MANAGEMENT AUTO;

-- 创建临时表空间
CREATE TEMPORARY TABLESPACE temp_large
TEMPFILE '/u01/app/oracle/oradata/orcl/temp_large01.dbf' SIZE 500M
AUTOEXTEND ON NEXT 50M MAXSIZE 2G;

-- 创建撤销表空间
CREATE UNDO TABLESPACE undo_large
DATAFILE '/u01/app/oracle/oradata/orcl/undo_large01.dbf' SIZE 200M
AUTOEXTEND ON NEXT 20M MAXSIZE 1G;

-- 添加数据文件
ALTER TABLESPACE sales_data
ADD DATAFILE '/u01/app/oracle/oradata/orcl/sales_data02.dbf' SIZE 100M;

-- 调整数据文件大小
ALTER DATABASE DATAFILE '/u01/app/oracle/oradata/orcl/sales_data01.dbf'
RESIZE 200M;

-- 设置自动扩展
ALTER DATABASE DATAFILE '/u01/app/oracle/oradata/orcl/sales_data01.dbf'
AUTOEXTEND ON NEXT 10M MAXSIZE 500M;

-- 表空间离线/在线
ALTER TABLESPACE sales_data OFFLINE;
ALTER TABLESPACE sales_data ONLINE;

-- 表空间只读/读写
ALTER TABLESPACE sales_data READ ONLY;
ALTER TABLESPACE sales_data READ WRITE;

-- 删除表空间
DROP TABLESPACE old_tablespace INCLUDING CONTENTS AND DATAFILES;

8.2 表空间监控

-- 查看表空间使用情况
SELECT 
    ts.tablespace_name,
    ROUND(ts.total_mb, 2) AS total_mb,
    ROUND(ts.used_mb, 2) AS used_mb,
    ROUND(ts.free_mb, 2) AS free_mb,
    ROUND((ts.used_mb / ts.total_mb) * 100, 2) AS used_percent
FROM (
    SELECT 
        tablespace_name,
        SUM(bytes) / 1024 / 1024 AS total_mb,
        SUM(bytes) / 1024 / 1024 - NVL(free.free_mb, 0) AS used_mb,
        NVL(free.free_mb, 0) AS free_mb
    FROM dba_data_files df
    LEFT JOIN (
        SELECT 
            tablespace_name,
            SUM(bytes) / 1024 / 1024 AS free_mb
        FROM dba_free_space
        GROUP BY tablespace_name
    ) free ON df.tablespace_name = free.tablespace_name
    GROUP BY df.tablespace_name, free.free_mb
) ts
ORDER BY used_percent DESC;

-- 查看数据文件信息
SELECT 
    file_name,
    tablespace_name,
    ROUND(bytes / 1024 / 1024, 2) AS size_mb,
    ROUND(maxbytes / 1024 / 1024, 2) AS max_size_mb,
    autoextensible,
    status
FROM dba_data_files
ORDER BY tablespace_name, file_id;

结语
感谢您的阅读!期待您的一键三连!欢迎指正!

在这里插入图片描述

你可能感兴趣的:(Oracle,oracle,数据库)