【问题标题】:Update values in a table based on a self-join query to itself in Oracle在 Oracle 中根据自联接查询更新表中的值
【发布时间】:2016-05-01 13:18:22
【问题描述】:

我有一个表,其中包含已记录的制造项目事件。我们将每个事件视为具有 2 个状态,这些状态基于同一项目的先前记录事件的详细信息和计算。因此,我开发了一个 SELECT 查询,该查询使用多个自连接来分析与每个事件相关的先前事件的因素,并计算状态。但是因为这个查询比较慢,所以我添加了 2 个状态列,我想在事件发生后用计算的状态更新列。这样我可以稍后在状态列上获得快速报告,而不必每次都运行所有计算。

这是我的桌子:

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

我的 SELECT 计算查询是这样的:

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

那么,问题是,如何将表中的 Status1 和 Status2 列更新为 Calculated_Status1 和 Calculated Status2 列?我尝试使用我的计算查询并通过 2 个主键将其连接到表中,但出现“ORA-01779:无法修改映射到非键保留表的列”错误。

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

我可以想象一个带有SET Status1=(SELECT... 的更新,但这需要某种相关的 WHERE 用于 ItemID 和 MessageTime,我希望它运行得非常慢。似乎应该有更直接的方法来做到这一点?

【问题讨论】:

  • 我建议您用样本数据和所需结果提出另一个问题。很有可能在不创建新列的情况下加快查询速度。

标签: sql oracle self-join


【解决方案1】:

我希望您正在寻找的解决方案可以使用 MERGE 语句提供。我希望您发布的查询是正确的。我已经在查询之上构建了解决方案。让我知道这是否有帮助。

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;

【讨论】:

  • 这似乎有效,但仅当我在 ItemLog 中的总记录数相对较少时。例如,当 ItemLog 有 1200 个事件时,我只运行查询来更新其中的 7 个(通过在计算查询中或在 ON 条件中的 it 表中包含 BETWEEN 条件),它需要 4 秒。当 ItemLog 有 2500 行时,同样的 7 行需要 21 秒才能更新。取出标准,以便更新所有 2500 行需要 40 秒。所以,不知何故 JOIN 必须关闭。
  • 是的,我们需要尝试优化查询以提高性能。
  • 您对合并的建议最终回答了我提出的问题,我将处理性能部分。谢谢!
猜你喜欢
  • 2015-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-11
  • 2018-11-03
  • 1970-01-01
  • 1970-01-01
  • 2015-09-02
相关资源
最近更新 更多