动态 SQL 循环遍历多个模式中的相同 table
Dynamic SQL to loop through same table in multiple schemas
我正在尝试创建一个 SQL 语句,其中包含每个模式的 DBA_USERS 的基本信息以及每个模式中特定 table 的 select 作为同一声明的一部分。
我从 StackExchange 上类似问题的其他答案中拼凑了部分陈述:
DECLARE
v_sql varchar2(4000);
cursor c1 is
select o.owner
, o.object_name
, u.created
, TO_CHAR(round(sum(ds.bytes)/1024/1024/1024,'0000'))||' GB'
from dba_users u
, dba_objects o
, dba_segments ds
WHERE u.account_status = 'OPEN'
and u.DEFAULT_TABLESPACE not in ('SYSAUX','SYSTEM')
and u.username=o.owner
and o.object_name='MASTER'
and o.object_type='TABLE'
and ds.owner =o.owner;
BEGIN
for REC in c1 loop
v_sql := 'select VERSION from '||REC.owner||'.'||REC.object_name;
EXECUTE IMMEDIATE v_sql;
end loop;
END;
/
这条语句运行但不会显示任何结果,因为我认为它应该使用批量收集器并使用 DBMS_OUTPUT.PUT_LINE
打印输出
输出应该是这样的:
USERNAME CREATED SIZE VERSION
SchemaA 2021-01-01 20GB 1.1
SchemaB 2021-01-02 22GB 1.2.2
SchemaC 2021-01-03 18GB 1.5.8
首先,我应该如何重写上面的语句以输出到会话,其次,是否可以return我期望的结果?
获得所需结果的一个选项是使用 pipelined functions
。他们以 table.
的形式提供结果
顺便说一句,您的查询并不完全正确,因为您需要加入更多的元素。这就是为什么总是最好使用 ANSI
语法。但是,我会保留您的语法,以便您更轻松地进行解释。
让我给你举个例子。我没有这个字段 version
,所以我使用的是行计数器:
首先我们需要创建两种类型,一种是object
,另一种是table of
。第一个是行,第二个是table构造。
SQL> CREATE OR REPLACE TYPE t_tf_row AS OBJECT ( username varchar2(40), created_date date, size_mb varchar2(10), counter number );
/
Type created.
SQL> CREATE TYPE t_tf_tab IS TABLE OF t_tf_row;
/
Type created.
现在,我们创建一个与您的非常相似的pipelined function
。
SQL> CREATE OR REPLACE FUNCTION get_schema_details RETURN t_tf_tab PIPELINED
AS
v_sql varchar2(4000);
v_counter pls_integer;
BEGIN
for h in
(
select o.owner
, o.object_name
, u.created
, round(ds.bytes/1024/1024/1024) as table_size
from dba_users u
, dba_objects o
, dba_segments ds
WHERE u.account_status = 'OPEN'
and u.DEFAULT_TABLESPACE not in ('SYSAUX','SYSTEM')
and u.username=o.owner
and u.username=ds.owner
and o.object_name = ds.segment_name
and o.object_type = ds.segment_type
and o.object_name='ODSPOSTING'
and o.object_type='TABLE'
)
loop
v_sql := 'select count(*) from '||h.owner||'.'||h.object_name;
EXECUTE IMMEDIATE v_sql into v_counter;
PIPE ROW(t_tf_row(h.owner,h.created,h.table_size,v_counter));
end loop;
END;
/
Function created.
SQL> select * from table(get_schema_details());
USERNAME CREATED_D SIZE_MB COUNTER
---------------------------------------- --------- ---------- ----------
ODSVIEWS 24-MAR-20 14 71853408
ALFAODS 20-DEC-19 14 71853408
您可以根据需要使函数动态化,例如引入输入参数而不是对值进行硬编码。
更新
您的测试用例场景
SQL> CREATE USER SCHEMA1 IDENTIFIED BY Oracle_1234
DEFAULT TABLESPACE USERS
TEMPORARY TABLESPACE TEMP_GROUP; 2 3
User created.
SQL> GRANT CREATE TABLE TO SCHEMA1;
Grant succeeded.
SQL> GRANT UNLIMITED TABLESPACE TO SCHEMA1;
Grant succeeded.
SQL> CREATE USER SCHEMA2 IDENTIFIED BY Oracle_1234
DEFAULT TABLESPACE USERS
TEMPORARY TABLESPACE TEMP_GROUP;
User created.
SQL> GRANT CREATE TABLE TO SCHEMA2;
Grant succeeded.
SQL> GRANT UNLIMITED TABLESPACE TO SCHEMA2;
Grant succeeded.
SQL> CREATE TABLE SCHEMA1.MASTER(VERSION VARCHAR2(6 BYTE));
Table created.
SQL> CREATE TABLE SCHEMA2.MASTER(VERSION VARCHAR2(6 BYTE));
Table created.
SQL> INSERT INTO "SCHEMA1"."MASTER" (VERSION) VALUES ('1.1.0');
1 row created.
SQL> COMMIT;
Commit complete.
SQL> INSERT INTO "SCHEMA2"."MASTER" (VERSION) VALUES ('2.2.0');
1 row created.
SQL> COMMIT;
Commit complete.
现在我们创建类型和函数。
SQL> CREATE OR REPLACE TYPE t_tf_row AS OBJECT ( username varchar2(40), created_date DATE, size_mb varchar2(10), counter NUMBER );
2 /
Type created.
SQL> CREATE OR REPLACE TYPE t_tf_tab IS TABLE OF t_tf_row;
2 /
Type created.
SQL> CREATE OR REPLACE FUNCTION get_master_version_details RETURN t_tf_tab PIPELINED
AS
v_sql varchar2(4000);
2 3 4 v_counter pls_integer;
BEGIN
5 6 FOR h IN
(
SELECT o.owner
7 8 9 , o.object_name
, u.created
, round(ds.bytes/1024/1024/1024) AS table_size
10 11 12 FROM dba_users u
, dba_objects o
, dba_segments ds
13 14 15 WHERE u.account_status = 'OPEN'
AND u.DEFAULT_TABLESPACE NOT IN ('SYSAUX','SYSTEM')
AND u.username=o.owner
16 17 18 AND u.username=ds.owner
AND o.object_name = ds.segment_name
AND o.object_type = ds.segment_type
19 20 21 AND o.object_name='MASTER'
AND o.object_type='TABLE'
)
22 23 24 loop
v_sql := 'select count(*) from '||h.owner||'.'||h.object_name;
EXECUTE IMMEDIATE v_sql INTO v_counter;
25 26 27 PIPE ROW(t_tf_row(h.owner,h.created,h.table_size,v_counter));
END loop;
END;
28 29 30 /
Function created.
SQL> SELECT COUNT(*) FROM all_objects WHERE object_name='MASTER' AND object_type='TABLE';
COUNT(*)
----------
2
SQL> SELECT * FROM TABLE(get_master_version_details());
USERNAME CREATED_D SIZE_MB COUNTER
---------------------------------------- --------- ---------- ----------
SCHEMA1 28-SEP-21 0 1
SCHEMA2 28-SEP-21 0 1
为什么你的情况不起作用?您必须在 user/schema 中安装函数和类型,并具有 运行 您正在执行的操作的正确权限。
在我上面的例子中,作为测试,我确实在我的 sys 模式上安装了函数和类型(你不应该这样做)。所以,让我们删除函数和类型,并为此创建一个额外的用户,我们将其称为 schema3
SQL> DROP TYPE t_tf_tab;
Type dropped.
SQL> DROP TYPE t_tf_row;
Type dropped.
SQL> DROP FUNCTION get_master_version_details;
Function dropped.
SQL> create user schema3 identified by Oracle_1234 default tablespace users temporary tablespace temp_group ;
User created.
SQL> grant select any table, create procedure, create table, select any dictionary to schema3 ;
Grant succeeded.
SQL> CREATE OR REPLACE TYPE schema3.t_tf_row AS OBJECT ( username varchar2(40), created_date DATE, size_mb varchar2(10), counter NUMBER );
2 /
Type created.
SQL> CREATE OR REPLACE TYPE schema3.t_tf_tab IS TABLE OF t_tf_row;
2 /
Type created.
SQL> CREATE OR REPLACE FUNCTION schema3.get_master_version_details RETURN t_tf_tab PIPELINED
AS
v_sql varchar2(4000);
v_counter pls_integer;
BEGIN
2 3 4 5 6 FOR h IN
7 (
SELECT o.owner
, o.object_name
8 9 10 , u.created
, round(ds.bytes/1024/1024/1024) AS table_size
FROM dba_users u
, dba_objects o
, dba_segments ds
WHERE u.account_status = 'OPEN'
11 12 13 14 15 16 AND u.DEFAULT_TABLESPACE NOT IN ('SYSAUX','SYSTEM')
AND u.username=o.owner
AND u.username=ds.owner
17 18 19 AND o.object_name = ds.segment_name
AND o.object_type = ds.segment_type
AND o.object_name='MASTER'
20 21 22 AND o.object_type='TABLE'
23 )
loop
24 25 v_sql := 'select count(*) from '||h.owner||'.'||h.object_name;
EXECUTE IMMEDIATE v_sql INTO v_counter;
PIPE ROW(t_tf_row(h.owner,h.created,h.table_size,v_counter));
26 27 28 END loop;
END;
/ 29 30
Function created.
SQL> SELECT * FROM TABLE(schema3.get_master_version_details());
USERNAME CREATED_D SIZE_MB COUNTER
---------------------------------------- --------- ---------- ----------
SCHEMA1 28-SEP-21 0 1
SCHEMA2 28-SEP-21 0 1
请注意我授予 schema3
的权限,以便流水线函数工作。
你没有'seeing'任何结果的原因是PL/SQL完全在服务器内运行。它与客户端没有连接,也无法访问客户端的输出显示。您需要使用 DBMS_OUTPUT.PUT_LINE 程序(在文档中查找)。该过程写入一个缓冲区,然后在该过程完成时将其返回给客户端。然后由客户端处理该缓冲区。如果使用 sqlplus,您可以将其配置为在调用任何过程之前通过 'set serverout on' 将输出显示为会话设置。
此外,我将重写您的过程以消除显式游标并使用 CURSOR FOR 循环:(我还将转换为用户 ANSI 标准 JOIN 语法,但我不打算在这里花时间分析查询以弄清楚如何转换那个_)。另外,我根本看不出程序是如何运行的,因为循环中的 SELECT 需要一个 INTO 子句来放置结果。
DECLARE
v_sql varchar2(4000);
v_version varchar2(80);
BEGIN
for REC in (select o.owner
,o.object_name
,u.created
,TO_CHAR(round(sum(ds.bytes)/1024/1024/1024,'0000'))||' GB'
from dba_users u
,dba_objects o
,dba_segments ds
WHERE u.account_status = 'OPEN'
and u.DEFAULT_TABLESPACE not in ('SYSAUX','SYSTEM')
and u.username=o.owner
and o.object_name='MASTER'
and o.object_type='TABLE'
and ds.owner =o.owner;)
loop
v_sql := 'select VERSION from '||REC.owner||'.'||REC.object_name into v_version;
EXECUTE IMMEDIATE v_sql;
dbms_output.put_line('Version is '||v_version);
end loop;
END;
/
问题是
execute immediate v_sql;
没有输出。它需要一个 into
子句,并需要一些东西来显示它:
declare
demo_text varchar2(50);
begin
execute immediate 'select 2 + 2 as demo from dual'
into demo_text;
dbms_output.put_line(demo_text);
end;
4
顺便说一下,我建议在 end;
和 END;
之间做出决定(记住这不是 COBOL)。
我正在尝试创建一个 SQL 语句,其中包含每个模式的 DBA_USERS 的基本信息以及每个模式中特定 table 的 select 作为同一声明的一部分。
我从 StackExchange 上类似问题的其他答案中拼凑了部分陈述:
DECLARE
v_sql varchar2(4000);
cursor c1 is
select o.owner
, o.object_name
, u.created
, TO_CHAR(round(sum(ds.bytes)/1024/1024/1024,'0000'))||' GB'
from dba_users u
, dba_objects o
, dba_segments ds
WHERE u.account_status = 'OPEN'
and u.DEFAULT_TABLESPACE not in ('SYSAUX','SYSTEM')
and u.username=o.owner
and o.object_name='MASTER'
and o.object_type='TABLE'
and ds.owner =o.owner;
BEGIN
for REC in c1 loop
v_sql := 'select VERSION from '||REC.owner||'.'||REC.object_name;
EXECUTE IMMEDIATE v_sql;
end loop;
END;
/
这条语句运行但不会显示任何结果,因为我认为它应该使用批量收集器并使用 DBMS_OUTPUT.PUT_LINE
打印输出输出应该是这样的:
USERNAME CREATED SIZE VERSION
SchemaA 2021-01-01 20GB 1.1
SchemaB 2021-01-02 22GB 1.2.2
SchemaC 2021-01-03 18GB 1.5.8
首先,我应该如何重写上面的语句以输出到会话,其次,是否可以return我期望的结果?
获得所需结果的一个选项是使用 pipelined functions
。他们以 table.
顺便说一句,您的查询并不完全正确,因为您需要加入更多的元素。这就是为什么总是最好使用 ANSI
语法。但是,我会保留您的语法,以便您更轻松地进行解释。
让我给你举个例子。我没有这个字段 version
,所以我使用的是行计数器:
首先我们需要创建两种类型,一种是object
,另一种是table of
。第一个是行,第二个是table构造。
SQL> CREATE OR REPLACE TYPE t_tf_row AS OBJECT ( username varchar2(40), created_date date, size_mb varchar2(10), counter number );
/
Type created.
SQL> CREATE TYPE t_tf_tab IS TABLE OF t_tf_row;
/
Type created.
现在,我们创建一个与您的非常相似的pipelined function
。
SQL> CREATE OR REPLACE FUNCTION get_schema_details RETURN t_tf_tab PIPELINED
AS
v_sql varchar2(4000);
v_counter pls_integer;
BEGIN
for h in
(
select o.owner
, o.object_name
, u.created
, round(ds.bytes/1024/1024/1024) as table_size
from dba_users u
, dba_objects o
, dba_segments ds
WHERE u.account_status = 'OPEN'
and u.DEFAULT_TABLESPACE not in ('SYSAUX','SYSTEM')
and u.username=o.owner
and u.username=ds.owner
and o.object_name = ds.segment_name
and o.object_type = ds.segment_type
and o.object_name='ODSPOSTING'
and o.object_type='TABLE'
)
loop
v_sql := 'select count(*) from '||h.owner||'.'||h.object_name;
EXECUTE IMMEDIATE v_sql into v_counter;
PIPE ROW(t_tf_row(h.owner,h.created,h.table_size,v_counter));
end loop;
END;
/
Function created.
SQL> select * from table(get_schema_details());
USERNAME CREATED_D SIZE_MB COUNTER
---------------------------------------- --------- ---------- ----------
ODSVIEWS 24-MAR-20 14 71853408
ALFAODS 20-DEC-19 14 71853408
您可以根据需要使函数动态化,例如引入输入参数而不是对值进行硬编码。
更新
您的测试用例场景
SQL> CREATE USER SCHEMA1 IDENTIFIED BY Oracle_1234
DEFAULT TABLESPACE USERS
TEMPORARY TABLESPACE TEMP_GROUP; 2 3
User created.
SQL> GRANT CREATE TABLE TO SCHEMA1;
Grant succeeded.
SQL> GRANT UNLIMITED TABLESPACE TO SCHEMA1;
Grant succeeded.
SQL> CREATE USER SCHEMA2 IDENTIFIED BY Oracle_1234
DEFAULT TABLESPACE USERS
TEMPORARY TABLESPACE TEMP_GROUP;
User created.
SQL> GRANT CREATE TABLE TO SCHEMA2;
Grant succeeded.
SQL> GRANT UNLIMITED TABLESPACE TO SCHEMA2;
Grant succeeded.
SQL> CREATE TABLE SCHEMA1.MASTER(VERSION VARCHAR2(6 BYTE));
Table created.
SQL> CREATE TABLE SCHEMA2.MASTER(VERSION VARCHAR2(6 BYTE));
Table created.
SQL> INSERT INTO "SCHEMA1"."MASTER" (VERSION) VALUES ('1.1.0');
1 row created.
SQL> COMMIT;
Commit complete.
SQL> INSERT INTO "SCHEMA2"."MASTER" (VERSION) VALUES ('2.2.0');
1 row created.
SQL> COMMIT;
Commit complete.
现在我们创建类型和函数。
SQL> CREATE OR REPLACE TYPE t_tf_row AS OBJECT ( username varchar2(40), created_date DATE, size_mb varchar2(10), counter NUMBER );
2 /
Type created.
SQL> CREATE OR REPLACE TYPE t_tf_tab IS TABLE OF t_tf_row;
2 /
Type created.
SQL> CREATE OR REPLACE FUNCTION get_master_version_details RETURN t_tf_tab PIPELINED
AS
v_sql varchar2(4000);
2 3 4 v_counter pls_integer;
BEGIN
5 6 FOR h IN
(
SELECT o.owner
7 8 9 , o.object_name
, u.created
, round(ds.bytes/1024/1024/1024) AS table_size
10 11 12 FROM dba_users u
, dba_objects o
, dba_segments ds
13 14 15 WHERE u.account_status = 'OPEN'
AND u.DEFAULT_TABLESPACE NOT IN ('SYSAUX','SYSTEM')
AND u.username=o.owner
16 17 18 AND u.username=ds.owner
AND o.object_name = ds.segment_name
AND o.object_type = ds.segment_type
19 20 21 AND o.object_name='MASTER'
AND o.object_type='TABLE'
)
22 23 24 loop
v_sql := 'select count(*) from '||h.owner||'.'||h.object_name;
EXECUTE IMMEDIATE v_sql INTO v_counter;
25 26 27 PIPE ROW(t_tf_row(h.owner,h.created,h.table_size,v_counter));
END loop;
END;
28 29 30 /
Function created.
SQL> SELECT COUNT(*) FROM all_objects WHERE object_name='MASTER' AND object_type='TABLE';
COUNT(*)
----------
2
SQL> SELECT * FROM TABLE(get_master_version_details());
USERNAME CREATED_D SIZE_MB COUNTER
---------------------------------------- --------- ---------- ----------
SCHEMA1 28-SEP-21 0 1
SCHEMA2 28-SEP-21 0 1
为什么你的情况不起作用?您必须在 user/schema 中安装函数和类型,并具有 运行 您正在执行的操作的正确权限。
在我上面的例子中,作为测试,我确实在我的 sys 模式上安装了函数和类型(你不应该这样做)。所以,让我们删除函数和类型,并为此创建一个额外的用户,我们将其称为 schema3
SQL> DROP TYPE t_tf_tab;
Type dropped.
SQL> DROP TYPE t_tf_row;
Type dropped.
SQL> DROP FUNCTION get_master_version_details;
Function dropped.
SQL> create user schema3 identified by Oracle_1234 default tablespace users temporary tablespace temp_group ;
User created.
SQL> grant select any table, create procedure, create table, select any dictionary to schema3 ;
Grant succeeded.
SQL> CREATE OR REPLACE TYPE schema3.t_tf_row AS OBJECT ( username varchar2(40), created_date DATE, size_mb varchar2(10), counter NUMBER );
2 /
Type created.
SQL> CREATE OR REPLACE TYPE schema3.t_tf_tab IS TABLE OF t_tf_row;
2 /
Type created.
SQL> CREATE OR REPLACE FUNCTION schema3.get_master_version_details RETURN t_tf_tab PIPELINED
AS
v_sql varchar2(4000);
v_counter pls_integer;
BEGIN
2 3 4 5 6 FOR h IN
7 (
SELECT o.owner
, o.object_name
8 9 10 , u.created
, round(ds.bytes/1024/1024/1024) AS table_size
FROM dba_users u
, dba_objects o
, dba_segments ds
WHERE u.account_status = 'OPEN'
11 12 13 14 15 16 AND u.DEFAULT_TABLESPACE NOT IN ('SYSAUX','SYSTEM')
AND u.username=o.owner
AND u.username=ds.owner
17 18 19 AND o.object_name = ds.segment_name
AND o.object_type = ds.segment_type
AND o.object_name='MASTER'
20 21 22 AND o.object_type='TABLE'
23 )
loop
24 25 v_sql := 'select count(*) from '||h.owner||'.'||h.object_name;
EXECUTE IMMEDIATE v_sql INTO v_counter;
PIPE ROW(t_tf_row(h.owner,h.created,h.table_size,v_counter));
26 27 28 END loop;
END;
/ 29 30
Function created.
SQL> SELECT * FROM TABLE(schema3.get_master_version_details());
USERNAME CREATED_D SIZE_MB COUNTER
---------------------------------------- --------- ---------- ----------
SCHEMA1 28-SEP-21 0 1
SCHEMA2 28-SEP-21 0 1
请注意我授予 schema3
的权限,以便流水线函数工作。
你没有'seeing'任何结果的原因是PL/SQL完全在服务器内运行。它与客户端没有连接,也无法访问客户端的输出显示。您需要使用 DBMS_OUTPUT.PUT_LINE 程序(在文档中查找)。该过程写入一个缓冲区,然后在该过程完成时将其返回给客户端。然后由客户端处理该缓冲区。如果使用 sqlplus,您可以将其配置为在调用任何过程之前通过 'set serverout on' 将输出显示为会话设置。
此外,我将重写您的过程以消除显式游标并使用 CURSOR FOR 循环:(我还将转换为用户 ANSI 标准 JOIN 语法,但我不打算在这里花时间分析查询以弄清楚如何转换那个_)。另外,我根本看不出程序是如何运行的,因为循环中的 SELECT 需要一个 INTO 子句来放置结果。
DECLARE
v_sql varchar2(4000);
v_version varchar2(80);
BEGIN
for REC in (select o.owner
,o.object_name
,u.created
,TO_CHAR(round(sum(ds.bytes)/1024/1024/1024,'0000'))||' GB'
from dba_users u
,dba_objects o
,dba_segments ds
WHERE u.account_status = 'OPEN'
and u.DEFAULT_TABLESPACE not in ('SYSAUX','SYSTEM')
and u.username=o.owner
and o.object_name='MASTER'
and o.object_type='TABLE'
and ds.owner =o.owner;)
loop
v_sql := 'select VERSION from '||REC.owner||'.'||REC.object_name into v_version;
EXECUTE IMMEDIATE v_sql;
dbms_output.put_line('Version is '||v_version);
end loop;
END;
/
问题是
execute immediate v_sql;
没有输出。它需要一个 into
子句,并需要一些东西来显示它:
declare
demo_text varchar2(50);
begin
execute immediate 'select 2 + 2 as demo from dual'
into demo_text;
dbms_output.put_line(demo_text);
end;
4
顺便说一下,我建议在 end;
和 END;
之间做出决定(记住这不是 COBOL)。