0

我编写了以下存储过程来检索要发送到我的 C# 应用程序的 DataSet 的数据。

您能否为我的存储过程建议一个更健壮的设计?我假设这不是返回记录集的最佳方式。

CREATE OR REPLACE PROCEDURE GET_EMPLOYEE_DATA 
(
  EMPLOYEE_EMAIL IN VARCHAR2,
  EMP_RECORD_SET1 OUT SYS_REFCURSOR,
  EMP_RECORD_SET2 OUT SYS_REFCURSOR,
  EMP_RECORD_SET3 OUT SYS_REFCURSOR,
  EMP_RECORD_SET4 OUT SYS_REFCURSOR
) AS 
BEGIN
  OPEN EMP_RECORD_SET1 FOR

  SELECT EMPLOYEENAME AS EMP_NAME,
         EMPLOYEELASTNAME AS EMP_LAST_NAME,
         EMPLOYEEFIRSTNAME AS EMP_FIRST_NAME
         FROM EMP.EMPLOYEES
         WHERE EMP_EMAIL = EMPLOYEE_EMAIL
      ;

     OPEN EMP_RECORD_SET2 FOR 


        SELECT EMPLOYEEADD AS EMP_ADDRESSESS,
         EMPLOYEECITY AS EMP_CITY,
         EMPLOYEE_STATE AS EMP_STATE
         FROM EMP.EMPLOYEES_ADDRESSES
         WHERE EMP_EMAIL = EMPLOYEE_EMAIL;

      OPEN EMP_RECORD_SET3 FOR

        SELECT EMPLOYEEPHONE AS EMP_PHONE,
         EMPLOYEEEXTENSION AS EMP_EXTENSION
         FROM EMP.EMPLOYEES_CONTACTS
         WHERE EMP_EMAIL = EMPLOYEE_EMAIL
      ;

      OPEN EMP_RECORD_SET4 FOR

  SELECT EMPLOYEEJOB AS EMP_JOB,
         EMPLOYEERESPONSIBILITIES AS EMP_RESPONSIBILITIES
         FROM EMP.EMPLOYEES_DATA
         WHERE EMP_EMAIL = EMPLOYEE_EMAIL
      ;


END GET_PROTOCOL_INFO_SP;

我需要知道我的语法是否正确以及是否应该关闭游标。我有 4 个不同的表,在应用程序中调用数据。

4

2 回答 2

0
  • 您的语法似乎是有效的。
  • 您不能关闭存储过程中的游标。由于游标被返回给客户端,客户端应用程序负责关闭它们。假设客户端是基于标签的 C# 应用程序,C# 应用程序将需要关闭光标。

在架构上,设计一个返回 4 个单独参数的存储过程是不常见的,SYS_REFCURSOR特别是在大多数查询似乎返回 1 行数据(假设EMP_EMAILEMPLOYEES表中的键)或少数数据行(如果,对于例如,员工可能有多个地址)。如果EMP_EMAIL不是EMPLOYEES表中的键,我看不到客户有任何方法可以确定哪个地址,哪个电话号码,或者哪个工作与哪个员工一起工作,这几乎肯定是一个错误。返回一个单一的会更常见SYS_REFCURSOR如果要返回许多行,则将数据库中的四个表连接在一起,如果您知道应该只返回一行,则返回标量(可能是标量类型),或者如果您真的需要,则有四个单独的过程返回四个独立的结果集。就个人而言,我更喜欢返回集合,而不是SYS_REFCURSOR如果您真的希望将地址OUT与电话号码分开,但如果您的 C# 应用程序确实声明了四个单独的 GUI 控件来显示这四个,那么这可能更难映射到您的 C# 应用程序单独的结果集。

作为一般约定,我强烈建议使用某种命名约定来标识参数并将它们与列名和潜在的列名分开。我建议使用锚定类型。我个人会使用P_前缀,例如

CREATE OR REPLACE PROCEDURE GET_EMPLOYEE_DATA 
(
  P_EMPLOYEE_EMAIL IN EMP.EMPLOYEES.EMAIL%TYPE,
  P_EMP_RECORD_SET1 OUT SYS_REFCURSOR,
  P_EMP_RECORD_SET2 OUT SYS_REFCURSOR,
  P_EMP_RECORD_SET3 OUT SYS_REFCURSOR,
  P_EMP_RECORD_SET4 OUT SYS_REFCURSOR
)

这种命名约定的原因是,当您在 PL/SQL 块中编写 SQL 语句时,首先使用列名解析标识符,然后使用局部变量。这导致很多人无意中编写了代码,他们打算将局部变量与列名进行比较,但最终将列名与其自身进行比较。例如,如果我写这样的东西

CREATE OR REPLACE PROCEDURE get_employee (
  emp_email  IN emp.employees.email%type,
  rc        OUT sys_refcursor 
)
AS
BEGIN
  OPEN rc
   FOR select *
         from emp.employees e
        where e.emp_email = emp_email;
END;

很明显,我的意图是将emp_emailemployees中的值与emp_email参数进行比较。但是,此代码最终将emp_email表中的 与自身进行比较,并返回表中不为 NULLemployees的每一行。emp_email相反,如果我对参数使用一致的命名约定

CREATE OR REPLACE PROCEDURE get_employee (
  p_emp_email  IN emp.employees.email%type,
  p_rc        OUT sys_refcursor 
)
AS
BEGIN
  OPEN p_rc
   FOR select *
         from emp.employees e
        where e.emp_email = p_emp_email;
END;

那么当我打算引用局部变量时,如果我无意中引用了列名,就会立即清楚得多,反之亦然。

一般来说,我也强烈建议每个程序都成为一个包的一部分。这有助于组织,允许您将相关程序组合在一起。在这种情况下,如果您决定将此过程分成四个单独的过程,每个过程都返回一个SYS_REFCURSOR. 包允许您定义仅可用于包中的方法的私有方法,这有助于封装。并且包有助于依赖管理,因为仅当您更改包规范而不是包体时才需要重新编译其他包。

于 2012-07-20T18:45:59.340 回答
0

如果您想获得 4 个不同的数据表,这似乎是一个足够合理的 SP。
您还可以考虑仅检索一个已连接的数据表,具体取决于每位员工的预期行数和您的应用程序的需求。

在任何情况下,您都可能希望为数据表使用更有意义的名称(例如Employees_PhonesEmployees_Addresses

于 2012-07-20T14:04:28.797 回答