0

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

/* 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 DAYS ACTION=APPLY VERSION=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

4

2 回答 2

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, COMMENTSvs. PATCH_ID, DESCRIPTION),这会通过 a 从表或视图中获取单独的 XML union all,但不会从两者中获取,因为这会产生无效的 XML 文档。在 12c 中,它看起来像是REGISTRY$HISTORY空的,但如果不是,它不会从中获取任何数据(如果DBA_REGISTRY_SQLPATCH存在)。(我有点懒惰并且不检查所有权,所以其他人创建一个具有该名称的表将是一个问题,但很容易修复)。它为列名设置别名,因此无论最终使用哪个表/视图,它们看起来都相同,从而允许对 XML 进行解包。

将其与您的字符串格式、消除子查询并使用分析last函数仅保留最近的行结合起来,您最终会得到如下结果:

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 实例来验证它的行为是否符合您的预期。

于 2016-05-10T15:14:04.950 回答
0

编辑:正如亚历克斯普尔在评论中显示的那样(对他的回答而不是对我的回答),我在下面描述的内容将不起作用。这实际上很好地说明了在这种情况下什么是行不通的。

我把它留在这里只是为了让那些可能已经看到它的人已经有机会看到它不好。我会在一段时间后删除答案。

谢谢亚历克斯指出!

-

显然,您可以编写自己的查询,所以我将在这里只展示一种方法来执行您询问的“切换”表达式。我只有第 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$version 在 Oracle 12c 中没有变化(即:仍然有一个视图 v$version,该列仍然称为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 ...

您不需要为“注册表”表中的每一位数据编写 Oracle 版本的 case 表达式 - 您可以在 case 表达式的两个分支中构建完整的字符串。您也可以调整它以适应“未安装补丁”分支。

祝你好运!

于 2016-05-10T15:34:17.890 回答