删除动态查询中最后一条记录末尾的 'union all'
Removing 'union all' at end of last record in dynamic query
对于我的问题,我设置了一个简单的例子来说明我的问题。
假设您有一个动态查询,它生成几个 select 语句,它们之间有一个 UNION ALL。有没有办法不让最后一条记录末尾的'UNION ALL'出现?
我的例子:
CREATE OR REPLACE PROCEDURE PROC_TEST AS
BEGIN
DECLARE
DDL_STRING CLOB;
BEGIN
FOR x IN (SELECT TABLE_NAME FROM HLP_TABLES WHERE ENABLED = 1)
LOOP
DDL_STRING := 'SELECT ID FROM ' || x.TABLE_NAME || ' UNION ALL ';
DBMS_OUTPUT.PUT_LINE(DDL_STRING);
END LOOP;
END;
END PROC_TEST;
这样做:
CREATE OR REPLACE PROCEDURE PROC_TEST AS
DDL_STRING CLOB;
BEGIN
FOR x IN (SELECT TABLE_NAME FROM HLP_TABLES WHERE ENABLED = 1)
LOOP
DDL_STRING := DDL_STRING||' SELECT ID FROM ' || x.TABLE_NAME || ' UNION ALL';
END LOOP;
DDL_STRING := REGEXP_REPLACE(DDL_STRING, ' UNION ALL$');
DBMS_OUTPUT.PUT_LINE(DDL_STRING);
END PROC_TEST;
如果您确定ddl的总长度不超过4000个字符,您可以使用LISTAGG
并避免循环。
CREATE OR REPLACE PROCEDURE proc_test
AS
DECLARE
ddl_string CLOB;
BEGIN
SELECT
LISTAGG('SELECT ID FROM ' || table_name,' UNION ALL ') WITHIN GROUP(
ORDER BY table_name)
INTO ddl_string
FROM HLP_TABLES
WHERE enabled = 1;
dbms_output.put_line(ddl_string);
END proc_test;
/
你可以在循环中使用counter来判断是否需要UNION ALL。
试试这个:
CREATE OR REPLACE PROCEDURE PROC_TEST AS
BEGIN
DECLARE
v_counter number := 1;
DDL_STRING CLOB;
BEGIN
FOR x IN (SELECT TABLE_NAME FROM HLP_TABLES WHERE ENABLED = 1) LOOP
DDL_STRING := case
when v_counter > 1 then
' UNION ALL ' end || ' SELECT ID FROM ' || x.TABLE_NAME;
DBMS_OUTPUT.PUT_LINE(DDL_STRING);
v_counter := v_counter + 1;
END LOOP;
END;
END PROC_TEST;
当生成这样的代码时,我通常使用一个简单的 if 语句,例如:
create or replace procedure proc_test as
ddl_string clob;
begin
for x in (select table_name from hlp_tables where enabled = 1)
loop
if ddl_string is not null then
ddl_string := ddl_string || ' union all ';
end if;
ddl_string := ddl_string || ' select id from ' || x.table_name;
end loop;
dbms_output.put_line(ddl_string);
end proc_test;
对于我的问题,我设置了一个简单的例子来说明我的问题。
假设您有一个动态查询,它生成几个 select 语句,它们之间有一个 UNION ALL。有没有办法不让最后一条记录末尾的'UNION ALL'出现?
我的例子:
CREATE OR REPLACE PROCEDURE PROC_TEST AS
BEGIN
DECLARE
DDL_STRING CLOB;
BEGIN
FOR x IN (SELECT TABLE_NAME FROM HLP_TABLES WHERE ENABLED = 1)
LOOP
DDL_STRING := 'SELECT ID FROM ' || x.TABLE_NAME || ' UNION ALL ';
DBMS_OUTPUT.PUT_LINE(DDL_STRING);
END LOOP;
END;
END PROC_TEST;
这样做:
CREATE OR REPLACE PROCEDURE PROC_TEST AS
DDL_STRING CLOB;
BEGIN
FOR x IN (SELECT TABLE_NAME FROM HLP_TABLES WHERE ENABLED = 1)
LOOP
DDL_STRING := DDL_STRING||' SELECT ID FROM ' || x.TABLE_NAME || ' UNION ALL';
END LOOP;
DDL_STRING := REGEXP_REPLACE(DDL_STRING, ' UNION ALL$');
DBMS_OUTPUT.PUT_LINE(DDL_STRING);
END PROC_TEST;
如果您确定ddl的总长度不超过4000个字符,您可以使用LISTAGG
并避免循环。
CREATE OR REPLACE PROCEDURE proc_test
AS
DECLARE
ddl_string CLOB;
BEGIN
SELECT
LISTAGG('SELECT ID FROM ' || table_name,' UNION ALL ') WITHIN GROUP(
ORDER BY table_name)
INTO ddl_string
FROM HLP_TABLES
WHERE enabled = 1;
dbms_output.put_line(ddl_string);
END proc_test;
/
你可以在循环中使用counter来判断是否需要UNION ALL。 试试这个:
CREATE OR REPLACE PROCEDURE PROC_TEST AS
BEGIN
DECLARE
v_counter number := 1;
DDL_STRING CLOB;
BEGIN
FOR x IN (SELECT TABLE_NAME FROM HLP_TABLES WHERE ENABLED = 1) LOOP
DDL_STRING := case
when v_counter > 1 then
' UNION ALL ' end || ' SELECT ID FROM ' || x.TABLE_NAME;
DBMS_OUTPUT.PUT_LINE(DDL_STRING);
v_counter := v_counter + 1;
END LOOP;
END;
END PROC_TEST;
当生成这样的代码时,我通常使用一个简单的 if 语句,例如:
create or replace procedure proc_test as
ddl_string clob;
begin
for x in (select table_name from hlp_tables where enabled = 1)
loop
if ddl_string is not null then
ddl_string := ddl_string || ' union all ';
end if;
ddl_string := ddl_string || ' select id from ' || x.table_name;
end loop;
dbms_output.put_line(ddl_string);
end proc_test;