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;
/
第一个赋值中的 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
(此查询完全未经测试,可能无法真正满足您的需求;我根据可用信息提供了我的最佳近似值。)
我想打印出动态构建的查询。然而我被困在变量声明中;第 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;
/
第一个赋值中的 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
(此查询完全未经测试,可能无法真正满足您的需求;我根据可用信息提供了我的最佳近似值。)