【问题标题】:PL/SQL Procedure SyntaxPL/SQL 过程语法
【发布时间】:2014-12-12 15:31:02
【问题描述】:

所以我在处理一些 PL SQL 语句时遇到了一些麻烦。本质上,我正在尝试创建一个程序,该程序将检查当插入新元组时,该程序会检查同一日期内没有同一个人的另一份合同,即。新合同的日期不与另一个日期重叠。

代码如下:

CREATE OR REPLACE PROCEDURE dateOrder 
(name IN VARCHAR2, start IN DATE, end IN DATE)
IS 
    x number;
    y number;

BEGIN
    CREATE OR REPLACE VIEW PersonContracts AS
    SELECT * FROM ContractInfo WHERE HasContract=name;

    SELECT COUNT(*) INTO x FROM PersonContracts
    WHERE start BETWEEN date_from AND date_to;

    SELECT COUNT(*) INTO y from PersonContracts
    WHERE end BETWEEN date_from AND date_to;
    IF x > 0 THEN
        dbms_output.put_line("overlaps.");
    END IF;

    IF Y > 0 THEN
        dbms_output.put_line("overlaps.");
    END IF;

END dateOrder;
/

BEGIN
    dateOrder("John Smith", "08-oct-2014", "12-oct-2014");
END;

我在有或没有视图的情况下都尝试过,但如果可能的话,我更愿意保留视图。我只是 PL 的新手!

【问题讨论】:

    标签: sql oracle plsql procedure


    【解决方案1】:

    您不能在使用 DDL 的过程中创建 VIEW(您必须使用 EXECUTE IMMEDIATE 来执行此操作)。

    我更愿意直接设置 SELECT 语句的 WHERE-Clause:

    CREATE OR REPLACE PROCEDURE dateOrder (name IN VARCHAR2, start IN DATE, end IN DATE)
        IS 
           x number;
           y number;
        BEGIN
    
        SELECT COUNT(*) INTO x FROM ContractInfo WHERE HasContract=name
        AND start BETWEEN date_from AND date_to;
    
        SELECT COUNT(*) INTO y from ContractInfo WHERE HasContract=name
        AND end BETWEEN date_from AND date_to;
    
        IF x > 0 THEN
            dbms_output.put_line("overlaps.");
        END IF;
    
        IF Y > 0 THEN
            dbms_output.put_line("overlaps.");
        END IF;
    
    END dateOrder;
    /
    
    BEGIN
        dateOrder("John Smith", "08-oct-2014", "12-oct-2014");
    END;
    

    【讨论】:

    • 在我看来,这是更好的解决方案。在过程代码中重新定义数据库对象通常是个坏主意。
    • 是的,谢谢!观点不是太重要!刚刚将 AND 添加到 WHERE 中,它给了我同样的效果 :)
    【解决方案2】:

    所以有些事情在您的程序中不起作用。将此作为建议而不是解决方案:

    1. 在过程中编写 ddl 不是一种好的风格。顺便说一句,在这个过程中访问新视图是不可能的!!
    2. 如果您想这样做,请将 Create View 放入动态 SQL 语句中,如下面的代码 sn-p 所示
    3. 您要从该过程访问的所有 DB 对象必须在编译时存在。因此,除非您也在动态 SQL 中编写所有 Select 语句,否则此代码将永远无法工作。
    4. 不要将参数命名为“start”或“end”。这些是保留字,因此不允许使用。
    5. 如果您调用 dateOrder 过程,请确保您将传递一个有效日期作为参数。在您的示例中,您将传递字符串。也许这适用于您的默认 NLS,但在另一个环境/数据库中可能不会。

    看看这个:

    CREATE OR REPLACE PROCEDURE dateOrder
    (name IN VARCHAR2, xstart IN DATE, xend IN DATE)
    IS
        x number;
        y number;
    
    BEGIN
        execute immediate (
          'CREATE OR REPLACE VIEW PersonContracts AS
          SELECT * FROM ContractInfo ....'
        );
        -- that won't work, because the PersonContracts will be not there at compile time.
        SELECT COUNT(*) INTO x FROM PersonContracts
        WHERE start BETWEEN date_from AND date_to;
    
        SELECT COUNT(*) INTO y from PersonContracts
        WHERE end BETWEEN date_from AND date_to;
        IF x > 0 THEN
            dbms_output.put_line("overlaps.");
        END IF;
    
        IF Y > 0 THEN
            dbms_output.put_line("overlaps.");
        END IF;
    
    
    END dateOrder;
    
    
    BEGIN
        dateOrder("John Smith", "08-oct-2014", "12-oct-2014");
    END;
    

    【讨论】:

    • 为了使其工作,编译过程时视图必须存在。它可以在过程中重新定义,只要它也提前存在。
    • 你是对的。我只关注动态 SQL,没有检查其余部分。顺便说一句,绝对不建议写这样的代码。
    【解决方案3】:

    即使允许,也不需要视图。您只想检查具有等于name 参数的HasContract 值的行。好的,按照您的需要编写查询,然后将HasContract = name 添加到where 子句。不要过度思考简单的解决方案。

    此外,您可以在一个查询中找到您需要的内容。您要捕获的条件是开始日期和停止日期定义的时间间隔与任何现有的开始日期和停止日期之间是否有任何重叠。虽然我们可以煞费苦心地列出所有可能会导致重叠的排列,但让我们看看仅有的两种不会导致重叠的排列。

    • 如果一个的结束日期小于或等于另一个的开始日期或
    • 如果一个的开始日期大于或等于另一个的结束数据。

    或者,以等式形式e1 <= s2 or s1 >= e2。一点布尔魔法,我们可以反转为e1 > s2 and s1 < e2。这给了我们简化的查询:

    select COUNT(*) into y
    from   ContractInfo
    where  HasContract = name
      and  p_end   > date_from
      and  p_start < date_to;
    

    如果此查询返回任何非零答案,则某处将存在重叠。一个简单的查询,一个之后的检查。很简单。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-03
      • 2019-11-20
      • 2012-05-23
      • 1970-01-01
      相关资源
      最近更新 更多