从 oracle 数据库中检索和显示数据

Retrieving and displaying data from oracle database

首先,我想感谢您 光临并花费您宝贵的时间来查看我的问题。

我在 Oracle 数据库中有 2 个不同的 table。

第一个 table 包含关于另一个 table 中存在的列的元数据。将第一个 (COL_TAB) table 视为 Oracle 默认附带的 ALL_TAB_COLS 的自定义版本。

COL_TAB
----------------------------------------------
| TABLE_NAME | COL_NAME   | COL_DESC         | 
----------------------------------------------
| TABLE1     | TAB1_COL_2 | TABLE 1 COLUMN 2 |
| TABLE1     | TAB1_COL_4 | TABLE 1 COLUMN 4 |
| TABLE1     | TAB1_COL_3 | TABLE 1 COLUMN 3 |
| TABLE1     | TAB1_COL_5 |                  |
| TABLE1     | TAB1_COL_1 | TABLE 1 COLUMN 1 |
----------------------------------------------

TABLE1
--------------------------------------------------------------------
| TAB1_COL_3      | TAB1_COL_1    | TAB1_COL_5     | TAB1_COL_2    |
--------------------------------------------------------------------
| TAB1_COL3_DATA1 | TAB1_COL1_DAT | TAB1_COL5_DAT2 | TAB1_COL2_DAT |
| TAB1_COL3_DATA2 | TAB1_COL1_DAT | TAB1_COL5_DAT1 | TAB1_COL2_DAT |
| TAB1_COL3_DATA3 | TAB1_COL1_DAT | TAB1_COL5_DAT3 | TAB1_COL2_DAT |
--------------------------------------------------------------------

我想将数据显示为 2 个不同的输出:

FIRST OUTPUT:
------------------------------------------------------------------------------------------------
| TABLE 1 COLUMN 3 | TABLE 1 COLUMN 1 | TAB1_COL_5       | TABLE 1 COLUMN 2 | TABLE 1 COLUMN 4 |
------------------------------------------------------------------------------------------------

-> 如果 COL_DESC 为空或空,则需要在输出中显示 COL_NAME。 -> "TABLE 1 COLUMN 3" AND "TABLE 1 COLUMN 1" 始终需要显示为第一列和第二列,然后是其余列。 -> 如果 COL_TAB table 中定义的任何列未在 TABLE1 中使用,则此类列需要显示在输出的最后一列, 例如,TAB1_COL_4 未在 TABLE1 中使用,因此显示在最后。

SECOND OUTPUT:
------------------------------------------------------------------------------------------------
| TAB1_COL3_DATA1  | TAB1_COL1_DAT    | TAB1_COL5_DAT2   | TAB1_COL2_DAT    |                  |
| TAB1_COL3_DATA2  | TAB1_COL1_DAT    | TAB1_COL5_DAT1   | TAB1_COL2_DAT    |                  |
| TAB1_COL3_DATA3  | TAB1_COL1_DAT    | TAB1_COL5_DAT3   | TAB1_COL2_DAT    |                  |
------------------------------------------------------------------------------------------------

-> 第二个输出中列的顺序需要与第一个输出中显示的列顺序同步。

我确实尝试了以下用于显示第一个输出的查询,但它不起作用(我确定它不正确):

SELECT NVL(COL_DESC, COL_NAME) AS COL_TEXT
FROM COL_TAB
WHERE TABLE_NAME = 'TABLE1'
PIVOT(MIN(COL_TEXT)
FOR COL_TEXT IN (SELECT COL_NAME FROM COL_TAB WHERE TABLE_NAME = 'TABLE1'));

以防万一,如果有任何不清楚的地方,请告诉我。我会尽力再解释一遍。再次感谢您的提前帮助。

您可以获得列 description/names - 以确定的顺序 - 使用类似的东西:

select coalesce(ct.col_desc, ct.col_name)
from col_tab ct
left join user_tab_columns utc
on utc.table_name = ct.table_name and utc.column_name = ct.col_name
where ct.table_name = 'TABLE1'
order by utc.column_id, ct.col_name;
COALESCE(CT.COL_
----------------
TABLE 1 COLUMN 3
TABLE 1 COLUMN 1
TAB1_COL_5
TABLE 1 COLUMN 2
TABLE 1 COLUMN 4

将这些行转为列需要动态完成。

您还可以生成一个动态查询,以类似的方式以相同的顺序获取数据。

这使用 SQL*Plus(或 SQLcl,或 SQL Developer)绑定变量引用游标来获取两个输出,并使用 table 名称在块内定义;但可以很容易地改编为传递 table 名称并具有 ref 游标参数的过程:

var rc1 refcursor;
var rc2 refcursor;

declare
  l_table_name varchar2(30) := 'TABLE1';
  l_stmt varchar2(4000);
begin
  select 'select '
    || listagg('''' || coalesce(ct.col_desc, ct.col_name) || '''',  ',')
         within group (order by utc.column_id, ct.col_name)
    || ' from dual'
  into l_stmt
  from col_tab ct
  left join user_tab_columns utc
  on utc.table_name = ct.table_name and utc.column_name = ct.col_name
  where ct.table_name = l_table_name;

  dbms_output.put_line(l_stmt);

  open :rc1 for l_stmt;

  select 'select '
    || listagg(coalesce(utc.column_name, 'null') || ' as ' || ct.col_name,  ',')
         within group (order by utc.column_id, ct.col_name)
    || ' from ' || l_table_name
  into l_stmt
  from col_tab ct
  left join user_tab_columns utc
  on utc.table_name = ct.table_name and utc.column_name = ct.col_name
  where ct.table_name = l_table_name;

  dbms_output.put_line(l_stmt);

  open :rc2 for l_stmt;

end;
/

运行 该块获得 dbms_output 的语句只是为了调试,但可能有兴趣:

select 'TABLE 1 COLUMN 3','TABLE 1 COLUMN 1','TAB1_COL_5','TABLE 1 COLUMN 2','TABLE 1 COLUMN 4' from dual
select TAB1_COL_3 as TAB1_COL_3,TAB1_COL_1 as TAB1_COL_1,TAB1_COL_5 as TAB1_COL_5,TAB1_COL_2 as TAB1_COL_2,null as TAB1_COL_4 from TABLE1

然后您可以打印参考游标(同样,client-specific 行为):

print rc1

'TABLE1COLUMN3'  'TABLE1COLUMN1'  'TAB1_COL_ 'TABLE1COLUMN2'  'TABLE1COLUMN4' 
---------------- ---------------- ---------- ---------------- ----------------
TABLE 1 COLUMN 3 TABLE 1 COLUMN 1 TAB1_COL_5 TABLE 1 COLUMN 2 TABLE 1 COLUMN 4

print rc2

TAB1_COL_3      TAB1_COL_1    TAB1_COL_5     TAB1_COL_2    TAB1_COL_4
--------------- ------------- -------------- ------------- ----------
TAB1_COL3_DATA1 TAB1_COL1_DAT TAB1_COL5_DAT2 TAB1_COL2_DAT           
TAB1_COL3_DATA2 TAB1_COL1_DAT TAB1_COL5_DAT1 TAB1_COL2_DAT           
TAB1_COL3_DATA3 TAB1_COL1_DAT TAB1_COL5_DAT3 TAB1_COL2_DAT           

Those 2 columns are common across all the tables.

在这种情况下,您可以使用 case 表达式来扩展排序逻辑:

         within group (order by case ct.col_name 
                                  when 'TAB1_COL_3' then 1
                                  when 'TAB1_COL_1' then 2
                                  else 3 end,
                                utc.column_id, ct.col_name)

然后得到:

'TABLE1COLUMN3'  'TABLE1COLUMN1'  'TAB1_COL_ 'TABLE1COLUMN2'  'TABLE1COLUMN4' 
---------------- ---------------- ---------- ---------------- ----------------
TABLE 1 COLUMN 3 TABLE 1 COLUMN 1 TAB1_COL_5 TABLE 1 COLUMN 2 TABLE 1 COLUMN 4


TAB1_COL_3      TAB1_COL_1    TAB1_COL_5     TAB1_COL_2    TAB1_COL_4
--------------- ------------- -------------- ------------- ----------
TAB1_COL3_DATA1 TAB1_COL1_DAT TAB1_COL5_DAT2 TAB1_COL2_DAT           
TAB1_COL3_DATA2 TAB1_COL1_DAT TAB1_COL5_DAT1 TAB1_COL2_DAT           
TAB1_COL3_DATA3 TAB1_COL1_DAT TAB1_COL5_DAT3 TAB1_COL2_DAT           

或者可能使用描述而不是名称,这取决于保持不变的是名称还是描述(很难从示例中猜出)。


It would be really great if you could show up how pivoting can be done dynamically.

这里其实用不着,比我上面用的listagg复杂多了;但你可以做类似的事情;

  select '
select * from (
  select row_number()
           over (order by case ct.col_name 
                            when ''TAB1_COL_3'' then 1
                            when ''TAB1_COL_1'' then 2
                            else 3
                          end,
                          utc.column_id, ct.col_name) as pos,
         coalesce(ct.col_desc, ct.col_name) as name
  from col_tab ct
  left join user_tab_columns utc
  on utc.table_name = ct.table_name and utc.column_name = ct.col_name
  where ct.table_name = :tab
)
pivot (max(name) as col for (pos) in ('
|| listagg(level, ',') within group (order by level)
|| '))'
  into l_stmt
  from dual
  connect by level <= (select count(*) from col_tab where table_name = l_table_name);

  dbms_output.put_line(l_stmt);

  open :rc1 for l_stmt using l_table_name;

它得到的输出显示生成的动态查询为:

select * from (
  select row_number()
           over (order by case ct.col_name 
                            when 'TAB1_COL_3' then 1
                            when 'TAB1_COL_1' then 2
                            else 3
                          end,
                          utc.column_id, ct.col_name) as pos,
         coalesce(ct.col_desc, ct.col_name) as name
  from col_tab ct
  left join user_tab_columns utc
  on utc.table_name = ct.table_name and utc.column_name = ct.col_name
  where ct.table_name = :tab
)
pivot (max(name) as col for (pos) in (1,2,3,4,5))

结果集为:

1_COL            2_COL            3_COL            4_COL            5_COL           
---------------- ---------------- ---------------- ---------------- ----------------
TABLE 1 COLUMN 3 TABLE 1 COLUMN 1 TAB1_COL_5       TABLE 1 COLUMN 2 TABLE 1 COLUMN 4

您可以使用列名代替 pos 作为数据透视表,我认为这只会使它更难阅读,因为您需要在它们两边加上引号。