2016-07-19 28 views
2

Ich versuche, eine Stichprobe von Daten aus jeder Spalte in jeder Tabelle in etwa 5 Schemas zu greifen. Unten ist ein Beispiel, wie ich diese Daten von nur einem Schema bekomme (ersetze "sde" mit was auch immer dein Schema ausführen soll). Dieses Stück läuft gut:Oracle/SQL Beispiel von Daten aus jeder Spalte aus jeder Tabelle - zu viel?

select CASE when 
lead(ROWNUM) over(order by ROWNUM) is null then 
'select '||''''||T.TABLE_NAME||''''||' as TABLE_NAME,'||''''||T.COLUMN_NAME||''''||' as COLUMN_NAME, cast('|| T.COLUMN_NAME ||' as 
VarChar2(50)) as SAMPLE_DATA from sde.'||T.TABLE_NAME ||' where '||T.COLUMN_NAME||' is not null and ROWNUM=1;' 
else 
'select '||''''||T.TABLE_NAME||''''||' as TABLE_NAME,'||''''||T.COLUMN_NAME||''''||' as COLUMN_NAME, cast('|| T.COLUMN_NAME ||' as 
VarChar2(50)) as SAMPLE_DATA from sde.'||T.TABLE_NAME ||' where '||T.COLUMN_NAME||' is not null and ROWNUM=1 union all' end as qry_txt 
from all_tab_columns t where T.OWNER='SDE' and T.DATA_TYPE != 'BLOB' and T.DATA_TYPE != 'LONG' 
ORDER BY ROWNUM asc, qry_txt asc 

Wenn die oben ausgeführt wird resultierende, hier ist ein Beispiel für 1 Zeile der Ausgabe:

select 'HUD_TYPE' as TABLE_NAME,'HUD_TYPE_ID' as COLUMN_NAME, cast(HUD_TYPE_ID as VarChar2(50)) as SAMPLE_DATA from sde.HUD_TYPE where HUD_TYPE_ID is not null and ROWNUM=1 union all 

Das Problem, das ich mit Blick auf bin, ist, dass, wenn ich laufe die komplette Reihe von Gewerkschaften, es wird nie abgeschlossen, habe ich nur in der Lage gewesen, mit zu einer Zeit, ein paar hundert bis ein paar tausend Zeilen laufen: mit nur 5.000 Zeilen

select CASE when 
lead(ROWNUM) over(order by ROWNUM) is null then 
'select '||''''||T.TABLE_NAME||''''||' as TABLE_NAME,'||''''||T.COLUMN_NAME||''''||' as COLUMN_NAME, cast('|| T.COLUMN_NAME ||' as 
VarChar2(50)) as SAMPLE_DATA from sde.'||T.TABLE_NAME ||' where '||T.COLUMN_NAME||' is not null and ROWNUM=1;' 
else 
'select '||''''||T.TABLE_NAME||''''||' as TABLE_NAME,'||''''||T.COLUMN_NAME||''''||' as COLUMN_NAME, cast('|| T.COLUMN_NAME ||' as 
VarChar2(50)) as SAMPLE_DATA from sde.'||T.TABLE_NAME ||' where '||T.COLUMN_NAME||' is not null and ROWNUM=1 union all' end as qry_txt 
from all_tab_columns t where T.OWNER='SDE' and T.DATA_TYPE != 'BLOB' and T.DATA_TYPE != 'LONG' 
ORDER BY ROWNUM asc, qry_txt asc 

OFFSET 4800 ROWS FETCH NEXT 400 ROWS ONLY; --Using this method so I grab the last few hundred lines so my case statement remains valid for demo 

Dieses besondere Schema ist die kleinste im Bund zurückgeben. Ist das, was ich versuche, eine unmögliche Aufgabe für eine On-The-Fly-Abfrage zu tun? Oder gibt es einen Weg, wie ich das effizienter machen oder in Schleifen aufteilen kann, um auf irgendeine Weise Stücke zu packen? Versuche zu vermeiden, dass wir unsere Entwickler involvieren müssen und Tabellen erstellen, ETLs usw. Ich bin kein SQL-Experte, aber wenn ich in die richtige Richtung gewiesen werde, kann ich es aushacken. :)

Vielen Dank im Voraus.

+0

Was ist das eigentliche Problem, das Sie versuchen zu lösen? Vielleicht ist das Sampling von Daten aus jeder Spalte in jeder Tabelle in fünf Schemas nicht die einzige Lösung für Ihr tatsächliches Problem (und vielleicht ist es nicht einmal die beste Lösung). – mathguy

+0

Sicher. Ich habe einen Tableau-Bericht für Kunden erstellt, der eine schnelle Möglichkeit bietet, unsere verfügbaren Tabellen und Spalten zu durchsuchen. Sie enthält Spalten wie Schema, Objekttyp (Tabelle, Ansicht usw.), Tabellenname, Spaltenname, Datentyp und Kommentare. Ich wollte auch "Sample Data" als eine Spalte, die eine einzelne Zeile von Daten aus jeder Spalte in jeder Tabelle ist. Ich weiß, wie es geht, aber die Union-Set scheint mit jeder normalen Größe Datensatz hängen ... Hoffnung für einige Optimierung Zeiger oder alternative Methoden, um das gleiche zu erreichen. – Drivium

Antwort

1

Ja, das ist wahrscheinlich zu viel in einer Abfrage zu tun.

Die Abfrage könnte hoffnungslos langsam sein wegen Parse Bugs, rekursivem SQL oder Leerraum Diese Aufgabe erfordert wahrscheinlich eine gewisse Entwicklung - entweder eine schwierige, aber genaue gespeicherte Prozedur oder eine schnelle und ungenaue Version, die Spaltenstatistiken verwendet.


Warum könnte es langsam

  1. Parse Time Oracle-Compiler in der Regel schnell, aber es gibt einige seltsame Fälle, in denen die Analysezeit exponentiell wächst. Die Verwendung einer großen Anzahl von UNION ALLs ist einer dieser Fälle. Vor allem mit älteren Versionen, wie 10g, wo mehr als 500 UNION ALLs für immer laufen würde. Diese Probleme wirken sich auf ähnliche Methoden aus, z. B. auf einen Einsatz mit mehreren Tabellen, wie in this answer gezeigt. Es gibt also wahrscheinlich keinen einfachen Weg dazu. Dieser Code zeigt, wie die Abfragezeit exponentiell mit UNION ALL ansteigt. Dies ist der beste mögliche Fall, nur die DUAL Tabelle verwenden.

    --Find time to execute simple statements. 
    declare 
        v_sql clob := 'select 1 a from dual'; 
        v_count number; 
        v_time_before number; 
        v_time_after number; 
    begin 
        for i in 1 .. 5000 loop 
         v_sql := v_sql || ' union all select 1 a from dual'; 
         --Only execute every 100th iteration. 
         if mod(i, 100) = 0 then 
          v_time_before := dbms_utility.get_time; 
          execute immediate 'select count(*) from ('||v_sql||')' into v_count; 
          v_time_after := dbms_utility.get_time; 
          dbms_output.put_line(i||':'||to_char(v_time_after-v_time_before)); 
         end if; 
        end loop; 
    end; 
    /
    

    enter image description here

  2. rekursive SQL Manchmal ist die SQL verwendet, um die SQL-Anweisung vorbereiten kann ein Problem sein. Normalerweise ist der schlimmste Täter eine dynamische Stichprobe. Dynamisches Sampling generiert Abfragen, um eine kleine Datenmenge zu finden, um Statistiken für die aktive Tabelle zu generieren. Normalerweise ist es schnell, aber manchmal können diese Abfragen schlechte Ausführungspläne haben und langsam sein. Sie können es mit einem Hinweis wie /*+ dynamic_sampling(0) */ deaktivieren.

    Wenn Sie GV $ SQL betrachten, können Sie andere rekursive SQL finden, die ähnliche Hinweise verwendet. Möglicherweise möchten Sie einige dieser Hinweise kopieren, z. B. /*+ no_parallel */.

  3. Leerer Raum Das Abrufen der ersten Zeile ist nicht unbedingt eine triviale Sache. Es ist möglich, dass eine Tabelle zuvor ein Terabyte an Daten hatte, jemand 99,9% gelöscht hat und nur eine Zeile am Ende des Segments sitzt. Oracle muss durch den ganzen leeren Raum schauen, um diese eine Zeile zu finden. Es gibt Möglichkeiten, das zu lösen, zum Beispiel den Tisch neu zu organisieren, aber es ist möglich, dass jemand das vergessen hat.

    Oder möglicherweise gibt es eine Milliarde Zeilen, aber nur einer von ihnen hat einen Wert für diese Spalte. Und diese Spalte hat keinen Index, daher muss die gesamte Tabelle gelesen werden.

Mögliche Lösungen

  1. Die genaue, Painful Way Die genaueste Lösung erfordert eine gespeicherte Prozedur durch kleinere Stücke von Unterabfragen in einer Zeit zu suchen. Möglicherweise die Verwendung von Parallelität für alle Leerraumprobleme. Wahrscheinlich müssen Sie die Werte vorübergehend in einer Tabelle speichern und einige langsame Abfragen auf dem Weg beheben. Eine kleine Chunk-Größe vermeidet Parsing-Probleme und macht es zumindest einfacher, Unterabfragen mit anderen Problemen zu finden.

  2. Der ungenaue, schnelle Weg Oracle sammelt standardmäßig Optimierungsstatistiken für jede Spalte in der Datenbank. (Sie sollten mit dem DBA überprüfen, ob die Standardwerte nicht deaktiviert wurden, was leider viele DBAs tun.) Mit den Standardalgorithmen überprüft Oracle jede Zeile in jeder Tabelle und zeichnet den oberen und unteren Wert für jede Spalte auf.

    Diese hohen und niedrigen Werte können fast sofort aus dem Datenwörterbuch gelesen werden. Das Problem ist, dass die Konvertierung auf den ursprünglichen Wert nicht vollständig genau ist.

    Der folgende Code ist von this article von Jonathan Lewis und speziell von einem anonymen Kommentar.

    create or replace function raw_to_num(i_raw raw) 
    return number 
    as 
        m_n number; 
    begin 
        dbms_stats.convert_raw_value(i_raw,m_n); 
        return m_n; 
    end; 
    / 
    
    create or replace function raw_to_date(i_raw raw) 
    return date 
    as 
        m_n date; 
    begin 
        dbms_stats.convert_raw_value(i_raw,m_n); 
        return m_n; 
    end; 
    / 
    
    create or replace function raw_to_varchar2(i_raw raw) 
    return varchar2 
    as 
        m_n varchar2(32767); 
    begin 
        dbms_stats.convert_raw_value(i_raw,m_n); 
        return m_n; 
    end; 
    /
    

    Abfrage, die Ergebnisse sehr schnell zurück:

    select 
         table_name, 
         column_name, 
         decode(data_type, 
           'VARCHAR2',to_char(raw_to_varchar2(low_value)), 
           'DATE',to_char(raw_to_date(low_value)), 
           'NUMBER',to_char(raw_to_num(low_value)) 
         ) low_value, 
         decode(data_type, 
           'VARCHAR2',to_char(raw_to_varchar2(high_value)), 
           'DATE',to_char(raw_to_date(high_value)), 
           'NUMBER',to_char(raw_to_num(high_value)) 
         ) high_value 
    from all_tab_columns t where T.OWNER='SDE' and T.DATA_TYPE != 'BLOB' and T.DATA_TYPE != 'LONG' 
    order by 1,2 
    
+0

Vielen Dank für die sehr gründliche Antwort. Das ist eine großartige Information. Klingt unwahrscheinlich, dass das, was ich suche, mit einer einfachen Abfrage erreichbar ist. Es scheint, dass ich keine Erlaubnis habe, Funktionen auszuführen ... Ich muss mich vielleicht bei dieser Anstrengung geschlagen geben. – Drivium