【问题标题】:Retrieving and displaying data from oracle database从 oracle 数据库中检索和显示数据
【发布时间】:2020-08-26 13:48:36
【问题描述】:

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

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

第一个表包含有关另一个表中存在的列的元数据。将第一个 (COL_TAB) 表视为 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 为空白或 null,则需要在输出中显示 COL_NAME。 -> "TABLE 1 COLUMN 3" 和 "TABLE 1 COLUMN 1" 总是需要显示为第 1 列和第 2 列,然后是其余列。 -> 如果 COL_TAB 表中定义的任何列未在 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'));

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

【问题讨论】:

  • 您将需要动态 SQL 来用于第一个输出的数据透视(因为您不能在数据透视子句中进行选择,除非您想要 XML 输出),以及获取列排序在第二个输出中。但是为什么你想要单独的输出,应该如何格式化和返回这些输出 - 你用 PL/SQL 标记所以你在考虑 dbms_output 或单独的查询 - 在这种情况下需要枢轴吗?您是否真的想要一个包含自定义列标题的表格数据的结果集?
  • 还有列顺序是怎么确定的?您提到了前两列,最后提到了不匹配的列;但是这些应该以什么顺序出现?所有匹配的列都应该按column_id 的顺序排列,还是只是前两个(这看起来不是很通用)?如果有多个不匹配的列,这些列是如何排序的?这个数据模型看起来很奇怪,但即便如此似乎应该有一个等价的column_idtab_cols 中的某个位置标记。
  • 是的,我同意需要动态 SQL。数据将在 3rd 方应用程序中使用,这需要列标题作为一个数据集,表数据作为另一个数据集,这就是我将其标记为 PL/SQL 的原因。
  • 就列顺序而言,考虑到上面的示例数据,“TABLE 1 COLUMN 3”和“TABLE 1 COLUMN 1”总是需要显示为第1列和第2列,然后是匹配的列,然后是不匹配的列。
  • 但是以什么顺序匹配(和不匹配)列?还是没关系,只要两个输出一致?为什么这两个不同 - 其他表的结构与table1 不同?

标签: oracle plsql oracle11g


【解决方案1】:

您可以按确定的顺序获取列描述/名称,例如:

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)绑定变量引用游标来获取两个输出,并使用块内定义的表名;但可以很容易地适应成为传递表名并为引用游标提供参数的过程:

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

然后您可以打印引用游标(同样,客户端特定的行为):

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           

这 2 列在所有表中都是通用的。

在这种情况下,您可以使用 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           

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


如果您能展示如何动态完成旋转,那就太好了。

这里最后真的不需要,而且比我上面用的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,我认为这只会让阅读变得更加困难,因为你需要在它们周围加上引号。

【讨论】:

  • 非常感谢 Alex 的快速解决方案,能否请您告诉我,我该如何进行动态旋转?
  • 嗯,是的,我最终不需要那个;当 listagg 做同样的工作时,你真的需要它吗?
  • 如果您能展示如何动态完成旋转,那就太好了。因为我对此很陌生。再次感谢您的宝贵时间。
  • 好吧,加了个例子,不过我觉得这里用处不大。
  • 非常感谢,亚历克斯!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-29
  • 2018-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多