【问题标题】:How to avoid ORA-01427 in update statement?如何在更新语句中避免 ORA-01427?
【发布时间】:2018-07-02 07:46:25
【问题描述】:

我知道下面的子查询应该返回 1 行。 您能告诉我如何更改下面的 UPDATE 查询以使用映射表的行更新 test1 表吗?

update test1 a set daily_value = ( select daily_value from mapping b where a.table_name = b.table_name);
                              *
ERROR at line 1:
ORA-01427: single-row subquery returns more than one row


create table test1
( table_name varchar2(10),
  daily_value varchar2(10)
)
/

insert into test1(table_name) values ('first');
insert into test1(table_name) values ('first');
commit;


create table mapping
( table_name varchar2(10),
  daily_value varchar2(10)
)
/

insert into mapping values ('first','value_1');
insert into mapping values ('first','value_2');
commit;



TEST1 table should have below data

TABLE_NAME  DAILY_VALUE
FIRST       value_1
FIRST       value_2

【问题讨论】:

  • 子查询返回两行value_1value_2
  • test1 中有两个相等的行;你如何决定哪个得到value_1,哪个得到value_2
  • 我不需要区分 test1 表中的 value_1 和 value_2。我只需要用 2 个值更新 test1。
  • 您能否编辑您的帖子并描述表 test1 的值在更新后应如何显示?
  • 没有 Ansi-SQL 方法可以做到这一点,因为您的表不提供要匹配的键。

标签: sql oracle sql-update subquery


【解决方案1】:

您需要确定表的排序标准,以决定哪些行获得value_1,哪些行获得value_2。 假设您没有订单,一个简单的rownum 可以工作;例如:

merge into test1 t1
using( select daily_value, table_name, rownum as rn 
       from mapping
     ) M
on (M.table_name = t1.table_name and rownum = M.rn)     
when matched then 
    update set daily_value = M.daily_value    

给予:

TABLE_NAME DAILY_VALUE
---------- -----------
first      value_1    
first      value_2

【讨论】:

  • 太棒了!你能把这个逻辑应用到 UPDATE 语句中吗?这样做有点困惑。
  • @Sigularity 为什么不能使用 Aleksej 提供的 MERGE 语句?
  • 抱歉,我应该将此逻辑应用到其他 dbms 以及 PostgreSQL 中。
  • 我对 Postgres 一无所知,所以我建议您编辑问题以澄清您需要一个同时在 Oracle 和 Postgres 中工作的解决方案,并等待了解这两个系统的人
  • 好的。非常感谢。它至少在 Oracle 上运行良好。
【解决方案2】:

更新无法返回 1 行,因为它无法区分表 test1 中的两行,因为它们具有相同的值“first”。如果您将数据更改为具有不同的值,它会按预期工作:

DELETE FROM mapping;
DELETE FROM test1;

INSERT INTO test1(table_name) VALUES ('first');
INSERT INTO test1(table_name) VALUES ('second');

INSERT INTO mapping VALUES ('first','value_1');
INSERT INTO mapping VALUES ('second','value_2');

update test1 a set daily_value = ( select daily_value from mapping b where a.table_name = b.table_name);
2 rows updated.

我个人更喜欢 MERGE 声明,因为我觉得它更清晰:

MERGE INTO test1 USING mapping 
   ON (test1.table_name = mapping.table_name)
 WHEN MATCHED THEN UPDATE SET 
      test1.daily_value = mapping.daily_value; 

【讨论】:

  • 谢谢你们。但是,数据集是非常特殊的情况。我必须在 test1 中使用 'first' 保持相同的值。
【解决方案3】:

一个选项是从(daily_value) 中选择MINMAX

UPDATE test1 a
SET
    daily_value = (
        SELECT
            MIN(daily_value)
        FROM
            mapping b
        WHERE
            a.table_name = b.table_name
    );

但是,这会让你在 test1 中的数据像这样

TABLE_NAME  DAILY_VALUE
first   value_1
first   value_1

如果您不希望这样,您应该添加另一个在两个表之间关联的唯一列,而不是table_name

Demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-30
    • 1970-01-01
    • 1970-01-01
    • 2012-08-17
    • 2015-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多