切换oracle数据库版本的case表达式

时间:2016-05-10 14:50:43

标签: oracle patch case-expression

我需要在Oracle数据库上查询补丁状态。自Oracle 12c版以来,视图sys.REGISTRY $ HISTORY被视图DBA_REGISTRY_SQLPATCH替换。在像11g这样的旧版本上,视图dba_registry_sqlpatch不存在。以下查询在oracle版本上创建错误< 12c因为视图dba_registry_sqlpatch不存在。我需要构建一个在所有oracle数据库版本上运行的查询。我不能使用PL / SQL。我认为它应该通过案例表达来解决。

/* Query for version < 11g: */
SELECT MIN (diff) diff, MIN (zeile) zeile
  FROM (SELECT TO_CHAR (TRUNC (SYSDATE - TRUNC (action_time)), '9999') DIFF,
                  'DIFF : '
               || TO_CHAR (TRUNC (SYSDATE - TRUNC (action_time)), '9999')
               || ' DAYS '
               || 'ACTION='
               || action
               || ' VERSION='
               || version
               || ' DATE='
               || TO_CHAR (action_time, 'yyyymmdd')
               || ' ID='
               || TO_CHAR (id, '09')
               || ' COMMENTS='
               || comments
               || ' PORT='
               || (SELECT DBMS_UTILITY.port_string
                     FROM DUAL)
                  ZEILE
          FROM sys.REGISTRY$HISTORY
         WHERE action_time = (SELECT MAX (action_time)
                                FROM sys.REGISTRY$HISTORY
                               WHERE action IN ('APPLY', 'ROLLBACK'))
        UNION ALL
        /*Query for version 12c: */
        (SELECT TO_CHAR (TRUNC (SYSDATE - TRUNC (action_time)), '9999') DIFF,
                   'DIFF : '
                || TO_CHAR (TRUNC (SYSDATE - TRUNC (action_time)), '9999')
                || ' DAYS '
                || 'ACTION='
                || action
                || ' VERSION='
                || version
                || ' DATE='
                || TO_CHAR (action_time, 'yyyymmdd')
                || ' ID='
                || TO_CHAR (patch_id)
                || ' COMMENTS='
                || description
                || ' PORT='
                || (SELECT DBMS_UTILITY.port_string
                      FROM DUAL)
                   ZEILE
           FROM dba_registry_sqlpatch
          WHERE action_time = (SELECT MAX (action_time)
                                 FROM dba_registry_sqlpatch
                                WHERE action IN ('APPLY', 'ROLLBACK')))
        UNION ALL
        /* Query for no patch installed: */
        SELECT (SELECT TO_CHAR (TRUNC (SYSDATE - TRUNC (created)), '9999')
                  FROM v$database)
                  DIFF,
                  'DIFF : '
               || (SELECT TO_CHAR (TRUNC (SYSDATE - TRUNC (created)), '9999')
                     FROM v$database)
               || ' DAYS ACTION=N./A. VERSION='
               || (SELECT SUBSTR (version, 1, 8)
                     FROM v$instance)
               || ' DATE='
               || (SELECT TO_CHAR (created, 'yyyymmdd')
                     FROM v$database)
               || ' ID= 99 COMMENTS='
               || (SELECT SUBSTR (version, 1, 8)
                     FROM v$instance)
               || ' PORT='
               || (SELECT DBMS_UTILITY.port_string
                     FROM DUAL)
                  ZEILE
          FROM DUAL)
 WHERE ROWNUM = 1;

11天前修补的Oracle 12c数据库上的示例输出: DIFF:11天操作=应用版本= 12.1.0.2 DATE = 20160429 ID = 22809813 COMMENTS = WINDOWS DB BUNDLE PATCH 12.1.0.2.160419(64bit):22809813 PORT = IBMPC / WIN_NT64-9.1.0

2 个答案:

答案 0 :(得分:2)

案例表达不会解决您的问题。要查询的表必须在分析时知道 - 在执行查询时,您无法动态选择表名,并且案例在评估案例之前仍会得到ORA-00942。

假设您只想要旧表和新视图中存在的列,您可以使用一些XML转换来从任何存在的数据中获取数据:

select x.*
from (
  select dbms_xmlgen.getxml(q'[select to_char(action_time, 'YYYY-MM-DD"T"HH24:MI:SS.FF9')
      as action_time, action, version, id as patch_id, comments as description
    from sys.REGISTRY$HISTORY]') as data
  from dba_tables
  where table_name = 'REGISTRY$HISTORY'
  and not exists (select null from dba_views where view_name = 'DBA_REGISTRY_SQLPATCH')
  union all
  select dbms_xmlgen.getxml(q'[select to_char(action_time, 'YYYY-MM-DD"T"HH24:MI:SS.FF9')
      as action_time, action, version, patch_id, description
    from DBA_REGISTRY_SQLPATCH]') as data
  from dba_views
  where view_name = 'DBA_REGISTRY_SQLPATCH'
) t
cross join xmltable('/ROWSET/ROW' passing xmltype(t.data)
  columns action_time timestamp path 'ACTION_TIME',
    action varchar2(30) path 'ACTION',
    version varchar2(30) path 'VERSION',
    patch_id number path 'PATCH_ID',
    comments varchar2(100) path 'DESCRIPTION'
) x;

然后将select x.*替换为您想要处理数据的任何内容,基本上将其插入现有查询中,添加联合以获取未修补的版本信息:

...
union all
select vd.created as action_time, 'N/A' as action, substr(vi.version, 1, 8) as version,
  99 as patch_id, substr(vi.version, 1, 8) as description
from v$database vd
cross join v$instance vi;

to_char()是将时间戳值转换为XML中预期的ISO格式。 dbms_xmlgen()调用将数据从表/视图转换为XML表示形式;并XMLTable()将其转换回来。这似乎有点无意义,但它让你不知道对象名称,直到运行时。

由于列略有不同(ID, COMMENTSPATCH_ID, DESCRIPTION),因此会通过union all从表或视图中获取单独的XML,但不能同时从两者中获取XML无效的XML文档。在12c中,REGISTRY$HISTORY似乎是空的,但如果DBA_REGISTRY_SQLPATCH存在,则不会从中获取任何数据。 (我有点懒,不检查所有权,所以其他人创建一个具有该名称的表将是一个问题,但很容易修复)。它对列名称进行别名,使它们看起来与最终使用的表/视图相同,从而允许解压缩XML。

将它与字符串格式放在一起,消除子查询,并使用the last analytic function仅保留最新的行,最终可能会出现以下情况:

select to_char (trunc (sysdate - trunc (max(action_time))), '9999') diff,
  'DIFF : ' || to_char (trunc (sysdate - trunc (max(action_time))), '9999') || ' DAYS'
    || ' ACTION=' || max(action) keep (dense_rank last order by action_time)
    || ' VERSION=' || max(version) keep (dense_rank last order by action_time)
    || ' DATE=' || to_char (max(action_time), 'yyyymmdd')
    || ' ID=' || to_char (max(patch_id) keep (dense_rank last order by action_time), '09')
    || ' COMMENTS=' || max(comments) keep (dense_rank last order by action_time)
    || ' PORT=' || dbms_utility.port_string zeile
from (
  select x.* from (
    select dbms_xmlgen.getxml(q'[select to_char(action_time, 'YYYY-MM-DD"T"HH24:MI:SS.FF9')
        as action_time, action, version, id as patch_id, comments as description
      from sys.REGISTRY$HISTORY]') as data
    from dba_tables
    where table_name = 'REGISTRY$HISTORY'
    and not exists (select null from dba_views where view_name = 'DBA_REGISTRY_SQLPATCH')
    union all
    select dbms_xmlgen.getxml(q'[select to_char(action_time, 'YYYY-MM-DD"T"HH24:MI:SS.FF9')
        as action_time, action, version, patch_id, description
      from DBA_REGISTRY_SQLPATCH]') as data
    from dba_views
    where view_name = 'DBA_REGISTRY_SQLPATCH'
  ) t
  cross join xmltable('/ROWSET/ROW' passing xmltype(t.data)
    columns action_time timestamp path 'ACTION_TIME',
      action varchar2(30) path 'ACTION',
      version varchar2(30) path 'VERSION',
      patch_id number path 'PATCH_ID',
      comments varchar2(100) path 'DESCRIPTION'
  ) x
  union all
  select vd.created as action_time, 'N./.A' as action, substr(vi.version, 1, 8) as version,
    99 as patch_id, substr(vi.version, 1, 8) as comments
  from v$database vd
  cross join v$instance vi
);

在11.2.0.4和10.2.0.5上测试过,但我没有一个未修补的实例或一个12c实例来验证它的行为是否符合您的预期。

答案 1 :(得分:0)

编辑:正如Alex Poole在评论中所显示的那样(对他的回答而不是我的回答),我在下面描述的内容将无效。这实际上很好地说明了在这种情况下不起作用的内容。

我将它留在这里,所以可能已经看过这个的人已经有机会看到它并不好。我会在一段时间后删除答案。

谢谢Alex指出来!

-

显然你可以自己编写查询,所以我只会在这里展示一种方法来做&#34;切换&#34;你问过的表达方式。我只有版本11(免费版)所以我无法完全测试,但这应该有效。要查找会话所在的Oracle DB版本,可以查询视图V $ VERSION。在我的机器上,我看到Oracle版本显示为:

SQL> select * from v$version where banner like 'Oracle%';

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production

假设v $版本在Oracle 12c中没有变化(即:仍有视图v $版本,该列仍称为banner,Oracle DB版本显示为Oracle Database 12c ... 。),为了获得action_time,你可以做这样的事情:

select case 
    regexp_substr((select banner from v$version where banner like 'Oracle%'), '\d{1,2}') 
           when '11' then (select action_time from sys.REGISTRY$HISTORY)
           when '12' then (select action_time from dba_registry_sqlpatch)
           end   as action_time ...

您不需要为来自&#34;注册表&#34;的每一位数据编写Oracle版本的案例表达式。 table - 您可以在case表达式的两个分支中构建完整的字符串。你可以调整它以适应&#34;没有安装补丁&#34;分支也是如此。

祝你好运!