嵌套循环从 Oracle 中的多个表中检索值
Nested Loops to Retrieve a value from several tables in Oracle
假设我有几个表都以 'PLAYER_' 开头,我试图遍历所有这些表以获取表名,然后再次循环以获取所有这些表中列的值.
此列存在于所有表中,因此我想使用嵌套的 FOR 循环来实现。
这是我目前所拥有的,但它似乎不起作用:
DECLARE
LOG_ID NUMBER;
TBL_NME VARCHAR2(30);
V_STRNG VARCHAR2(4000);
BEGIN
FOR i IN (SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME LIKE 'PLAYER_%') LOOP
TBL_NME := i.TABLE_NAME;
DBMS_OUTPUT.PUT_LINE('TABLE EXTRACTED IS ' || TBL_NME);
FOR j IN(SELECT LOG_ID FROM i.TABLE_NAME) LOOP
V_EXEC_OBJ_STRNG := 'SELECT LOG_ID FROM ' || i.TABLE_NAME;
EXECUTE IMMEDIATE V_STRNG INTO LOG_ID;
DBMS_OUTPUT.PUT_LINE('LOG_ID IS ' || LOG_ID || ' FOR TABLE ' || i.TABLE_NAME);
END LOOP;
END LOOP;
END;
/
你可能只需要一个循环就可以逃脱......
例子
create table player_01 ( id, name )
as
select level, dbms_random.string( 'x', 25 )
from dual
connect by level <= 10 ;
create table player_02 ( id, name )
as
select level, dbms_random.string( 'x', 25 )
from dual
connect by level <= 11 ;
create table player_03 ( id, name )
as
select level, dbms_random.string( 'x', 25 )
from dual
connect by level <= 12 ;
匿名块:
-- find all relevant tables and retrieve the highest id values
declare
logid number := 0 ;
tablename varchar2( 30 ) := '' ;
v_string varchar2( 4000 ) := '' ;
begin
for r in (
select table_name from user_tables
where table_name like 'PLAYER%'
order by table_name
) loop
-- dbms_output.put_line( ' current table -> ' || r.table_name ) ;
v_string := 'select max( id ) as logid from ' || r.table_name;
execute immediate v_string into logid ;
dbms_output.put_line( 'log id is ' || logid || ' for table ' || r.table_name ) ;
end loop ;
end ;
/
-- result
log id is 10 for table PLAYER_01
log id is 11 for table PLAYER_02
log id is 12 for table PLAYER_03
根据您的评论,每个PLAYER_中有几个LOGID_ table。也许下面的例子更接近"real thing"。 (并且:匿名块具有嵌套循环......(使用 Oracle 12c 和 11g 测试,dbfiddle here)。
表
create table player_01 ( id, details, logid )
as
select level, dbms_random.string( 'x', 25 ), abs( dbms_random.random() )
from dual
connect by level <= 3 ;
create table player_02 ( id, details, logid )
as
select level, dbms_random.string( 'x', 25 ), abs( dbms_random.random() )
from dual
connect by level <= 4 ;
create table player_03 ( id, details, logid )
as
select level, dbms_random.string( 'x', 25 ), abs( dbms_random.random() )
from dual
connect by level <= 4 ;
样本数据在PLAYER_01/PLAYER_02/PLAYER_03
select * from player_01 ;
ID DETAILS LOGID
1 VZAQXPFCQK3U2F0RL32I31N40 699945134
2 32QWFFMUCF1DL6E3Z5QM4DSWY 1635628934
3 48GWBETOLUSDEFA3SMY061NUO 1237793316
select * from player_02;
ID DETAILS LOGID
1 HS827U4VCY853N8DKTI98J82D 1993524164
2 XLYS0XPJG0IQP4BNKDQ0ZITPA 1665941353
3 DWVVR5O6N5T1HP5MDYHVH3NZJ 1129581845
4 L7N8HCPVTHP466WJ5TCQ04YHE 794237444
select * from player_03;
ID DETAILS LOGID
1 SYVX5G2FE5IC1MI6TCSAHNOUU 720476135
2 4IQZIG6DAUCWW3APJY5OZ63TF 287457960
3 525NMZFVGLWKIT7EIFA41C8MB 784891618
4 0XHJXV2O4TCQQSITOTIQCO3AA 1578737054
匿名块
declare
logid number := 0 ;
tablename varchar2( 30 ) := '' ;
v_string1 varchar2( 4000 ) := '' ;
v_string2 varchar2( 4000 ) := '' ;
rowcount number := 0 ;
begin
for r in (
select table_name from user_tables
where table_name like 'PLAYER%'
order by table_name
) loop
v_string1 := 'select count(*) from ' || r.table_name ;
execute immediate v_string1 into rowcount ;
dbms_output.put_line( rowcount ) ;
for rn in 1 .. rowcount
loop
-- dbms_output.put_line( rn ) ;
v_string2 := 'select logid from ( '
|| 'select logid, row_number() over ( order by id ) rn '
|| ' from ' || r.table_name || ' )'
|| ' where rn = ' || rn;
-- dbms_output.put_line( v_string2 ) ;
execute immediate v_string2 into logid ;
dbms_output.put_line( 'log id is ' || logid || ' for table ' || r.table_name ) ;
end loop ;
end loop ;
end ;
/
dbms_output:
3
log id is 699945134 for table PLAYER_01
log id is 1635628934 for table PLAYER_01
log id is 1237793316 for table PLAYER_01
4
log id is 1993524164 for table PLAYER_02
log id is 1665941353 for table PLAYER_02
log id is 1129581845 for table PLAYER_02
log id is 794237444 for table PLAYER_02
4
log id is 720476135 for table PLAYER_03
log id is 287457960 for table PLAYER_03
log id is 784891618 for table PLAYER_03
log id is 1578737054 for table PLAYER_03
第二个查询字符串 (v_string2) 看起来有点像这样(可能比所有字符串部分和 || 更容易阅读):
select logid
from (
select
logid
, row_number() over ( order by id ) rn
from player_01
) where rn = 1
;
-- query result
LOGID
1338793259
在内循环中查询
(回答您评论中的问题)
子查询使用 row_number() - 参见 documentation:
"ROW_NUMBER is an analytic function. It assigns a unique number to
each row to which it is applied (either each row in the partition or
each row returned by the query), in the ordered sequence of rows
specified in the order_by_clause, beginning with 1."
我们正在使用它来获取连续的数字,就像对 LOGID 进行编号一样。然后,我们在 WHERE 子句(外部 select 的)中使用 RN 值,并将它们与内部 FOR 循环的 "rn" 值进行比较。
select
logid
, row_number() over ( order by id ) rn
from player_01 ;
-- result
LOGID RN
1775991812 1
262095022 2
2090118607 3
假设我有几个表都以 'PLAYER_' 开头,我试图遍历所有这些表以获取表名,然后再次循环以获取所有这些表中列的值.
此列存在于所有表中,因此我想使用嵌套的 FOR 循环来实现。
这是我目前所拥有的,但它似乎不起作用:
DECLARE
LOG_ID NUMBER;
TBL_NME VARCHAR2(30);
V_STRNG VARCHAR2(4000);
BEGIN
FOR i IN (SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME LIKE 'PLAYER_%') LOOP
TBL_NME := i.TABLE_NAME;
DBMS_OUTPUT.PUT_LINE('TABLE EXTRACTED IS ' || TBL_NME);
FOR j IN(SELECT LOG_ID FROM i.TABLE_NAME) LOOP
V_EXEC_OBJ_STRNG := 'SELECT LOG_ID FROM ' || i.TABLE_NAME;
EXECUTE IMMEDIATE V_STRNG INTO LOG_ID;
DBMS_OUTPUT.PUT_LINE('LOG_ID IS ' || LOG_ID || ' FOR TABLE ' || i.TABLE_NAME);
END LOOP;
END LOOP;
END;
/
你可能只需要一个循环就可以逃脱......
例子
create table player_01 ( id, name )
as
select level, dbms_random.string( 'x', 25 )
from dual
connect by level <= 10 ;
create table player_02 ( id, name )
as
select level, dbms_random.string( 'x', 25 )
from dual
connect by level <= 11 ;
create table player_03 ( id, name )
as
select level, dbms_random.string( 'x', 25 )
from dual
connect by level <= 12 ;
匿名块:
-- find all relevant tables and retrieve the highest id values
declare
logid number := 0 ;
tablename varchar2( 30 ) := '' ;
v_string varchar2( 4000 ) := '' ;
begin
for r in (
select table_name from user_tables
where table_name like 'PLAYER%'
order by table_name
) loop
-- dbms_output.put_line( ' current table -> ' || r.table_name ) ;
v_string := 'select max( id ) as logid from ' || r.table_name;
execute immediate v_string into logid ;
dbms_output.put_line( 'log id is ' || logid || ' for table ' || r.table_name ) ;
end loop ;
end ;
/
-- result
log id is 10 for table PLAYER_01
log id is 11 for table PLAYER_02
log id is 12 for table PLAYER_03
根据您的评论,每个PLAYER_中有几个LOGID_ table。也许下面的例子更接近"real thing"。 (并且:匿名块具有嵌套循环......(使用 Oracle 12c 和 11g 测试,dbfiddle here)。
表
create table player_01 ( id, details, logid )
as
select level, dbms_random.string( 'x', 25 ), abs( dbms_random.random() )
from dual
connect by level <= 3 ;
create table player_02 ( id, details, logid )
as
select level, dbms_random.string( 'x', 25 ), abs( dbms_random.random() )
from dual
connect by level <= 4 ;
create table player_03 ( id, details, logid )
as
select level, dbms_random.string( 'x', 25 ), abs( dbms_random.random() )
from dual
connect by level <= 4 ;
样本数据在PLAYER_01/PLAYER_02/PLAYER_03
select * from player_01 ;
ID DETAILS LOGID
1 VZAQXPFCQK3U2F0RL32I31N40 699945134
2 32QWFFMUCF1DL6E3Z5QM4DSWY 1635628934
3 48GWBETOLUSDEFA3SMY061NUO 1237793316
select * from player_02;
ID DETAILS LOGID
1 HS827U4VCY853N8DKTI98J82D 1993524164
2 XLYS0XPJG0IQP4BNKDQ0ZITPA 1665941353
3 DWVVR5O6N5T1HP5MDYHVH3NZJ 1129581845
4 L7N8HCPVTHP466WJ5TCQ04YHE 794237444
select * from player_03;
ID DETAILS LOGID
1 SYVX5G2FE5IC1MI6TCSAHNOUU 720476135
2 4IQZIG6DAUCWW3APJY5OZ63TF 287457960
3 525NMZFVGLWKIT7EIFA41C8MB 784891618
4 0XHJXV2O4TCQQSITOTIQCO3AA 1578737054
匿名块
declare
logid number := 0 ;
tablename varchar2( 30 ) := '' ;
v_string1 varchar2( 4000 ) := '' ;
v_string2 varchar2( 4000 ) := '' ;
rowcount number := 0 ;
begin
for r in (
select table_name from user_tables
where table_name like 'PLAYER%'
order by table_name
) loop
v_string1 := 'select count(*) from ' || r.table_name ;
execute immediate v_string1 into rowcount ;
dbms_output.put_line( rowcount ) ;
for rn in 1 .. rowcount
loop
-- dbms_output.put_line( rn ) ;
v_string2 := 'select logid from ( '
|| 'select logid, row_number() over ( order by id ) rn '
|| ' from ' || r.table_name || ' )'
|| ' where rn = ' || rn;
-- dbms_output.put_line( v_string2 ) ;
execute immediate v_string2 into logid ;
dbms_output.put_line( 'log id is ' || logid || ' for table ' || r.table_name ) ;
end loop ;
end loop ;
end ;
/
dbms_output:
3
log id is 699945134 for table PLAYER_01
log id is 1635628934 for table PLAYER_01
log id is 1237793316 for table PLAYER_01
4
log id is 1993524164 for table PLAYER_02
log id is 1665941353 for table PLAYER_02
log id is 1129581845 for table PLAYER_02
log id is 794237444 for table PLAYER_02
4
log id is 720476135 for table PLAYER_03
log id is 287457960 for table PLAYER_03
log id is 784891618 for table PLAYER_03
log id is 1578737054 for table PLAYER_03
第二个查询字符串 (v_string2) 看起来有点像这样(可能比所有字符串部分和 || 更容易阅读):
select logid
from (
select
logid
, row_number() over ( order by id ) rn
from player_01
) where rn = 1
;
-- query result
LOGID
1338793259
在内循环中查询 (回答您评论中的问题)
子查询使用 row_number() - 参见 documentation:
"ROW_NUMBER is an analytic function. It assigns a unique number to each row to which it is applied (either each row in the partition or each row returned by the query), in the ordered sequence of rows specified in the order_by_clause, beginning with 1."
我们正在使用它来获取连续的数字,就像对 LOGID 进行编号一样。然后,我们在 WHERE 子句(外部 select 的)中使用 RN 值,并将它们与内部 FOR 循环的 "rn" 值进行比较。
select
logid
, row_number() over ( order by id ) rn
from player_01 ;
-- result
LOGID RN
1775991812 1
262095022 2
2090118607 3