Oracle 《数据库 2 天开发人员指南》第10章:部署Oracle数据库应用程序

本文为2 Day Developer's Guide 第10章 Deploying an Oracle Database Application 的笔记。

开发应用程序后,您可以将它安装在其他数据库(称为部署环境)上,其他用户可以在其中运行它。

10.1 关于开发和部署环境

您在其上开发应用程序的数据库称为开发环境。 开发应用程序后,您可以将其安装在其他数据库(称为部署环境)上,其他用户可以在其中运行它。

第一个部署环境是测试环境。 在测试环境中,您可以彻底测试应用程序的功能,确定其结构是否正确,并在将其部署到生产环境之前解决所有问题。

您还可以在将应用程序部署到生产环境之前或之后将其部署到教育环境。 教育环境为用户提供了在不影响其他环境的情况下练习运行应用程序的场所。

如果您的组织中不存在所需的部署环境,您可以创建它们。

10.2 关于安装脚本

安装脚本可以包含创建应用程序所需的所有 SQL 语句,也可以是运行其他脚本的主脚本。

脚本是文件名以 .sql 结尾的一系列 SQL 语句(例如,create_app.sql)。 当您在 SQL*Plus 或 SQL Developer 等客户端程序中运行脚本时,SQL 语句将按照它们在脚本中出现的顺序运行。 其 SQL 语句创建应用程序的脚本称为安装脚本。

要部署应用程序,您需要在部署环境中运行一个或多个安装脚本。 对于新应用程序,您必须创建安装脚本。 对于较旧的应用程序,安装脚本可能存在,但如果不存在,您可以创建它们。

10.2.1 关于 DDL 语句和模式对象依赖关系

安装脚本包含创建架构对象的 DDL 语句,以及可选的将数据加载到 DDL 语句创建的表中的 INSERT 语句。 要正确创建安装脚本并以正确的顺序运行多个安装脚本,您必须了解应用程序的架构对象之间的依赖关系。

如果对象 A 的定义引用对象 B,则 A 依赖于 B。因此,您必须在创建 A 之前创建 B。否则,创建 B 的语句要么失败,要么创建处于无效状态的 B,具体取决于对象类型。

对于复杂的应用程序,创建对象的顺序很少是显而易见的。 通常,您必须咨询数据库设计者或设计图。

10.2.2 关于 INSERT 语句和约束

当您运行包含 INSERT 语句的安装脚本时,您必须确定在将源表(在开发环境中)中的数据插入到部署环境中的新表中时是否会违反约束。

对于应用程序中的每个源表,您必须确定将其数据插入新表时是否会违反任何约束。 如果是这样,您必须首先禁用那些约束,然后插入数据,然后尝试重新启用约束。 如果数据项违反约束,则在更正数据项之前无法重新启用该约束。

如果您只是以正确的顺序插入查找数据(如"加载数据"),则不会违反约束。 因此,您不需要先禁用它们。

如果您要从外部源(例如文件、电子表格或较旧的应用程序)或从许多具有大量相关数据的表中插入数据,请在插入数据之前禁用约束(是为性能考虑吗???)。

禁用和重新启用约束的一些可能方法是:

  • 使用 SQL Developer,一次禁用和重新启用一个约束:
  • 编辑安装脚本,添加禁用和重新启用每个约束的 SQL 语句。
  • 使用禁用和启用每个约束的 SQL 语句创建 SQL 脚本。
  • 在 Oracle 数据库数据字典中查找约束,并使用 SQL 语句创建 SQL 脚本以禁用和启用每个约束。

例如,要从"创建表"中查找并启用 EVALUATIONS、PERFORMANCE_PARTS 和 SCORES 表中使用的约束,请输入以下语句:

sql 复制代码
SELECT 'ALTER TABLE '|| TABLE_NAME || ' DISABLE CONSTRAINT '|| 
  CONSTRAINT_NAME ||';'
  FROM user_constraints
 WHERE table_name IN ('EVALUATIONS','PERFORMANCE_PARTS','SCORES');
 
SELECT 'ALTER TABLE '|| TABLE_NAME || ' ENABLE CONSTRAINT '|| 
  CONSTRAINT_NAME ||';'
  FROM user_constraints
 WHERE table_name IN ('EVALUATIONS','PERFORMANCE_PARTS','SCORES');

10.3 创建安装脚本

您可以在 SQL Developer 或文本编辑器中创建安装脚本。

如果安装脚本只需要 DDL 和 INSERT 语句,那么您可以使用 SQL Developer 或任何文本编辑器创建它。 在 SQL Developer 中,您可以使用购物车或数据库导出向导。 对于希望在多个部署环境中运行的安装脚本,Oracle 建议使用 Cart,对于希望仅在一个部署环境中运行的安装脚本,建议使用数据库导出向导。

如果安装脚本需要既不是 DDL 也不是 INSERT 语句的 SQL 语句,那么您必须使用文本编辑器创建它。

本节说明如何使用购物车和数据库导出向导创建安装脚本,何时以及如何编辑创建序列和触发器的安装脚本,以及如何为开发简单 Oracle 数据库应用程序("示例应用程序"中的应用程序创建安装脚本 ")。

10.3.1 使用购物车创建安装脚本

SQL Developer Cart 是一种方便的工具,用于将 Oracle 数据库对象从一个或多个数据库连接部署到目标连接。

您将对象从导航器框架拖放到购物车窗口中,指定所需的选项,然后单击导出购物车图标以显示导出对象对话框。 完成该对话框中的信息后,SQL Developer 会创建一个包含脚本(包括主脚本)的 .zip 文件,以在所需目标连接的架构中创建对象。

可以考虑将HR Sample Schema导出为安装脚本

10.3.2 使用数据库导出向导创建安装脚本

要使用数据库导出向导在 SQL Developer 中创建安装脚本,您需要指定安装脚本的名称、要导出的对象和数据以及所需的选项,然后向导会生成一个安装脚本。

SQL Developer菜单为Tools>Database Export

  • 不要取消选择 Terminator,否则安装脚本会失败。
  • 可以选择是否导出数据。
  • 文件名必须以 .sql 结尾。
  • 可以导出部分表
  • 表的数据导出,可以指定部分列,或通过where条件导出部分行

10.3.3 编辑创建序列的安装脚本

如果您的应用程序使用序列生成唯一键,并且您不会将源表中的数据插入到相应的新表中,那么您可能需要编辑安装脚本中的 START WITH 值。

对于一个序列,SQL Developer 生成一个 CREATE SEQUENCE 语句,其 START WITH 值是相对于开发环境中序列的当前值的。

如果您的应用程序使用序列生成唯一键,并且您不会将源表中的数据插入到相应的新表中,那么您可能需要编辑安装脚本中的 START WITH 值。

您可以在工作表或任何文本编辑器中编辑安装脚本。

10.3.4 编辑创建触发器的安装脚本

如果您的应用程序在源表上有一个 BEFORE INSERT 触发器,并且您要将该源表中的数据插入到相应的新表中,则必须决定是否要在安装脚本中的每个 INSERT 语句将数据插入到 新表。

例如,NEW_EVALUATION_TRIGGER(在"教程:创建在插入行之前为行生成主键的触发器"中创建)在将行插入 EVALUATIONS 表之前触发。 触发器使用 EVALUATIONS_SEQUENCE 为该行的主键生成唯一编号。

源 EVALUATIONS 表填充有主键。 如果您不希望安装脚本将新的主键值放入新的 EVALUATIONS 表中,则必须编辑安装脚本中的 CREATE TRIGGER 语句,如粗体所示:

sql 复制代码
CREATE OR REPLACE
TRIGGER NEW_EVALUATION_TRIGGER
BEFORE INSERT ON EVALUATIONS
FOR EACH ROW
BEGIN
  IF :NEW.evaluation_id IS NULL THEN
    :NEW.evaluation_id := evaluations_sequence.NEXTVAL
  END IF;
END;

此外,如果序列的当前值不大于主键列中的最大值,则必须使其更大。

您可以在工作表或任何文本编辑器中编辑安装脚本。

编辑安装脚本的两种替代方法是:

  • 更改源文件中的触发器定义,然后重新创建安装脚本。
  • 在运行数据安装脚本之前禁用触发器,然后再重新启用它。

10.3.5 为示例应用程序创建安装脚本

您可以为示例应用程序创建安装脚本。

这些脚本用于开发简单的 Oracle 数据库应用程序中的应用程序:

  • schemas.sql,它在部署环境中的作用与您在"为应用程序创建模式"和"向模式授予权限"中在开发环境中所做的相同
  • objects.sql,它在部署环境中的作用与您在"创建模式对象和加载数据"中在开发环境中所做的相同
  • employees.sql,它在部署环境中的作用与您在"创建 employees_pkg 包"中在开发环境中所做的相同
  • admin.sql,它在部署环境中的作用与您在"创建 admin_pkg 包"中在开发环境中所做的相同
  • create_app.sql,运行上述脚本的主脚本,从而在部署环境中部署示例应用程序

您可以按任何顺序创建脚本。 要创建 schemas.sql 和 create_app.sql,您必须使用文本编辑器。 要创建其他脚本,您可以使用文本编辑器或 SQL Developer。

10.3.5.1 创建安装脚本 schemas.sql

要创建 schemas.sql,请在任何文本编辑器中输入以下文本并将文件另存为 schemas.sql。

sql 复制代码
-----------------
-- Create schemas
-----------------
 
DROP USER app_data CASCADE;
 
CREATE USER app_data IDENTIFIED BY password
DEFAULT TABLESPACE USERS
QUOTA UNLIMITED ON USERS
ENABLE EDITIONS;
 
DROP USER app_code CASCADE;
 
CREATE USER app_code IDENTIFIED BY password
DEFAULT TABLESPACE USERS
QUOTA UNLIMITED ON USERS
ENABLE EDITIONS;
 
DROP USER app_admin CASCADE;
 
CREATE USER app_admin IDENTIFIED BY password
DEFAULT TABLESPACE USERS
QUOTA UNLIMITED ON USERS
ENABLE EDITIONS;
 
DROP USER app_user CASCADE;
 
CREATE USER app_user IDENTIFIED BY password
ENABLE EDITIONS;
 
DROP USER app_admin_user CASCADE;
 
CREATE USER app_admin_user IDENTIFIED BY password
ENABLE EDITIONS;
 
------------------------------
-- Grant privileges to schemas
------------------------------
 
GRANT CREATE SESSION TO app_data;
GRANT CREATE TABLE, CREATE VIEW, CREATE TRIGGER, CREATE SEQUENCE TO app_data;
GRANT SELECT ON HR.DEPARTMENTS TO app_data;
GRANT SELECT ON HR.EMPLOYEES TO app_data;
GRANT SELECT ON HR.JOB_HISTORY TO app_data;
GRANT SELECT ON HR.JOBS TO app_data;
 
GRANT CREATE SESSION, CREATE PROCEDURE, CREATE SYNONYM TO app_code;
 
GRANT CREATE SESSION, CREATE PROCEDURE, CREATE SYNONYM TO app_admin;
 
GRANT CREATE SESSION, CREATE SYNONYM TO app_user;
 
GRANT CREATE SESSION, CREATE SYNONYM TO app_admin_user;
10.3.5.2 创建安装脚本 objects.sql

您可以使用文本编辑器或 SQL Developer 创建 objects.sql。

要在任何文本编辑器中创建 objects.sql,请输入以下文本并将文件另存为 objects.sql。 对于密码,请使用 schema.sql 在创建用户 app_data 时指定的密码。

注意:仅当部署环境具有标准 HR 模式时,加载数据的 INSERT 语句才有效。 如果没有,则使用 SQL Developer 创建一个脚本,用源表(在开发环境中)中的数据加载新表(在部署环境中),或者修改以下脚本中的 INSERT 语句。

sql 复制代码
------------------------
-- Create schema objects
------------------------
 
CONNECT app_data/password
 
CREATE TABLE jobs#
( job_id      VARCHAR2(10)
              CONSTRAINT jobs_pk PRIMARY KEY,
  job_title   VARCHAR2(35)
              CONSTRAINT jobs_job_title_not_null NOT NULL,
  min_salary  NUMBER(6)
              CONSTRAINT jobs_min_salary_not_null NOT NULL,
  max_salary  NUMBER(6)
              CONSTRAINT jobs_max_salary_not_null NOT NULL
)
/
 
CREATE TABLE departments#
( department_id    NUMBER(4)
                   CONSTRAINT departments_pk PRIMARY KEY,
  department_name  VARCHAR2(30)
                   CONSTRAINT dept_department_name_not_null NOT NULL
                   CONSTRAINT dept_department_name_unique UNIQUE,
  manager_id       NUMBER(6) 
)
/
 
CREATE TABLE employees#
( employee_id     NUMBER(6)
                  CONSTRAINT employees_pk PRIMARY KEY,
  first_name      VARCHAR2(20)
                  CONSTRAINT emp_first_name_not_null NOT NULL,
  last_name       VARCHAR2(25)
                  CONSTRAINT emp_last_name_not_null NOT NULL,
  email_addr      VARCHAR2(25) 
                  CONSTRAINT emp_email_addr_not_null NOT NULL,
  hire_date       DATE
                  DEFAULT TRUNC(SYSDATE)
                  CONSTRAINT emp_hire_date_not_null NOT NULL
                  CONSTRAINT emp_hire_date_check
                    CHECK(TRUNC(hire_date) = hire_date),
  country_code    VARCHAR2(5)
                  CONSTRAINT emp_country_code_not_null NOT NULL,
  phone_number    VARCHAR2(20)
                  CONSTRAINT emp_phone_number_not_null NOT NULL,
  job_id          CONSTRAINT emp_job_id_not_null NOT NULL
                  CONSTRAINT emp_to_jobs_fk REFERENCES jobs#,
  job_start_date  DATE
                  CONSTRAINT emp_job_start_date_not_null NOT NULL,
                  CONSTRAINT emp_job_start_date_check
                    CHECK(TRUNC(JOB_START_DATE) = job_start_date),
  salary          NUMBER(6)
                  CONSTRAINT emp_salary_not_null NOT NULL,
  manager_id      CONSTRAINT emp_mgrid_to_emp_empid_fk REFERENCES employees#,
  department_id   CONSTRAINT emp_to_dept_fk REFERENCES departments#
)
/
 
CREATE TABLE job_history#
( employee_id  CONSTRAINT job_hist_to_emp_fk REFERENCES employees#,
  job_id       CONSTRAINT job_hist_to_jobs_fk REFERENCES jobs#,
  start_date   DATE
               CONSTRAINT job_hist_start_date_not_null NOT NULL,
  end_date     DATE
               CONSTRAINT job_hist_end_date_not_null NOT NULL,
  department_id
             CONSTRAINT job_hist_to_dept_fk REFERENCES departments#
             CONSTRAINT job_hist_dept_id_not_null NOT NULL,
             CONSTRAINT job_history_pk PRIMARY KEY(employee_id,start_date),
             CONSTRAINT job_history_date_check CHECK( start_date < end_date )
)
/
 
CREATE EDITIONING VIEW jobs AS SELECT * FROM jobs#
/
CREATE EDITIONING VIEW departments AS SELECT * FROM departments#
/
CREATE EDITIONING VIEW employees AS SELECT * FROM employees#
/
CREATE EDITIONING VIEW job_history AS SELECT * FROM job_history#
/
 
CREATE OR REPLACE TRIGGER employees_aiufer
AFTER INSERT OR UPDATE OF salary, job_id ON employees FOR EACH ROW
DECLARE
  l_cnt NUMBER;
BEGIN
  LOCK TABLE jobs IN SHARE MODE;  -- Ensure that jobs does not change
                                  -- during the following query.
  SELECT COUNT(*) INTO l_cnt
  FROM jobs
  WHERE job_id = :NEW.job_id
  AND :NEW.salary BETWEEN min_salary AND max_salary;
 
  IF (l_cnt<>1) THEN
    RAISE_APPLICATION_ERROR( -20002,
      CASE
        WHEN :new.job_id = :old.job_id
        THEN 'Salary modification invalid'
        ELSE 'Job reassignment puts salary out of range'
      END );
  END IF;
END;
/
 
CREATE OR REPLACE TRIGGER jobs_aufer
AFTER UPDATE OF min_salary, max_salary ON jobs FOR EACH ROW
WHEN (NEW.min_salary > OLD.min_salary OR NEW.max_salary < OLD.max_salary)
DECLARE
  l_cnt NUMBER;
BEGIN
  LOCK TABLE employees IN SHARE MODE;
 
  SELECT COUNT(*) INTO l_cnt
  FROM employees
  WHERE job_id = :NEW.job_id
  AND salary NOT BETWEEN :NEW.min_salary and :NEW.max_salary;
 
  IF (l_cnt>0) THEN
    RAISE_APPLICATION_ERROR( -20001,
      'Salary update would violate ' || l_cnt || ' existing employee records' );
  END IF;
END;
/
 
CREATE SEQUENCE employees_sequence START WITH 210;
CREATE SEQUENCE departments_sequence START WITH 275;
 
------------
-- Load data
------------
 
INSERT INTO jobs (job_id, job_title, min_salary, max_salary)
SELECT job_id, job_title, min_salary, max_salary
  FROM HR.JOBS
/
 
INSERT INTO departments (department_id, department_name, manager_id)
SELECT department_id, department_name, manager_id
  FROM HR.DEPARTMENTS
/
 
INSERT INTO employees (employee_id, first_name, last_name, email_addr,
  hire_date, country_code, phone_number, job_id, job_start_date, salary,
  manager_id, department_id)
SELECT employee_id, first_name, last_name, email, hire_date,
  CASE WHEN phone_number LIKE '011.%'
    THEN '+' || SUBSTR( phone_number, INSTR( phone_number, '.' )+1,
      INSTR( phone_number, '.', 1, 2 ) -  INSTR( phone_number, '.' ) - 1 )
    ELSE '+1'
  END country_code,
  CASE WHEN phone_number LIKE '011.%'
    THEN SUBSTR( phone_number, INSTR(phone_number, '.', 1, 2 )+1 )
    ELSE phone_number
  END phone_number,
  job_id,
  NVL( (SELECT MAX(end_date+1)
        FROM HR.JOB_HISTORY jh
        WHERE jh.employee_id = employees.employee_id), hire_date),
  salary, manager_id, department_id  
  FROM HR.EMPLOYEES
/
 
INSERT INTO job_history (employee_id, job_id, start_date, end_date,
  department_id)
SELECT employee_id, job_id, start_date, end_date, department_id
  FROM HR.JOB_HISTORY
/
 
COMMIT;
 
-----------------------------
-- Add foreign key constraint
-----------------------------
 
ALTER TABLE departments#
ADD CONSTRAINT dept_to_emp_fk
FOREIGN KEY(manager_id) REFERENCES employees#;
 
----------------------------------------------
-- Grant privileges on schema objects to users
----------------------------------------------
 
GRANT SELECT, INSERT, UPDATE, DELETE ON employees TO app_code;
GRANT SELECT ON departments TO app_code;
GRANT SELECT ON jobs TO app_code;
GRANT SELECT, INSERT on job_history TO app_code;
GRANT SELECT ON employees_sequence TO app_code;
 
GRANT SELECT, INSERT, UPDATE, DELETE ON jobs TO app_admin;
GRANT SELECT, INSERT, UPDATE, DELETE ON departments TO app_admin;
GRANT SELECT ON employees_sequence TO app_admin;
GRANT SELECT ON departments_sequence TO app_admin;
 
GRANT SELECT ON jobs TO app_admin_user;
GRANT SELECT ON departments TO app_admin_user;
10.3.5.3 创建安装脚本 employees.sql

您可以使用文本编辑器或 SQL Developer 创建 employees.sql。

要在任何文本编辑器中创建 employees.sql,请输入以下文本并将文件另存为 employees.sql。 对于密码,请使用 schema.sql 在创建用户 app_code 时指定的密码。

sql 复制代码
-----------------------
-- Create employees_pkg
-----------------------
 
CONNECT app_code/password
 
CREATE SYNONYM employees FOR app_data.employees;
CREATE SYNONYM departments FOR app_data.departments;
CREATE SYNONYM jobs FOR app_data.jobs;
CREATE SYNONYM job_history FOR app_data.job_history;
 
CREATE OR REPLACE PACKAGE employees_pkg
AS
  PROCEDURE get_employees_in_dept
    ( p_deptno     IN     employees.department_id%TYPE,
      p_result_set IN OUT SYS_REFCURSOR );
 
  PROCEDURE get_job_history
    ( p_employee_id  IN     employees.department_id%TYPE,
      p_result_set   IN OUT SYS_REFCURSOR );
 
  PROCEDURE show_employee
    ( p_employee_id  IN     employees.employee_id%TYPE,
      p_result_set   IN OUT SYS_REFCURSOR );
 
  PROCEDURE update_salary
    ( p_employee_id IN employees.employee_id%TYPE,
      p_new_salary  IN employees.salary%TYPE );
 
  PROCEDURE change_job
    ( p_employee_id IN employees.employee_id%TYPE,
      p_new_job     IN employees.job_id%TYPE,
      p_new_salary  IN employees.salary%TYPE := NULL,
      p_new_dept    IN employees.department_id%TYPE := NULL );
END employees_pkg;
/
 
CREATE OR REPLACE PACKAGE BODY employees_pkg
AS
  PROCEDURE get_employees_in_dept
    ( p_deptno     IN     employees.department_id%TYPE,
      p_result_set IN OUT SYS_REFCURSOR )
  IS
     l_cursor SYS_REFCURSOR;
  BEGIN
    OPEN p_result_set FOR
      SELECT e.employee_id,
        e.first_name || ' ' || e.last_name name,
        TO_CHAR( e.hire_date, 'Dy Mon ddth, yyyy' ) hire_date,
        j.job_title,
        m.first_name || ' ' || m.last_name manager,
        d.department_name
      FROM employees e INNER JOIN jobs j ON (e.job_id = j.job_id)
        LEFT OUTER JOIN employees m ON (e.manager_id = m.employee_id)
        INNER JOIN departments d ON (e.department_id = d.department_id)
      WHERE e.department_id = p_deptno ;
  END get_employees_in_dept;
 
  PROCEDURE get_job_history
    ( p_employee_id  IN     employees.department_id%TYPE,
      p_result_set   IN OUT SYS_REFCURSOR )
  IS 
  BEGIN
    OPEN p_result_set FOR
      SELECT e.First_name || ' ' || e.last_name name, j.job_title,
        e.job_start_date start_date,
        TO_DATE(NULL) end_date
      FROM employees e INNER JOIN jobs j ON (e.job_id = j.job_id)
      WHERE e.employee_id = p_employee_id
      UNION ALL
      SELECT e.First_name || ' ' || e.last_name name,
        j.job_title,
        jh.start_date,
        jh.end_date
      FROM employees e INNER JOIN job_history jh
        ON (e.employee_id = jh.employee_id)
        INNER JOIN jobs j ON (jh.job_id = j.job_id)
      WHERE e.employee_id = p_employee_id
      ORDER BY start_date DESC;
  END get_job_history;
 
  PROCEDURE show_employee
    ( p_employee_id  IN     employees.employee_id%TYPE,
      p_result_set   IN OUT sys_refcursor )
  IS 
  BEGIN
    OPEN p_result_set FOR
      SELECT *
      FROM (SELECT TO_CHAR(e.employee_id) employee_id,
              e.first_name || ' ' || e.last_name name,
              e.email_addr,
              TO_CHAR(e.hire_date,'dd-mon-yyyy') hire_date,
              e.country_code,
              e.phone_number,
              j.job_title,
              TO_CHAR(e.job_start_date,'dd-mon-yyyy') job_start_date,
              to_char(e.salary) salary,
              m.first_name || ' ' || m.last_name manager,
              d.department_name
            FROM employees e INNER JOIN jobs j on (e.job_id = j.job_id)
              RIGHT OUTER JOIN employees m ON (m.employee_id = e.manager_id)
              INNER JOIN departments d ON (e.department_id = d.department_id)
            WHERE e.employee_id = p_employee_id)
      UNPIVOT (VALUE FOR ATTRIBUTE IN (employee_id, name, email_addr, hire_date,
        country_code, phone_number, job_title, job_start_date, salary, manager,
        department_name) );
  END show_employee;
 
  PROCEDURE update_salary
    ( p_employee_id IN employees.employee_id%type,
      p_new_salary  IN employees.salary%type )
  IS 
  BEGIN
    UPDATE employees
    SET salary = p_new_salary
    WHERE employee_id = p_employee_id;
  END update_salary;
 
  PROCEDURE change_job
    ( p_employee_id IN employees.employee_id%TYPE,
      p_new_job     IN employees.job_id%TYPE,
      p_new_salary  IN employees.salary%TYPE := NULL,
      p_new_dept    IN employees.department_id%TYPE := NULL )
  IS
  BEGIN
    INSERT INTO job_history (employee_id, start_date, end_date, job_id,
      department_id)
    SELECT employee_id, job_start_date, TRUNC(SYSDATE), job_id, department_id
      FROM employees
      WHERE employee_id = p_employee_id;
 
    UPDATE employees
    SET job_id = p_new_job,
      department_id = NVL( p_new_dept, department_id ),
      salary = NVL( p_new_salary, salary ),
      job_start_date = TRUNC(SYSDATE)
    WHERE employee_id = p_employee_id;
  END change_job;
END employees_pkg;
/
 
---------------------------------------------
-- Grant privileges on employees_pkg to users
---------------------------------------------
 
GRANT EXECUTE ON employees_pkg TO app_user;
GRANT EXECUTE ON employees_pkg TO app_admin_user;
10.3.5.4 创建安装脚本 admin.sql

您可以使用文本编辑器或 SQL Developer 创建 admin.sql。

要在任何文本编辑器中创建 admin.sql,请输入以下文本并将文件另存为 admin.sql。 对于密码,请使用 schema.sql 在创建用户 app_admin 时指定的密码。

sql 复制代码
-------------------
-- Create admin_pkg
-------------------
 
CONNECT app_admin/password
 
CREATE SYNONYM departments FOR app_data.departments;
CREATE SYNONYM jobs FOR app_data.jobs;
CREATE SYNONYM departments_sequence FOR app_data.departments_sequence;
 
CREATE OR REPLACE PACKAGE admin_pkg
AS
  PROCEDURE update_job
    ( p_job_id      IN jobs.job_id%TYPE,
      p_job_title   IN jobs.job_title%TYPE := NULL,
      p_min_salary  IN jobs.min_salary%TYPE := NULL,
      p_max_salary  IN jobs.max_salary%TYPE := NULL );
 
  PROCEDURE add_job
    ( p_job_id      IN jobs.job_id%TYPE,
      p_job_title   IN jobs.job_title%TYPE,
      p_min_salary  IN jobs.min_salary%TYPE,
      p_max_salary  IN jobs.max_salary%TYPE );
 
  PROCEDURE update_department
    ( p_department_id     IN departments.department_id%TYPE,
      p_department_name   IN departments.department_name%TYPE := NULL,
      p_manager_id        IN departments.manager_id%TYPE := NULL,
      p_update_manager_id IN BOOLEAN := FALSE );
 
  FUNCTION add_department
    ( p_department_name   IN departments.department_name%TYPE,
      p_manager_id        IN departments.manager_id%TYPE )
    RETURN departments.department_id%TYPE;
 
END admin_pkg;
/
 
CREATE OR REPLACE PACKAGE BODY admin_pkg
AS
  PROCEDURE update_job
    ( p_job_id      IN jobs.job_id%TYPE,
      p_job_title   IN jobs.job_title%TYPE := NULL,
      p_min_salary  IN jobs.min_salary%TYPE := NULL,
      p_max_salary  IN jobs.max_salary%TYPE := NULL )
  IS
  BEGIN
    UPDATE jobs
    SET job_title  = NVL( p_job_title, job_title ),
        min_salary = NVL( p_min_salary, min_salary ),
        max_salary = NVL( p_max_salary, max_salary )
    WHERE job_id = p_job_id;
  END update_job;
 
  PROCEDURE add_job
    ( p_job_id      IN jobs.job_id%TYPE,
      p_job_title   IN jobs.job_title%TYPE,
      p_min_salary  IN jobs.min_salary%TYPE,
      p_max_salary  IN jobs.max_salary%TYPE )
  IS
  BEGIN
    INSERT INTO jobs ( job_id, job_title, min_salary, max_salary )
    VALUES ( p_job_id, p_job_title, p_min_salary, p_max_salary );
  END add_job;
 
  PROCEDURE update_department
    ( p_department_id     IN departments.department_id%TYPE,
      p_department_name   IN departments.department_name%TYPE := NULL,
      p_manager_id        IN departments.manager_id%TYPE := NULL,
      p_update_manager_id IN BOOLEAN := FALSE )
  IS
  BEGIN
    IF ( p_update_manager_id ) THEN
      UPDATE departments
      SET department_name = NVL( p_department_name, department_name ),
          manager_id = p_manager_id
      WHERE department_id = p_department_id;
    ELSE
      UPDATE departments
      SET department_name = NVL( p_department_name, department_name )
      WHERE department_id = p_department_id;
    END IF;
  END update_department;
 
  FUNCTION add_department
    ( p_department_name   IN departments.department_name%TYPE,
      p_manager_id        IN departments.manager_id%TYPE )
    RETURN departments.department_id%TYPE
  IS
    l_department_id departments.department_id%TYPE;
  BEGIN
    INSERT INTO departments ( department_id, department_name, manager_id )
      VALUES ( departments_sequence.NEXTVAL, p_department_name, p_manager_id )
      RETURNING department_id INTO l_department_id;
    RETURN l_department_id;
  END add_department;
 
END admin_pkg;
/
 
----------------------------------------
-- Grant privileges on admin_pkg to user
----------------------------------------
 
GRANT EXECUTE ON admin_pkg TO app_admin_user;
10.3.5.5 创建主安装脚本 create_app.sql

主安装脚本 create_app.sql 以正确的顺序运行示例应用程序的其他四个安装脚本,从而在部署环境中部署示例应用程序。

要创建 create_app.sql,请在任何文本编辑器中输入以下文本并将文件另存为 create_app.sql:

sql 复制代码
@schemas.sql
@objects.sql
@employees.sql
@admin.sql

10.4 部署示例应用程序

您可以使用安装脚本部署示例应用程序。

注意:对于以下过程,您需要具有 CREATE USER 和 DROP USER 系统权限的用户的名称和密码。

要部署示例应用程序:

  1. 将之前创建的安装脚本复制到部署环境。
  2. 在部署环境中,以具有 CREATE USER 和 DROP USER 系统权限的用户身份连接到 Oracle 数据库。
  3. 运行主安装脚本:
sql 复制代码
@create_app.sql

10.5 检查安装的有效性

查看Reports pane中的Expand Data Dictionary Reports。

10.6 安装脚本归档

验证应用程序安装有效后,Oracle 建议您将安装脚本存档在源代码控制系统中。

在这样做之前,为每个文件添加注释,记录其创建日期和目的。 如果您必须将同一个应用程序部署到另一个环境,您可以使用这些归档文件。

相关推荐
XDHCOM3 小时前
ORA-31215: DBMS_LDAP PL/SQL无效LDAP修改值,Oracle报错故障修复与远程处理方案,快速解决连接配置难题
数据库·sql·oracle
betazhou5 小时前
记一次Oracle REDO在线日志损坏故障修复
数据库·oracle·redo·ora-00600
老苏畅谈运维6 小时前
DBA分析 ORA 报错的利器,errorstack让 Oracle 错误现原形
数据库·oracle·dba
大江东去浪淘尽千古风流人物6 小时前
【Basalt】nfr_mapper 中的“小 SfM/BA 后端”
c++·人工智能·计算机视觉·oracle·augmented reality
曹牧17 小时前
Oracle数据库中,将JSON字符串转换为多行数据
数据库·oracle·json
jnrjian18 小时前
text index 查看index column index定义 index 刷新频率 index视图
数据库·oracle
韶博雅18 小时前
emcc升级
oracle
迷枫71219 小时前
达梦数据库的体系架构
数据库·oracle·架构
夜晚打字声20 小时前
9(九)Jmeter如何连接数据库
数据库·jmeter·oracle
NineData20 小时前
NineData 智能数据管理平台新功能发布|2026 年 3 月
数据库·oracle·架构·dba·ninedata·数据复制·数据迁移工具