Frage

Ich habe PL/SQL-Code geschrieben, um eine Tabelle in eine viel einfacher abzufragende Form zu denormalisieren.Der Code verwendet für einen Teil seiner Arbeit eine temporäre Tabelle, indem er einige Zeilen aus der Originaltabelle zusammenführt.

Die Logik wird als geschrieben Pipeline-Tabellenfunktion, nach dem Muster aus dem verlinkten Artikel.Die Tabellenfunktion verwendet a PRAGMA AUTONOMOUS_TRANSACTION Deklaration, um die Manipulation temporärer Tabellen zu ermöglichen, und akzeptiert auch einen Cursor-Eingabeparameter, um die Denormalisierung auf bestimmte ID-Werte zu beschränken.

Anschließend habe ich eine Ansicht zum Abfragen der Tabellenfunktion erstellt und dabei alle möglichen ID-Werte als Cursor übergeben (andere Verwendungszwecke der Funktion sind restriktiver).

Meine Frage:ist das alles wirklich notwendig?Habe ich eine viel einfachere Möglichkeit, dasselbe zu erreichen, völlig übersehen?

Jedes Mal, wenn ich PL/SQL berühre, habe ich den Eindruck, dass ich viel zu viel tippe.

Aktualisieren: Ich füge eine Skizze der Tabelle hinzu, mit der ich mich befasse, um allen eine Vorstellung von der Denormalisierung zu geben, von der ich spreche.Die Tabelle speichert einen Verlauf der Mitarbeiterjobs, jeweils mit einer Aktivierungszeile und (möglicherweise) einer Beendigungszeile.Es ist möglich, dass ein Mitarbeiter gleichzeitig mehreren Jobs nachgeht und in nicht zusammenhängenden Datumsbereichen immer wieder denselben Job ausübt.Zum Beispiel:

| EMP_ID | JOB_ID | STATUS | EFF_DATE    | other columns...
|      1 |     10 | A      | 10-JAN-2008 |
|      2 |     11 | A      | 13-JAN-2008 |
|      1 |     12 | A      | 20-JAN-2008 |
|      2 |     11 | T      | 01-FEB-2008 |
|      1 |     10 | T      | 02-FEB-2008 |
|      2 |     11 | A      | 20-FEB-2008 |

Das abzufragen, um herauszufinden, wer wann in welchem ​​Job arbeitet, ist nicht trivial.Meine Denormalisierungsfunktion füllt die temporäre Tabelle also nur mit den Datumsbereichen für jeden Job, für jeden EMP_IDs wird durch den Cursor übergeben.Vorbeigehen EMP_IDs 1 und 2 würden Folgendes ergeben:

| EMP_ID | JOB_ID | START_DATE  | END_DATE    |
|      1 |     10 | 10-JAN-2008 | 02-FEB-2008 |
|      2 |     11 | 13-JAN-2008 | 01-FEB-2008 |
|      1 |     12 | 20-JAN-2008 |             |
|      2 |     11 | 20-FEB-2008 |             |

(END_DATE erlaubt NULLs für Jobs, die kein vorher festgelegtes Kündigungsdatum haben.)

Wie Sie sich vorstellen können, lässt sich diese denormalisierte Form viel, viel einfacher abfragen, aber um sie zu erstellen, ist – soweit ich das beurteilen kann – eine temporäre Tabelle erforderlich, um die Zwischenergebnisse zu speichern (z. B. Jobdatensätze, für die die Aktivierungszeile erstellt wurde). gefunden, aber noch nicht die Kündigung).Die Verwendung der Pipeline-Tabellenfunktion zum Auffüllen der temporären Tabelle und zum anschließenden Zurückgeben ihrer Zeilen ist die einzige Möglichkeit, die ich herausgefunden habe.

War es hilfreich?

Lösung

Ich denke, eine Möglichkeit, dies zu erreichen, besteht darin, analytische Funktionen zu verwenden ...

Ich habe Ihren Testfall eingerichtet mit:

create table employee_job (
    emp_id integer,
    job_id integer,
    status varchar2(1 char),
    eff_date date
    );  

insert into employee_job values (1,10,'A',to_date('10-JAN-2008','DD-MON-YYYY'));
insert into employee_job values (2,11,'A',to_date('13-JAN-2008','DD-MON-YYYY'));
insert into employee_job values (1,12,'A',to_date('20-JAN-2008','DD-MON-YYYY'));
insert into employee_job values (2,11,'T',to_date('01-FEB-2008','DD-MON-YYYY'));
insert into employee_job values (1,10,'T',to_date('02-FEB-2008','DD-MON-YYYY'));
insert into employee_job values (2,11,'A',to_date('20-FEB-2008','DD-MON-YYYY'));

commit;

Ich habe das verwendet führen Funktion, um das nächste Datum abzurufen, und verpackte dann alles als Unterabfrage, nur um die „A“-Datensätze abzurufen und das Enddatum hinzuzufügen, falls vorhanden.

select
    emp_id,
    job_id,
    eff_date start_date,
    decode(next_status,'T',next_eff_date,null) end_date
from
    (
    select
        emp_id,
        job_id,
        eff_date,
        status,
        lead(eff_date,1,null) over (partition by emp_id, job_id order by eff_date, status) next_eff_date,
        lead(status,1,null) over (partition by emp_id, job_id order by eff_date, status) next_status
    from
        employee_job
    )
where
    status = 'A'
order by
    start_date,
    emp_id,
    job_id

Ich bin mir sicher, dass es einige Anwendungsfälle gibt, die ich übersehen habe, aber Sie verstehen, worauf es ankommt.Analytische Funktionen sind dein Freund :)

EMP_ID   JOB_ID     START_DATE     END_DATE            
  1        10       10-JAN-2008    02-FEB-2008         
  2        11       13-JAN-2008    01-FEB-2008         
  2        11       20-FEB-2008                              
  1        12       20-JAN-2008                              

Andere Tipps

Anstatt den Eingabeparameter als Cursor zu haben, würde ich eine Tabellenvariable haben (ich weiß nicht, ob Oracle so etwas hat, ich bin ein TSQL-Typ) oder eine andere temporäre Tabelle mit den ID-Werten füllen und diese in der Ansicht verknüpfen /function oder wo auch immer Sie benötigen.

Meiner ehrlichen Meinung nach ist der einzige Zeitpunkt für Cursor, wenn Sie haben schleifen.Und wenn Sie eine Schleife ausführen müssen, empfehle ich immer, dies außerhalb der Datenbank in der Anwendungslogik zu tun.

Es hört sich so an, als würden Sie hier etwas Lesekonsistenz verschenken, z. B.:Es ist möglich, dass der Inhalt Ihrer temporären Tabelle nicht mit den Quelldaten synchronisiert ist, wenn Sie gleichzeitig Änderungen an den Daten vornehmen.

Ohne die Anforderungen oder die Komplexität dessen zu kennen, was Sie erreichen möchten.Ich würde es versuchen

  1. um eine Ansicht zu definieren, die (möglicherweise komplexe) Logik in SQL enthält, sonst würde ich der Mischung etwas PL/SQL hinzufügen;
  2. Eine Pipeline-Tabellenfunktion, die jedoch einen SQL-Sammlungstyp verwendet (anstelle der temporären Tabelle).Ein einfaches Beispiel ist hier: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4447489221109

Nummer 2 würde Ihnen weniger bewegliche Teile geben und Ihr Konsistenzproblem lösen.

Mathew Butler

Das eigentliche Problem hier ist das „schreibgeschützte“ Tabellendesign – ich meine damit, dass es einfach ist, Daten einzufügen, aber schwierig und ineffizient, nützliche Informationen daraus zu gewinnen!Ihre „temporäre“ Tabelle hat die Struktur, die die „permanente“ Tabelle ursprünglich haben sollte.

Könnten Sie vielleicht Folgendes tun:

  • Erstellen Sie eine permanente Tabelle mit der besseren Struktur
  • Füllen Sie es so aus, dass es mit den Daten in der ersten Tabelle übereinstimmt
  • Definieren Sie einen Datenbanktrigger für die ursprüngliche Tabelle, um die neue Tabelle von nun an synchron zu halten

Anschließend können Sie einfach aus der neuen Tabelle auswählen, um Ihre Berichterstellung durchzuführen.

Ich kann dir nur zustimmen, HollyStyles.Ich war früher auch ein TSQL-Typ und finde einige der Eigenheiten von Oracle mehr als nur verwirrend.Leider sind temporäre Tabellen in Oracle nicht so praktisch, und in diesem Fall erwartet eine andere vorhandene SQL-Logik, eine Tabelle direkt abzufragen, daher gebe ich ihr stattdessen diese Ansicht.In diesem System gibt es wirklich keine Anwendungslogik außerhalb der Datenbank.

Oracle-Entwickler scheinen Cursor viel häufiger zu verwenden, als ich gedacht hätte.Angesichts der Knechtschaft und Disziplinierung von PL/SQL ist das umso überraschender.

Die einfachste Lösung ist:

  1. Ein ... kreieren globale temporäre Tabelle Enthält nur die IDs, die Sie benötigen:

    CREATE GLOBAL TEMPORARY TABLE tab_ids (id INTEGER)  
    ON COMMIT DELETE ROWS;
    
  2. Füllen Sie die temporäre Tabelle mit den benötigten IDs.

  3. Verwenden Sie in Ihrer Prozedur die EXISTS-Operation, um die Zeilen auszuwählen, die nur in der IDs-Tabelle enthalten sind:

      SELECT yt.col1, yt.col2 FROM your\_table yt  
       WHERE EXISTS (  
          SELECT 'X' FROM tab_ids ti  
           WHERE ti.id = yt.id  
       )
    

Sie können auch eine durch Kommas getrennte Zeichenfolge von IDs als Funktionsparameter übergeben und diese in eine Tabelle analysieren.Dies wird durch ein einzelnes SELECT durchgeführt.Wenn Sie mehr wissen möchten, fragen Sie mich wie :-) Aber es muss eine separate Frage sein.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top