Oracle 查询逻辑声明和使用

Oracle Query Logic Declare and With

我想打印出动态构建的查询。然而我被困在变量声明中;第 2 行错误。 我需要这些 VARCHAR2 变量的最大大小。

我的整体结构好吗?

我在动态查询中使用 WITH 的结果。

DECLARE l_sql_query VARCHAR2(2000);
        l_sql_queryFinal VARCHAR2(2000);
        
    
with cntp as (select distinct 
cnt.code code_container,
*STUFF*
FROM container cnt

WHERE 

cnt.status !='DESTROYED'
order by cnt.code)

BEGIN
FOR l_counter IN 2022..2032
LOOP
    l_sql_query := l_sql_query || 'SELECT cntp.code_container *STUFF*
FROM cntp 
GROUP BY cntp.code_container ,cntp.label_container, cntp.Plan_Classement, Years
HAVING
cntp.Years=' || l_counter ||'
AND
/*stuff*/ TO_DATE(''31/12/' || l_counter ||''',''DD/MM/YYYY'')
AND SUM(cntp.IsA)=0
AND SUM(cntp.IsB)=0

UNION
';

END LOOP;
END;

l_sql_queryFinal := SUBSTR(l_sql_query,  0,  LENGTH (l_sql_query) – 5);
l_sql_queryFinal := l_sql_queryFinal||';'

dbms_output.put_line(l_sql_queryFinal);

Is there a way to put the maximum size "automaticcaly" like "VARCHAR2(MAX_STRING_SIZE) does it work ?

没有。没有。

PL/SQL 中 varchar2 的最大大小为 32767。如果您想在将来的某个时候对冲这种变化,您可以在共享包中声明 a user-defined subtype。 ..

create or replace package my_subtypes as

  subtype max_string_size is varchar2(32767);

end my_subtypes;
/

...并在您的程序中引用...

DECLARE
  l_sql_query       my_subtypes.max_string_size;
  l_sql_queryFinal  my_subtypes.max_string_size;
...

因此,如果 Oracle 随后在 PL/SQL 中提高 VARCHAR2 的最大允许大小,您只需更改 my_subtypes.max_string_size 的定义,以便在您使用该子类型的任何地方提高边界。

或者,只需使用 CLOB。当 CLOB 的大小 <= 32k 时,Oracle 非常聪明地将 CLOB 视为 VARCHAR2。

要解决您的其他问题,您需要将 WITH 子句视为字符串并将其分配给您的查询变量。

l_sql_query       my_subtypes.max_string_size := q'[
with cntp as (select distinct 
cnt.code code_container,
*STUFF*
FROM container cnt
WHERE cnt.status !='DESTROYED'
order by cnt.code) ]';

请注意使用特殊引号语法 q'[ ... ]' 以避免在查询片段中转义引号。


A dynamic string query do not access a temp table ?

Dynamic SQL 是包含 DML 或 DDL 语句的字符串,我们使用 EXECUTE IMMEDIATE 或 DBMS_SQL 命令执行这些语句。否则它与 static SQL 完全相同,它的行为没有任何不同。事实上,编写动态 SQL 的最佳方法是首先在工作表中编写静态语句,使其正确,然后找出哪些位需要动态(变量、占位符)以及哪些位保持静态(样板).在您的情况下,WITH 子句是语句的静态部分。

您发布的代码有不少问题,其中:

  • 您将 with (CTE) 作为声明部分中的独立片段,这是无效的。如果你想让它成为动态字符串的一部分,那么把它放在字符串中;
  • 你的END;放错地方了;
  • 你有 而不是 -;
  • 你删除了最后 5 个字符,但是你以一个新行结束,所以你需要删除 6 以包括最后一个 UNION 的 U;
  • 附加分号的行本身缺少分号(尽管对于动态 SQL 您通常不需要分号,因此整行可能会被删除);
  • 2000 个字符对于您的示例来说太小了,但实际最大值 32767 没问题。
DECLARE
  l_sql_query VARCHAR2(32767);
  l_sql_queryFinal VARCHAR2(32767);
BEGIN
  -- initial SQL which just declares the CTE
  l_sql_query := q'^
with cntp as (select distinct 
cnt.code code_container,
*STUFF*
FROM container cnt

WHERE 

cnt.status !='DESTROYED'
order by cnt.code)

^';

  -- loop around each year...
  FOR l_counter IN 2022..2032
  LOOP
    l_sql_query := l_sql_query || 'SELECT cntp.code_container *STUFF*
FROM cntp 
GROUP BY cntp.code_container ,cntp.label_container, cntp.Plan_Classement, Years
HAVING
cntp.Years=' || l_counter ||'
AND
MAX(TO_DATE(cntp.DISPOSITION_DATE,''DD/MM/YYYY'')) BETWEEN TO_DATE(''01/01/'|| l_counter ||''',''DD/MM/YYYY'') AND TO_DATE(''31/12/' || l_counter ||''',''DD/MM/YYYY'')
AND SUM(cntp.IsA)=0
AND SUM(cntp.IsB)=0

UNION
';

  END LOOP;

  l_sql_queryFinal := SUBSTR(l_sql_query,  0,  LENGTH (l_sql_query) - 6);
  l_sql_queryFinal := l_sql_queryFinal||';';

  dbms_output.put_line(l_sql_queryFinal);
END;
/

db<>fiddle

第一个赋值中的 q[^...^]the alternative quoting mechanism,这意味着您不必转义(通过 doubling-up)该字符串中的引号,围绕 'DESTYORED'.请注意 ^ 分隔符不会出现在最终生成的查询中。

生成的查询是否真正按照您的要求执行是另一回事... cntp.Years= 部分可能应该在 where 子句中,而不是 having;并且您可能能够将其简化为单个查询而不是许多联合,因为您已经在聚合。不过,所有这些都超出了您的问题范围。

正如已经指出的那样,您似乎误解了 with 子句是什么:它是 SQL 语句的子句,而不是程序声明。我的定义,后面必须是select.

但是,作为一般规则,我建议尽可能避免使用动态 SQL。在这种情况下,如果您可以使用所需的年份范围模拟 table,则可以加入,而不必多次 运行 相同的查询。

这样做的简单技巧是使用 Oracle 的 connect by 语法来使用递归查询来生成预期的行数。

完成后,将此 table 添加为非常简单的连接:

WITH cntp AS
(
                SELECT DISTINCT code code_container,
                                [additional columns]
                FROM            container
                WHERE           status !='DESTROYED') cntc,
(
       SELECT to_date('01/01/'
                     || (LEVEL+2019), 'dd/mm/yyyy') AS start_date,
              to_date('31/12/'
                     || (LEVEL+2019), 'dd/mm/yyyy') AS end_date,
              (LEVEL+2019)                          AS year
       FROM   dual
              CONNECT BY LEVEL <= 11) year_table
SELECT   cntp.code_container,
         [additional columns]
FROM     cntp
join     year_table
ON       cntp.years = year_table.year
GROUP BY [additional columns],
         years,
         year_table.start_date,
         year_table.end_date
HAVING   max(to_date(cntp.disposition_date,''dd/mm/yyyy'')) BETWEEN year_table.start_date AND year_table.end_date
AND      SUM(cntp.isa)=0
AND      SUM(cntp.isb)=0

(此查询完全未经测试,可能无法真正满足您的需求;我根据可用信息提供了我的最佳近似值。)