根据 Oracle 中对自身的自连接查询更新 table 中的值
Update values in a table based on a self-join query to itself in Oracle
我有一个 table,其中包含制造物品的记录事件。我们将每个事件视为具有 2 个状态,这些状态基于同一项目的先前记录事件的详细信息和计算。因此,我开发了一个 SELECT 查询,它使用多个自连接来分析与每个事件相关的先前事件的因素,并计算状态。但是因为这个查询相对较慢,我添加了 2 个状态列,并且我想在事件发生后更新具有计算状态的列。这样我以后就可以在状态列上快速获得报告,而不必每次都运行所有的计算。
这是我的 table:
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
所以,问题是,如何将 table 中的 Status1 和 Status2 列更新为 Calculated_Status1 和 Calculated Status2 列?我已尝试采用我的计算查询并通过 2 个主键将其加入 table,但我收到 "ORA-01779: cannot modify a column which maps to a non key-preserved table" 错误。
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...
的 UPDATE,但这需要 ItemID 和 MessageTime 的某种相关 WHERE,我希望它 运行 非常慢。看起来应该有更直接的方法来做到这一点?
我希望您正在寻找的解决方案可以使用 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;
我有一个 table,其中包含制造物品的记录事件。我们将每个事件视为具有 2 个状态,这些状态基于同一项目的先前记录事件的详细信息和计算。因此,我开发了一个 SELECT 查询,它使用多个自连接来分析与每个事件相关的先前事件的因素,并计算状态。但是因为这个查询相对较慢,我添加了 2 个状态列,并且我想在事件发生后更新具有计算状态的列。这样我以后就可以在状态列上快速获得报告,而不必每次都运行所有的计算。
这是我的 table:
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
所以,问题是,如何将 table 中的 Status1 和 Status2 列更新为 Calculated_Status1 和 Calculated Status2 列?我已尝试采用我的计算查询并通过 2 个主键将其加入 table,但我收到 "ORA-01779: cannot modify a column which maps to a non key-preserved table" 错误。
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...
的 UPDATE,但这需要 ItemID 和 MessageTime 的某种相关 WHERE,我希望它 运行 非常慢。看起来应该有更直接的方法来做到这一点?
我希望您正在寻找的解决方案可以使用 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;