是否可以使用模式匹配从Oracle SQL数据库中选择列?

问题描述

我在Oracle sql DB中有一个表,该表的列标题带有“ PERCENT_”之类的名称以及其他名称。我想选择所有带有“ PERCENT_”前缀的列。

我已经找到了一些问题,这些问题可以回答诸如如何在数据库中查找所有具有与某些模式匹配的列的表的问题,但似乎没有一个可以回答这个特定问题。

可以做到吗?我能够返回与“ all_tab_columns”匹配的所有列名称的列表

select column_name from all_tab_columns where column_name like 'PERCENT_%' from <table_name>

但是我可以做这样的事情从我想要的表中选择那些列吗?

with desired_cols as (select column_name from all_tab_columns where column_name like 'PERCENT_%' from <table_name>)
select desired_cols from <table_name>

解决方法

SQL查询始终返回一组固定的预定义列。您要求的是动态SQL:即运行一个查询,该查询收集列名,将结果保存在变量中,然后使用它来构建要执行的实际查询。

如果这是一次性查询,那么我建议只运行以下查询:

select 'select ' 
    || listagg('"' || column_name || '"',',') within group (order by column_id)
    || ' from mytable' as sql_query
from all_tab_columns 
where owner = 'MYSCHEMA' and table_name = 'MYTABLE' and column_name like 'PERCENT_%';

这将生成所需的查询,然后可以复制/粘贴并独立运行。

请注意,我在模式名称上添加了一个过滤器(这是唯一标识数据库中的表所必需的)。我还按列在表中的位置对其进行了排序。

另一方面,如果您想要完全动态的内容,则可以例如构建一个函数,该函数将返回包含查询结果的游标。

create function myfunction return sys_refcursor
as
    v_sql varchar(1000);
    v_cursor sys_refcursor;
begin
    select 'select ' 
        || listagg('"' || column_name || '"',') within group (order by column_id)
        || ' from mytable' as sql_query
    into v_sql
    from all_tab_columns 
    where table_name = 'MYTABLE' and column_name like 'PERCENT_%';
        
    open v_cursor for v_sql;
    return v_cursor;
end;
/

然后,您必须决定如何使用光标。

,

在18c及更高版本中,您可以创建一个多态表函数来根据模式动态选择列。在12c及更低版本中,您需要使用Oracle数据盒带在SQL中创建动态SQL。两种解决方案都需要PL / SQL,但是最终结果的外观和行为与常规SQL相似。实际上,您可能希望在应用程序层而不是数据库中解决这类动态问题。例如,如果您要构建Excel电子表格,则大多数Oracle-Excel API都允许将查询作为字符串传递。

18c及更高版本-多态表功能

首先,创建一个包,该包标识表中的哪些列要通过SQL语句,哪些要忽略。多态表函数有很多不寻常的语法,因此下面的代码乍看之下没有多大意义。

create or replace package percent_cols as
    function percent_cols(tab in table) return table pipelined row polymorphic using percent_cols;
    function describe(tab in out dbms_tf.table_t) return dbms_tf.describe_t;
end percent_cols;
/

create or replace package body percent_cols as
    function describe (tab in out dbms_tf.table_t) return dbms_tf.describe_t
    as
    begin
        for i in 1 .. tab.column.count() loop
            if tab.column(i).description.name like '%PERCENT\_%' escape '\' then
                tab.column(i).pass_through := true;
            else
                tab.column(i).pass_through := false;
            end if;
        end loop;

        return null;
    end;
end percent_cols;
/

创建一些示例表来测试软件包:

create table test1(a number,percent_1 number);
insert into test1 values(1,1);

create table test2(percent_1 number,percent_2 number);
insert into test2 values(2,2);

create table test3(a number);
insert into test3 values(3);

以下示例查询仅显示PERCENT%列。您可能需要为没有匹配列的表添加一些异常处理。

SQL> select * from percent_cols(test1);

 PERCENT_1
----------
         1

SQL> select * from percent_cols(test2);

 PERCENT_1  PERCENT_2
---------- ----------
         2          2

SQL> select * from percent_cols(test3);
select * from percent_cols(test3)
              *
ERROR at line 1:
ORA-30732: table contains no user-visible columns

12c及以下-Oracle数据盒带

数据盒带使我们可以通过许多有趣的方式扩展Oracle,但是该接口很难使用。与其创建自己的PL / SQL类型和函数,不如从my open source project开始。

此解决方案比多态表函数需要更多的代码,但是它具有在常规SQL中保留更多逻辑的优点-您可以在表函数中看到单个数据字典查询,而不是将其隐藏在PL / SQL之后

select * from table(method4.dynamic_query(
    q'[
        select 'select '||listagg(column_name,') within group (order by column_id)||' from '||table_name v_sql
        from all_tab_columns
        where table_name = 'TEST2'
            and column_name like 'PERCENT_%'
        group by table_name
    ]'
));

 PERCENT_1  PERCENT_2
---------- ----------
         2          2