简体   繁体   English

Oracle 将使用 CTE 的工作 SQL 转换为程序

[英]Oracle converting working SQL with CTE to procedure

I have some SQL code, which works as designed.我有一些 SQL 代码,可以按设计工作。 I'm trying to convert the code to a procedure to make it flexible so that different values maybe passed in.我正在尝试将代码转换为过程以使其灵活,以便可以传入不同的值。

I am running into an error while trying to create the procedure.我在尝试创建过程时遇到错误。

Errors: PROCEDURE CREATE_XXX
Line/Col: 28/1 PL/SQL: SQL Statement ignored
Line/Col: 37/3 PL/SQL: ORA-00928: missing SELECT keyword

The problem seems to be occurring in a CTE, which contains a SELECT so I'm a bit confused and can use some assistance.问题似乎出现在 CTE 中,其中包含一个 SELECT,所以我有点困惑,需要一些帮助。

Below is a test CASE that contains the working SQL along with the procedure I'm trying to create.下面是一个测试案例,其中包含工作 SQL 以及我尝试创建的过程。

Thanks in advance for your help, patience and expertise and to all who answer.提前感谢您的帮助、耐心和专业知识,并感谢所有回答的人。


ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';

create table schedule(
       schedule_id NUMBER(4),
       location_id number(4),
       base_date DATE,
       start_date DATE,
       end_date DATE,
         CONSTRAINT start_min check (start_date=trunc(start_date,'MI')),   
       CONSTRAINT end_min check (end_date=trunc(end_date,'MI')),
 CONSTRAINT end_gt_start CHECK (end_date >= start_date),
CONSTRAINT same_day CHECK (TRUNC(end_date) = TRUNC(start_date))
      );
/

    CREATE TABLE locations AS
    SELECT level AS location_id,
       'Door ' || level AS location_name,

    CASE round(dbms_random.value(1,3)) 
            WHEN 1 THEN 'A' 
            WHEN 2 THEN 'T' 
            WHEN 3 THEN 'T' 
         END AS location_type

    FROM   dual
    CONNECT BY level <= 15;


     ALTER TABLE locations 
         ADD ( CONSTRAINT locations_pk
       PRIMARY KEY (location_id));


-- works fine

WITH  params  AS
(
    SELECT  1 AS schedule_id,

TO_DATE ( '2021-08-21 00:00:00'
            , 'YYYY-MM-DD HH24:MI:SS'
            )             AS base_date
    ,    INTERVAL '83760' SECOND          AS offset
    ,    INTERVAL '10' MINUTE           AS incr
    ,    INTERVAL '5' MINUTE         AS duration
    FROM    dual
)
SELECT   p.schedule_id
,               l.location_id
,      p.base_date
,      p.base_date + offset
              + (incr * (ROWNUM - 1)) AS start_date
,      p.base_date + offset
              + (incr * (ROWNUM - 1))
            + p.duration         AS end_date
FROM      locations l
CROSS JOIN params   p
ORDER BY  start_date
;

-- having problem 

CREATE OR REPLACE PROCEDURE CREATE_XXX
 (
  i_schedule_id IN PLS_INTEGER,
  i_base_date IN DATE,
  i_offset IN PLS_INTEGER DEFAULT 0, 
i_incr IN PLS_INTEGER DEFAULT 10,
  i_duration         IN PLS_INTEGER DEFAULT 5
)
 AS 
 
l_offset  interval day to second;
   l_incr interval day to second;
  l_duration interval day to second;


BEGIN
 
l_offset :=
NUMTODSINTERVAL(i_offset, 'SECOND') ;

l_incr :=
NUMTODSINTERVAL(i_incr, 'MINUTE') ;

l_duration :=
NUMTODSINTERVAL(i_duration, 'MINUTE') ;


WITH params AS(
SELECT 
   i_schedule_id 
  ,i_base_date
  ,l_offset
  ,l_incr
  ,l_duration
FROM DUAL
)

INSERT INTO schedule(
        schedule_id
      ,location_id
      ,base_date
      ,start_date
      ,end_date
  )
  VALUES 
  (p.schedule_id 
   ,l.location_id 
   ,p.base_date
   ,start_date 
   ,end_date 
);

SELECT   p.schedule_id
,               l.location_id
,      p.base_date
,      p.base_date + p.offset
              + (p.incr * (ROWNUM - 1)) AS start_date
,      p.base_date + p.offset
              + (p.incr * (ROWNUM - 1))
            + p.duration         AS end_date
FROM      locations l
CROSS JOIN params   p
ORDER BY  start_date;
END;
/

The issue is this statement.问题是这个声明。

WITH params AS(
SELECT 
   i_schedule_id 
  ,i_base_date
  ,l_offset
  ,l_incr
  ,l_duration
FROM DUAL
)

INSERT INTO schedule(
        schedule_id
      ,location_id
      ,base_date
      ,start_date
      ,end_date
  )
  VALUES 
  (p.schedule_id 
   ,l.location_id 
   ,p.base_date
   ,start_date 
   ,end_date 
);

I'm not sure what you're trying to accomplish.我不确定你想要完成什么。 Syntactically, if you want to have a CTE as part of an insert , you'd want to do an insert... select从句法上讲,如果您想将 CTE 作为insert的一部分,则需要insert... select

INSERT INTO schedule(
        schedule_id
      ,location_id
      ,base_date
      ,start_date
      ,end_date
  )
WITH params AS(
SELECT 
   i_schedule_id 
  ,i_base_date
  ,l_offset
  ,l_incr
  ,l_duration
FROM DUAL
)
SELECT 
    p.schedule_id 
   ,l.location_id 
   ,p.base_date
   ,start_date 
   ,end_date 
  FROM params p;

From there, though, you've still got several syntax issues that it's not obvious how you'd want to solve all of them.但是,从那里开始,您仍然遇到一些语法问题,您并不清楚您希望如何解决所有这些问题。

  • p.schedule_id isn't valid because there is no schedule_id column in the params CTE. p.schedule_id无效,因为params CTE 中没有schedule_id列。 My guess is that you want to alias i_schedule_id to schedule_id in the CTE.我的猜测是您想在 CTE i_schedule_id别名为schedule_id
  • l.location_id doesn't make sense because you're not selecting from a table that could plausibly be given an alias of l . l.location_id没有意义,因为您不是从一个可能被赋予l别名的表中进行选择。
  • There is no base_date , start_date , or end_date column in your params CTE. params CTE 中没有base_datestart_dateend_date列。 And it's not obvious how you'd plausibly fix that.而且你如何合理地解决这个问题并不明显。

Maybe you actually intended the subsequent select statement to actually be part of this insert statement despite the fact that you terminated the insert with a semicolon?也许您实际上希望后续的select语句实际上成为此insert语句的一部分,尽管您用分号终止了insert If so, maybe you want如果是这样,也许你想要

    INSERT INTO schedule(
            schedule_id
          ,location_id
          ,base_date
          ,start_date
          ,end_date
      )
    WITH params AS(
    SELECT 
       i_schedule_id schedule_id
      ,i_base_date base_date
      ,l_offset offset
      ,l_incr incr
      ,l_duration duration
    FROM DUAL
    )
SELECT   p.schedule_id
,               l.location_id
,      p.base_date
,      p.base_date + p.offset
              + (p.incr * (ROWNUM - 1)) AS start_date
,      p.base_date + p.offset
              + (p.incr * (ROWNUM - 1))
            + p.duration         AS end_date
FROM      locations l
CROSS JOIN params   p
ORDER BY  start_date;

That would produce code that compiles at least.这将产生至少可以编译的代码。 There doesn't, however, appear to be any reason to bother with a CTE here at all.但是,似乎根本没有任何理由为 CTE 而烦恼。 Just reference the local variables and input parameters directly.直接引用局部变量和输入参数即可。 I've also eliminated the order by since it doesn't make sense in the context of an insert statement.我还删除了order by因为它在insert语句的上下文中没有意义。 I assume there is a reason that you need to take input parameters as integers and convert them to local variables with the same name and a different data type rather than just passing in interval parameters to the procedure.我假设有一个原因需要将输入参数作为整数并将它们转换为具有相同名称和不同数据类型的局部变量,而不是仅仅将interval参数传递给过程。 If you can do that, you can further simplify things by eliminating the local variables.如果你能做到这一点,你就可以通过消除局部变量来进一步简化事情。

CREATE OR REPLACE PROCEDURE CREATE_XXX
 (
  i_schedule_id IN PLS_INTEGER,
  i_base_date IN DATE,
  i_offset IN PLS_INTEGER DEFAULT 0, 
i_incr IN PLS_INTEGER DEFAULT 10,
  i_duration         IN PLS_INTEGER DEFAULT 5
)
 AS 
 
l_offset  interval day to second;
   l_incr interval day to second;
  l_duration interval day to second;


BEGIN
 
l_offset :=
NUMTODSINTERVAL(i_offset, 'SECOND') ;

l_incr :=
NUMTODSINTERVAL(i_incr, 'MINUTE') ;

l_duration :=
NUMTODSINTERVAL(i_duration, 'MINUTE') ;


        INSERT INTO schedule(
                schedule_id
              ,location_id
              ,base_date
              ,start_date
              ,end_date
          )
    SELECT   i_schedule_id
    ,        l.location_id
    ,        i_base_date
    ,      i_base_date + l_offset
                  + (l_incr * (ROWNUM - 1)) AS start_date
    ,      i_base_date + l_offset
                  + (l_incr * (ROWNUM - 1))
                + l_duration         AS end_date
    FROM      locations l;
END;
/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM