【问题标题】:Update rows based on another table基于另一个表更新行
【发布时间】:2012-08-23 08:41:33
【问题描述】:

我有一张表格可以记录我库存中链轮的物理质量。

create table sprockets(
    id NUMBER,
    mass NUMBER
);

INSERT into sprockets VALUES (1, 4);
INSERT into sprockets VALUES (2, 8);
INSERT into sprockets VALUES (3, 15);
INSERT into sprockets VALUES (4, 16);
INSERT into sprockets VALUES (5, 23);
INSERT into sprockets VALUES (6, 42);

我聘请链轮机械师对我的链轮进行日常维护。如果他们的修改使链轮的质量发生了变化,他们会在维护报告中记录下来。

create table maintenance_events(
    sprocket_id NUMBER,
    new_mass NUMBER
);

--chipped a widget off of sprocket #1; mass reduced to 3 kg
INSERT into maintenance_events VALUES (1, 3);       
--new lead bearings makes sprocket #2 weigh 413 kg
INSERT into maintenance_events VALUES (2, 413);     

我想使用每个链轮的当前质量来更新sprockets 表。我想采用maintenance_events 中的new_mass 并覆盖sprockets 中的旧mass 值。我参考了this question 的前两个答案,但都给出了错误。

UPDATE sprockets
set mass = maintenance_events.new_mass
from sprockets, maintenance_events
where sprockets.id = maintenance_events.sprocket_id

Error at Command Line:2 Column:38
Error report:
SQL Error: ORA-00933: SQL command not properly ended

UPDATE sprockets
set sprockets.mass = maintenance_events.new_mass
from sprockets
INNER JOIN maintenance_events
on sprockets.id = maintenance_events.sprocket_id

Error at Command Line:2 Column:48
Error report:
SQL Error: ORA-00933: SQL command not properly ended

我做错了什么?

【问题讨论】:

  • 除非您只为每个 sprocket 保留最新的维护事件,否则您可能还希望在该表中存储一个日期,以便您可以确定您使用的是该 sprocket 的最新值跨度>
  • 所有语句都以分号结尾吗?我找到了this link
  • @Joe,这在我的 2.0 版本列表中。目前,我的维护数据集保证具有唯一的链轮 ID。
  • 链接的 Q 是关于 SQL Server 的。 Oracle 不支持“UPDATE...FROM...”

标签: sql oracle11g sql-update


【解决方案1】:

这就是 merge(一个 upsert)的用途:

merge into sprockets s
using ( select * from maintenance_events ) m
on (s.id = m.sprocket_id)
when matched then
 update 
    set s.mass = m.new_mass
        ;

这比使用where not exists 等进行多次表扫描要高效得多。

这是一个 SQL Fiddle 来证明它有效。

【讨论】:

  • 您需要在查询中为合并做的就是拥有某种时间戳字段等,并且只获取每个 sprocket_id 的最新记录。
  • @rfusca,你不需要需要,除非maintenance_events 中有多个,但此表中没有时间戳。您可以轻松地将rank() over ( partition by sprocket_id order by new_mass ) as rnk 或类似于selectrnk = 1 添加到on 子句以绕过它。
  • 好吧,我假设他在现实世界中的 maintenance_event 每个 sprocket 会有多个记录,否则只需更新 sprocket 表。
  • 在问题范围内使用排名是任意的,但在现实世界中却很愚蠢。没有理由认为链轮的质量只会增加或减少。
  • @rfusca,这就是我的意思......因为表格中没有时间戳,如果没有它,您的问题将完全不确定。 merge,将与给出的其他查询完全相同,如果有多个 sprocket_id,则会失败。但是,它更有效。
【解决方案2】:

这个怎么样?

UPDATE sprockets
SET sprockets.mass = (select new_mass 
                           from maintenance_events 
                      where sprockets.id = maintenance_events.sprocket_id)
WHERE EXISTS (select new_mass 
                           from maintenance_events 
                      where sprockets.id = maintenance_events.sprocket_id);

【讨论】:

  • 它说,命令行错误:1 列:16 SQL 错误:ORA-00971:缺少 SET 关键字
  • 哎呀。现在试试。我一定对我的 WHERE 和 SET 位置感到困惑
  • 现在我得到了前两次尝试的错误,“Sql 命令未正确结束”
  • 如果您有多个 sprocket_id 记录,那么这将中断。
【解决方案3】:

试试这个:

UPDATE sprockets
set mass = (select maintenance_events.new_mass
from maintenance_events
where sprockets.id = maintenance_events.sprocket_id) where exists 
(select maintenance_events.sprocket_id from maintenance_events 
 where sprockets.id = maintenance_events.sprocket_id);

您可以在这里查看:http://sqlfiddle.com/#!5/f4262/11/0

【讨论】:

  • 这部分有效,因为它将链轮 1 的质量按预期设置为 5。但是,我的所有其他质量都已设置为空!幸好我做了备份:-)
  • 是的,这个查询不包含过滤器
猜你喜欢
  • 2013-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-12
  • 1970-01-01
相关资源
最近更新 更多