oracle 数据库版本的 switch case 表达式
Switch case expression for oracle database versions
我需要查询 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 数据库的示例输出:
差异:11 天操作=应用版本=12.1.0.2 日期=20160429 ID=22809813 评论=WINDOWS 数据库捆绑补丁 12.1.0.2.160419(64 位):22809813 端口=IBMPC/WIN_NT64-9.1.0
case 表达式无法解决您的问题。被查询的 tables 必须在解析时知道 - 你不能在执行查询时动态选择 table 名称,并且案例在案例被执行之前仍然会得到 ORA-00942评价。
假设您只需要旧视图 table 和新视图中都存在的列,您可以使用一些 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()
调用将数据从 table/view 转换为 XML 表示; XMLTable()
将其转换回来。这看起来有点无意义,但它让你直到运行时才知道对象名称。
由于列略有不同(ID, COMMENTS
与 PATCH_ID, DESCRIPTION
),这与 table 或通过 [=19= 的视图分开 XML ],但不能同时来自两者,因为那样会给出无效的 XML 文档。在 12c 中,REGISTRY$HISTORY
看起来是空的,但万一它不是空的,如果 DBA_REGISTRY_SQLPATCH
存在,它不会从中获取任何数据。 (我有点懒惰,没有检查所有权,所以其他人使用该名称创建 table 将是一个问题,但很容易解决)。它为列名称设置了别名,因此它们看起来与它最终使用的 table/view 相同,从而允许 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 实例来验证它的行为是否符合您的预期。
编辑:正如 Alex Poole 在评论(针对他的回答而非我的回答)中显示的那样,我在下面描述的内容将不起作用。它实际上很好地说明了在这种情况下什么是行不通的。
我把它留在这里只是为了让可能已经看到它的人有机会看到它不好。过段时间我会删除答案
谢谢 Alex 指出!
-
很明显,您可以编写自己的查询,因此我将在此处仅展示一种执行您询问的 "switch" 表达式的方法。我只有第 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 ...
您不需要为来自 "registry" table 的每一位数据编写 Oracle 版本的 case 表达式 - 您可以在 case 的两个分支中构建完整的字符串表达。您也可以调整它以适应 "no patch installed" 分支。
祝你好运!
我需要查询 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 数据库的示例输出: 差异:11 天操作=应用版本=12.1.0.2 日期=20160429 ID=22809813 评论=WINDOWS 数据库捆绑补丁 12.1.0.2.160419(64 位):22809813 端口=IBMPC/WIN_NT64-9.1.0
case 表达式无法解决您的问题。被查询的 tables 必须在解析时知道 - 你不能在执行查询时动态选择 table 名称,并且案例在案例被执行之前仍然会得到 ORA-00942评价。
假设您只需要旧视图 table 和新视图中都存在的列,您可以使用一些 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()
调用将数据从 table/view 转换为 XML 表示; XMLTable()
将其转换回来。这看起来有点无意义,但它让你直到运行时才知道对象名称。
由于列略有不同(ID, COMMENTS
与 PATCH_ID, DESCRIPTION
),这与 table 或通过 [=19= 的视图分开 XML ],但不能同时来自两者,因为那样会给出无效的 XML 文档。在 12c 中,REGISTRY$HISTORY
看起来是空的,但万一它不是空的,如果 DBA_REGISTRY_SQLPATCH
存在,它不会从中获取任何数据。 (我有点懒惰,没有检查所有权,所以其他人使用该名称创建 table 将是一个问题,但很容易解决)。它为列名称设置了别名,因此它们看起来与它最终使用的 table/view 相同,从而允许 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 实例来验证它的行为是否符合您的预期。
编辑:正如 Alex Poole 在评论(针对他的回答而非我的回答)中显示的那样,我在下面描述的内容将不起作用。它实际上很好地说明了在这种情况下什么是行不通的。
我把它留在这里只是为了让可能已经看到它的人有机会看到它不好。过段时间我会删除答案
谢谢 Alex 指出!
-
很明显,您可以编写自己的查询,因此我将在此处仅展示一种执行您询问的 "switch" 表达式的方法。我只有第 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 ...
您不需要为来自 "registry" table 的每一位数据编写 Oracle 版本的 case 表达式 - 您可以在 case 的两个分支中构建完整的字符串表达。您也可以调整它以适应 "no patch installed" 分支。
祝你好运!