Oracle存储过程的问题

时间:2012-07-20 13:52:46

标签: c# .net stored-procedures ado.net oracle11g

我编写了下面的存储过程来检索要发送到我的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个不同的表,其中数据在应用程序中调用。

2 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:0)

  • 您的语法似乎有效。
  • 您无法关闭存储过程中的游标。由于游标返回到客户端,因此客户端应用程序负责关闭它们。假设客户端是基于标记的C#应用​​程序,C#应用程序将需要关闭光标。

在架构上,设计一个返回4个独立SYS_REFCURSOR参数的存储过程是不常见的,特别是在大多数查询似乎返回1行数据的情况下(假设EMP_EMAIL是一个关键字) EMPLOYEES表)或少量数据行(例如,员工可能有多个地址)。如果EMP_EMAIL不是EMPLOYEES表中的关键字,我认为客户无法找出哪个地址,哪个电话号码或哪个工作与哪个员工几乎可以肯定是个bug。如果返回许多行,返回单个数据库中的四个表所产生的单个SYS_REFCURSOR会更常见,如果您知道只有一个,则返回标量(可能是标量类型)如果您确实需要返回四个独立的结果集,则应该返回行,或者有四个单独的过程。就个人而言,如果您真的希望地址与电话号码分开的SYS_REFCURSOR参数,我更愿意返回集合而不是OUT,但假设您的C#应用​​程序是C#应用程序可能更难映射到您的C#应用​​程序确实声明了四个单独的GUI控件来显示四个独立的结果集。

作为一般惯例,我强烈建议使用某种命名约定来识别参数并将它们与列名和潜在列名分开。我建议使用锚定类型。我个人会使用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_email表中的employeesemp_email参数进行比较。但是,此代码最终会将emp_email表中的employees与自身进行比较,并返回emp_email不为NULL的表中的每一行。相反,如果我对参数使用一致的命名约定

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,则此组织将非常有用。包允许您定义仅适用于包中的方法的私有方法,这有助于封装。并且包有助于依赖关系管理,因为当您更改包规范而不是包体时,只需要重新编译其他包。