如何在 Oracle 中动态分析给定模式名和表名的元数据?
Posted
技术标签:
【中文标题】如何在 Oracle 中动态分析给定模式名和表名的元数据?【英文标题】:How to analyze metadata of given schema name and table name dynamically in Oracle? 【发布时间】:2021-09-01 07:43:55 【问题描述】:我正在尝试编写一个动态 oracle plsql 脚本,该脚本将 schema_name 和 table_name 作为参数并返回 Primaty Key 列名、索引标志(如果表包含索引返回 1 否则 0)、db 中表大小的数据量、行所选架构和表的计数和列数。这是我的代码
DECLARE
p_table_name VARCHAR2(1000);
p_owner_name VARCHAR2(1000);
v_pk_columns_name VARCHAR2(1000);
v_ind_exists_flg VARCHAR2(1000);
v_data_volume VARCHAR2(1000);
v_row_cnt VARCHAR2(1000);
v_column_cnt VARCHAR2(1000);
v_flag_one NUMBER;
v_flag_zero NUMBER;
v_constraint_type VARCHAR2(100);
BEGIN
v_flag_one := 1;
v_flag_zero := 0;
v_constraint_type := 'P';
p_owner_name := '0DS03';
p_table_name := 'ODS_SALES';
v_data_volume := 'SELECT SUM(bytes)/1024/1024 INTO v_db_size
FROM dba_segments
WHERE owner = ' || p_owner_name ||
'AND segment_name = ' || p_table_name;
EXECUTE IMMEDIATE v_data_volume;
v_row_cnt := 'SELECT COUNT(*) FROM ' || p_owner_name;
EXECUTE IMMEDIATE v_row_cnt;
v_ind_exists_flg := 'SELECT CASE WHEN (index_name) <> null THEN ' || v_flag_one || '
ELSE ' || v_flag_zero || '
END AS flag
FROM dba_ind_columns
WHERE table_owner =' || p_owner_name ||
'AND table_name =' || p_table_name;
EXECUTE IMMEDIATE v_ind_exists_flg;
v_column_cnt := 'SELECT COUNT(column_name)
FROM all_tab_columns
WHERE table_name =' || p_table_name ||
'AND owner = ' ||p_owner_name;
EXECUTE IMMEDIATE v_column_cnt;
v_pk_columns_name := 'SELECT cols.column_name
FROM all_constraints cons, all_cons_columns cols
WHERE cons.constraint_type = ' || v_constraint_type ||
'AND cols.owner = ' || p_owner_name ||
'AND cons.table_name = ' || p_table_name ||
'AND cons.constraint_name = cols.constraint_name
AND cons.owner = cols.owner';
EXECUTE IMMEDIATE v_pk_columns_name;
END;
它给了我这些错误:
ORA-00933: SQL command not properly ended
ORA-06512: at line 23
00933. 00000 - "SQL command not properly ended"
如何解决这些错误?我的语法有什么问题?感谢您的帮助。
【问题讨论】:
';'不见了。 第 18 行是p_table_name : = 'ODS_SALES'
。第 14 列是 ':` 和 =
之间的空格。删除它。您似乎是一次编写了整个过程而不是构建它(即编写和测试代码以做一件事然后扩展它),因为当您更正第一个语法错误时,您会遇到许多额外的语法错误.然后,如果不是所有动态组装的 SQL 语句,大多数情况下都会出现问题。在执行语句之前打印/记录语句,以便调试调用 execute immediate
时遇到的错误。
如果您将EXECUTE IMMEDIATE
与返回值的语句一起使用,请使用EXECUTE IMMEDIATE <your statement> INTO <a variable>
它似乎比它需要的复杂得多,因为它使用动态 SQL。把它变成普通的静态 SQL(除了计算表中的行数),它会更容易使用。
一周前的问题***.com/questions/68884984/… 似乎向您展示了如何做到这一点
【参考方案1】:
工作版本:
declare
p_owner_name all_tables.owner%type := 'ODS03';
p_table_name all_tables.table_name%type := 'ODS_SALES';
v_size_mb number(8,1);
v_pk_columns varchar2(1000);
v_index_cnt number;
v_row_cnt_sql varchar2(1000) := 'select count(*) from ' || p_owner_name||'.'||p_table_name;
v_row_cnt integer;
v_column_cnt number;
begin
select sum(bytes)/power(1024,2) into v_size_mb
from dba_segments
where owner = p_owner_name and segment_name = p_table_name;
execute immediate v_row_cnt_sql into v_row_cnt;
select count(*) into v_column_cnt
from all_tab_columns
where table_name = p_table_name and owner = p_owner_name;
select count(*) into v_index_cnt
from all_indexes i
where i.owner = p_owner_name
and i.table_name = p_table_name;
select listagg(cols.column_name, ', ') within group (order by cols.position)
into v_pk_columns
from all_constraints cons
join all_cons_columns cols
on cols.owner = cons.owner
and cols.table_name = cons.table_name
and cols.constraint_name = cons.constraint_name
where cons.owner = cols.owner
and cons.table_name = p_table_name
and cons.constraint_type = 'P';
dbms_output.put_line('Table: '||p_owner_name||'.'||p_table_name||':');
dbms_output.put_line('Size (MB): '||round(v_size_mb,1));
dbms_output.put_line('Rows: '||v_row_cnt);
dbms_output.put_line('Columns: '||v_column_cnt);
dbms_output.put_line('Indexes: '||v_index_cnt);
dbms_output.put_line('PK columns: '||v_pk_columns);
end;
唯一需要动态的部分是计算行数。
在许多系统中,表会非常大,以至于计算行数可能需要很多小时,因此您可能需要考虑对此的要求。可以接受近似计数吗?统计信息 (all_tables.num_rows) 是否足够接近?如果确实需要精确计数,可以考虑并行查询(需要企业版)。
【讨论】:
【参考方案2】:您的方法不是最优的。您不能使用静态 SQL 从参数化表中进行选择,但可以使用静态 SQL 从数据库元数据中选择任何表。
一种可能的方法是定义一个选择字典数据的视图(这不是必需的,但会使您的过程更简单)。
create or replace view my_tab_meta as
with pk as (
SELECT COLS.OWNER,CONS.TABLE_NAME, listagg(cols.column_name,'.') within group (order by cols.POSITION) as pk_cols
FROM ALL_CONSTRAINTS CONS, ALL_CONS_COLUMNS COLS
WHERE CONS.CONSTRAINT_TYPE = 'P'
AND CONS.CONSTRAINT_NAME = COLS.CONSTRAINT_NAME
AND CONS.OWNER = COLS.OWNER
group by COLS.OWNER,CONS.TABLE_NAME),
idx as (
select distinct TABLE_OWNER, TABLE_NAME from all_indexes)
select tab.OWNER, tab.TABLE_NAME,
tab.NUM_ROWS,
tab.BLOCKS * (select VALUE from v$parameter where name = 'db_block_size') /1024/1024 db_size,
case when idx.TABLE_OWNER is not null then 1 else 0 end as idx_flag,
pk.pk_cols
from all_tables tab
left outer join idx
on tab.owner = idx.TABLE_OWNER and tab.TABLE_NAME = idx.TABLE_NAME
left outer join pk
on tab.owner = pk.OWNER and tab.TABLE_NAME = pk.TABLE_NAME;
一些讨论
您应该决定使用 DBA
视图或 ALL
视图 - 而不是组合它们。
您需要访问字典视图的权限才能创建视图
我也从字典中获取大小和行数。这更有效,但需要最新的对象统计信息。
下面是 PL/SQL 块的框架
declare
p_table_name varchar2(128) := 'TAB1';
p_owner_name varchar2(128) := 'REPORTER';
v_PK_COLUMNS_NAME varchar2(4000);
v_IND_EXISTS_FLG varchar2(1);
v_DATA_VOLUME number;
v_ROW_CNT number;
v_COLUMN_CNT number;
begin
select
NUM_ROWS, DB_SIZE, IDX_FLAG, PK_COLS
into v_ROW_CNT, v_DATA_VOLUME, v_IND_EXISTS_FLG, v_PK_COLUMNS_NAME
from MY_TAB_META
where owner = p_owner_name and table_name = p_table_name;
end;
/
还要注意正确数据类型的使用,例如行数为number
。
【讨论】:
以上是关于如何在 Oracle 中动态分析给定模式名和表名的元数据?的主要内容,如果未能解决你的问题,请参考以下文章
需要用表名和表的模式列出 SQL Server 数据库中的所有触发器