2016-05-10 5 views
0

Ich muss den Patch-Status auf Oracle-Datenbanken abfragen. Seit Oracle Version 12c wurde die View sys.REGISTRY $ HISTORY durch die View DBA_REGISTRY_SQLPATCH ersetzt. Bei älteren Versionen wie 11g ist die Ansicht dba_registry_sqlpatch nicht vorhanden. Die folgende Abfrage erstellt Fehler in Oracle-Versionen < 12c, da die Ansicht dba_registry_sqlpatch nicht vorhanden ist. Ich muss eine Abfrage erstellen, die auf allen Oracle-Datenbankversionen ausgeführt wird. Ich kann PL/SQL nicht verwenden. Ich denke, es sollte mit einem Fallausdruck gelöst werden.Switch-Case-Ausdruck für Oracle-Datenbankversionen

/* 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; 

Beispiel Ausgabe auf einer Oracle-Datenbank 12c gepatcht 11 Tagen: DIFF: 11 Tage ACTION = GILT VERSION = 12.1.0.2 DATE = 20160429 ID = 22809813 KOMMENTAR = WINDOWS DB BUNDLE PATCH 12.1.0.2.160419 (64bit): 22809813 PORT = IBMPC/WIN_NT64-9.1.0

+0

12c funktioniert - nur bei älteren Versionen ist eine andere Handhabung erforderlich. – r0tt

+0

REGISTRY $ HISTORY existiert noch in 12c, ist aber nicht ausgefüllt. – r0tt

Antwort

2

Ein Case-Ausdruck löst Ihr Problem nicht. Die abgefragten Tabellen müssen zum Zeitpunkt der Analyse bekannt sein. Sie können den Tabellennamen nicht dynamisch auswählen, wenn die Abfrage ausgeführt wird, und ein Fall erhält immer noch einen ORA-00942, bevor der Fall ausgewertet wird.

Sie nur die Spalten wollen Unter der Annahme, dass sowohl in existieren der alten Tabelle und die neue Ansicht, könnten Sie ein bisschen XML Übersetzung verwenden, um die Daten zu erhalten, von welcher ein solches vorhanden ist:

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; 

Und dann select x.* ersetzen mit allem, was Sie mit den Daten tun möchten, im wesentlichen, dass in ihr vorhandene Abfrage Aufstecken, eine Vereinigung Hinzufügen der ungepatchten Version Informationen zu bekommen:

... 
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; 

der to_char() ist der Zeitstempel-Wert in den ISO-forma zu erhalten t das wird in XML erwartet. Die Aufrufe dbms_xmlgen() konvertieren die Daten aus Tabelle/View in eine XML-Darstellung. und die XMLTable() konvertiert es zurück. Das scheint ein wenig sinnlos, aber Sie können den Objektnamen erst zur Laufzeit kennen.

Da die Säulen sind etwas anders (ID, COMMENTS vs. PATCH_ID, DESCRIPTION) erhält diese separate XML entweder aus der Tabelle oder der Ansicht über eine union all, aber nicht von beiden als das wäre ein ungültiges XML-Dokument geben. In 12c sieht es aus wie REGISTRY$HISTORY wird leer sein, aber falls es nicht ist, wird es keine Daten davon erhalten, wenn DBA_REGISTRY_SQLPATCH existiert. (Ich bin ein bisschen faul und überprüfe nicht die Eigentumsrechte, also würde jemand anders, der eine Tabelle mit diesem Namen erstellt, ein Problem sein, aber leicht behoben werden). Es aliasiert die Namen der Spalten, so dass sie für alle Tabellen/Views identisch sind, die verwendet werden, sodass das XML entpackt werden kann.

Putting, dass zusammen mit dem String-Formatierung, die Unterabfragen zu beseitigen, und mit the last analytic function nur der letzten Zeile zu halten, können Sie mit etwas wie Ende:

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 
); 

auf 11.2.0.4 Getestet und 10.2. 0.5, aber ich habe keine ungepatchte Instanz oder eine 12c-Instanz, um zu verifizieren, dass sie sich so verhält, wie Sie das erwarten.

+0

Ich denke, ein Fallausdruck kann verwendet werden (ich habe gerade gezeigt, wie). Die Tabellennamen sind tatsächlich zum Zeitpunkt der Syntaxanalyse bekannt, das ist kein Problem - sie sind in der Abfrage fest codiert. Welche Tabelle verwendet wird, kann mit der Bedingung im Case-Ausdruck ausgewählt werden. – mathguy

+0

@mathguy - der Tabellenname ist bekannt, aber es existiert nicht, also wird es einen Fehler geben? –

+0

Oh .... ** guter Punkt! ** Immer noch meine schlechten Gewohnheiten von prozeduralen Programmiersprachen ... Ich bin sicher, du hast Recht, der Parser wird nicht glücklich sein. Ich hatte nur einen weiteren schlechten Gedanken: Speichere die Tabellennamen in einer kleinen Tabelle und wähle sie von dort aus - aber das wird auch nicht fliegen, da der Parser Tabellennamen benötigt und nicht etwas, das zur Laufzeit aus einer Tabelle gezogen wird. Schade, es war eine clevere Idee! (wenn ich es selbst sage). Vielen Dank! – mathguy

0

Bearbeiten: Wie Alex Poole in einem Kommentar (zu seiner Antwort statt zu meiner) zeigt, wird das, was ich unten beschreibe, NICHT FUNKTIONIEREN. Es ist tatsächlich eine gute Illustration von dem, was in dieser Art von Situation NICHT funktionieren wird.

Ich verlasse es hier nur, damit Leute, die das schon gesehen haben, eine Chance haben zu sehen, dass es nicht gut ist. Ich werde die Antwort nach einer Weile löschen.

Danke Alex für den Hinweis!

-

Offensichtlich können Sie Ihre eigenen Abfragen schreiben, also werde ich zeigen hier nur eine Möglichkeit, den „Schalter“ Ausdruck Sie gebeten, zu tun. Ich habe nur Version 11 (freie Ausgabe), also kann ich nicht vollständig testen, aber das sollte funktionieren. Um die Version der Oracle DB zu finden, in der sich Ihre Sitzung befindet, können Sie die Ansicht V $ VERSION abfragen. Auf meinem Rechner sehe ich die Oracle-Version wird wie folgt angezeigt:

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 Unter der Annahme, in Oracle 12c nicht (änderte das heißt: es gibt nach wie vor eine Ansicht v $ Version ist, wird die Spalte noch genannt Banner und die Oracle-DB-Version ist wie Oracle Database 12c gezeigt ....), um nur die ACTION_TIME bekommen Sie so etwas tun könnte:

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 ... 

Sie brauchen keinen Fall Ausdruck für die Oracle zu schreiben Version für jedes Bit Daten aus der "Registrierung" -Tabelle - Sie können die vollständigen Zeichenfolgen in den beiden Zweigen des Case-Ausdrucks erstellen. Sie können dies anpassen, um auch den Zweig "no patch installed" zu berücksichtigen.

Viel Glück!

+0

Die Unterabfrage wird noch geparst, also wirft in 11g die 'from dba_registry_sqlpatch' ORA-00942? –

+0

OK ... Würde es funktionieren, wenn man unter 11g eine Dummy-Tabelle "dba_registry_sqlpatch" erstellt, mit nichts drin, nur damit der Parser zufrieden ist? Obwohl, selbst wenn das funktioniert, ist es vielleicht eine schlechte Übung ... – mathguy

+0

Ha, ich habe das gerade auf einen Kommentar zu meiner Antwort bezogen ... es würde wahrscheinlich funktionieren, aber ich denke nicht, dass es eine gute Idee ist. Und wenn Sie hätten, würde die ursprüngliche Abfrage des OPs sowieso funktionieren. –