2016-05-01 5 views
1

Ich habe eine Tabelle, die protokollierte Ereignisse für gefertigte Artikel enthält. Wir betrachten jedes Ereignis als 2 Status, die auf Details und Berechnungen der zuvor protokollierten Ereignisse für denselben Artikel basieren. Also habe ich eine SELECT-Abfrage entwickelt, die mehrere Self-Joins verwendet, um Faktoren von früheren Ereignissen in Bezug auf jedes Ereignis zu analysieren und die Status zu berechnen. Da diese Abfrage jedoch relativ langsam ist, habe ich zwei Statusspalten hinzugefügt, und ich möchte die Spalten mit den berechneten Status gut aktualisieren, nachdem die Ereignisse eingetreten sind. Auf diese Weise kann ich später in den Statusspalten schnelle Berichte erhalten, anstatt jedes Mal alle Berechnungen durchführen zu müssen.Werte in einer Tabelle basierend auf einer Self-Join-Abfrage zu sich selbst aktualisieren in Oracle

Hier würde mein Tisch sein:

CREATE TABLE ItemLog 
(
    ItemID decimal(11) NOT NULL, 
    MessageTime DATE NOT NULL, 
    Temperature float(7), 
    Voltage float(7), 
    Status1 VARCHAR2(10 BYTE), 
    Status2 VARCHAR2(10 BYTE), 
    CONSTRAINT "ItemLog_PK" PRIMARY KEY ("ItemID ", "MessageTime ") 
); 

Meine SELECT Berechnung Abfrage so etwas wie diese:

SELECT ItemID, MessageTime, 
    CASE WHEN A.Voltage<B.Voltage and A.Voltage<C.Avg_Voltage and C.SD_Voltage<5 THEN 'Good' ELSE 'Bad' END Calculated_Status1, 
    CASE WHEN A.Temperature<B.Temperature and A.Temperature>C.Temperature and C.SD_Temperature>10 THEN 'Good' ELSE 'Bad' END Calculated_Status2 
FROM ItemLog A, 
    (SELECT F.ItemID, 
     F.MessageTime Key_MessageTime, 
     S.Voltage, 
     S.Temperature 
    FROM ItemLog F, 
     ItemLog S 
    WHERE F.ItemID=S.ItemID 
     and S.MessageTime= 
      SELECT MAX(MessageTime) 
      FROM ItemLog 
      WHERE ItemID=F.ItemID 
       and MessageTime<F.MessageTime 
       and Voltage<12 
       and Temperature<125 
    ) B, -- Returns the Voltage and Temperature from the prior time it was <12 and <125 
    (SELECT K.ItemID, K.MessageTime, 
     AVG(L.Temp) Avg_Temperature, STDDEV(L.Temperature) SD_Temp, 
     AVG(L.Voltage) Avg_Voltage, STDDEV(L.Voltage) SD_Voltage 
    FROM ItemLog K, 
     ItemLog L 
    WHERE K.ItemID=L.ItemID 
     and L.MessageTime= 
      SELECT MAX(MessageTime) 
      FROM ItemLog 
      WHERE ItemID=K.ItemID 
       and MessageTime<K.MessageTime 
    GROUP BY K.ItemID, K.MessageTime 
    ) C -- Returns the Voltage and Temperature stats from all prior messages 
    (SELECT ItemID 
    FROM ItemLog 
    WHERE Voltage>40 
    ) D -- Returns all ItemID where Voltage was ever >40, to exclude them 
WHERE A.ItemID=B.ItemID and A.MessageTime=B.MessageTime 
    and A.ItemID=C.ItemID and A.MessageTime=C.MessageTime 
    and A.ItemID=D.ItemID(+) and D.ItemID IS NULL 

Also, die Frage ist, wie kann ich die Status1 und Status2 Spalten in der Aktualisierung Tabelle soll die Spalten Calculated_Status1 und Calculated Status2 sein? Ich habe versucht, meine Berechnungsabfrage zu nehmen und sie mit den 2 Primärschlüsseln zur Tabelle zu verbinden, aber ich bekomme den Fehler "ORA-01779: kann eine Spalte nicht ändern, die einer nicht Schlüssel-konservierten Tabelle zuordnet".

UPDATE ( 
    SELECT U.*, 
     V.Calculated_Status1 
     V.Calculated_Status2 
    FROM ItemLog U, 
     (<calculation query above>) V 
    WHERE U.ItemID=V.ItemID and U.MessageTime=V.MessageTime) 
SET U.Status1=V.CalculatedStatus1, 
    U.Status2=V.CalculatedStatus2 

Ich könnte ein Update mit einem SET Status1=(SELECT... vorstellen, aber das wäre eine Art korrelierte WHERE für die ItemID und Messagetime benötigt, und ich es schrecklich langsam läuft erwarten würde. Es scheint, dass es einen direkteren Weg geben sollte, dies zu tun?

+0

Ich würde vorschlagen, dass Sie eine andere Frage mit Beispieldaten und gewünschten Ergebnissen stellen. Es ist durchaus möglich, dass Ihre Abfrage beschleunigt wird, ohne neue Spalten zu erstellen. –

Antwort

1

Ich hoffe, dass die Lösung, die Sie suchen, mit MERGE-Anweisung serviert werden kann. Ich hoffe, die von Ihnen gepostete Anfrage ist korrekt. Ich habe die Lösung oben auf der Abfrage erstellt. Lassen Sie mich wissen, ob das hilft.

MERGE INTO ItemLog it USING 
(SELECT ItemID, MessageTime, 
    CASE WHEN A.Voltage<B.Voltage and A.Voltage<C.Avg_Voltage and C.SD_Voltage<5 THEN 'Good' ELSE 'Bad' END Calculated_Status1, 
    CASE WHEN A.Temperature<B.Temperature and A.Temperature>C.Temperature and C.SD_Temperature>10 THEN 'Good' ELSE 'Bad' END Calculated_Status2 
FROM ItemLog A, 
    (SELECT F.ItemID, 
     F.MessageTime Key_MessageTime, 
     S.Voltage, 
     S.Temperature 
    FROM ItemLog F, 
     ItemLog S 
    WHERE F.ItemID=S.ItemID 
     and S.MessageTime= 
      SELECT MAX(MessageTime) 
      FROM ItemLog 
      WHERE ItemID=F.ItemID 
       and MessageTime<F.MessageTime 
       and Voltage<12 
       and Temperature<125 
    ) B, -- Returns the Voltage and Temperature from the prior time it was <12 and <125 
    (SELECT K.ItemID, K.MessageTime, 
     AVG(L.Temp) Avg_Temperature, STDDEV(L.Temperature) SD_Temp, 
     AVG(L.Voltage) Avg_Voltage, STDDEV(L.Voltage) SD_Voltage 
    FROM ItemLog K, 
     ItemLog L 
    WHERE K.ItemID=L.ItemID 
     and L.MessageTime= 
      SELECT MAX(MessageTime) 
      FROM ItemLog 
      WHERE ItemID=K.ItemID 
       and MessageTime<K.MessageTime 
    GROUP BY K.ItemID, K.MessageTime 
    ) C -- Returns the Voltage and Temperature stats from all prior messages 
    (SELECT ItemID 
    FROM ItemLog 
    WHERE Voltage>40 
    ) D -- Returns all ItemID where Voltage was ever >40, to exclude them 
WHERE A.ItemID=B.ItemID and A.MessageTime=B.MessageTime 
    and A.ItemID=C.ItemID and A.MessageTime=C.MessageTime 
    and A.ItemID=D.ItemID(+) and D.ItemID IS NULL)z 
ON 
(it.ItemID = z.ItemID AND it.MessageTime = z.MessageTime) 
WHEN MATCHED THEN 
UPDATE SET it.STATUS1 = z.Calculated_Status1, 
     it.STATUS2 = z.Calculated_Status2; 
+0

Das schien zu funktionieren, aber nur, wenn ich in ItemLog nur eine relativ kleine Anzahl von Datensätzen hatte. Wie in, wenn ItemLog 1200 Ereignisse hat, und ich nur die Abfrage ausführte, um 7 von ihnen zu aktualisieren (durch Einschließen eines BETWEEN-Kriteriums in der Berechnungsabfrage oder auf der 'it' Tabelle in' ON'-Bedingung), dauerte es 4 Sekunden. Wenn ItemLog 2500 Zeilen hatte, dauerte das Aktualisieren derselben 7 Zeilen 21 Sekunden. Das Herausnehmen der Kriterien, so dass alle 2500 Zeilen aktualisiert wurden, dauerte 40 Sekunden. Also, irgendwie muss der JOIN ausgeschaltet sein. –

+0

Ja, dafür müssen wir versuchen, die Abfrage zu optimieren, um die Leistung zu verbessern. –

+0

Ihr Vorschlag mit der Zusammenführung beantwortete letztlich die Frage, die ich gestellt habe, und ich werde mit den Leistungsteilen arbeiten. Vielen Dank! –

Verwandte Themen