【问题标题】:How to update a column using select in Oracle如何在 Oracle 中使用 select 更新列
【发布时间】:2019-06-27 16:32:38
【问题描述】:

我需要根据名称和出生日期更新表中的列 ID2 值。我创建了一个 oracle 序列并决定将值填充到列 ID2 中,但我无法将逻辑组合到更新查询中。请看我下面的代码,我打算按名称、DOB 进行分组并更新 ID2,但我被困在逻辑的中间。如果您在 sql 或 plsql 中有任何解决方案,那么它可以工作!谢谢。

CREATE SEQUENCE seq
  MINVALUE 1
  START WITH 100
  INCREMENT BY 1;


UPDATE table1 SET ID2 = seq.nextval
where Name= ---NOT SURE WHAT TO DO ?
select count(*) from table1
group by NAME,DOB;

【问题讨论】:

  • 您打算将序列用于未来的插入,还是仅用于此一次性更新?如果将来,您将如何处理 .1/.2/.3 部分? (为什么首先要重复名称/ DOB?)另外,ID2 值的顺序应该与 ID1 值顺序匹配,还是应该独立并由其他东西排序?
  • 您好,这只是一次处理。为什么会有重复的name和dob?那是数据的结构。 ID2 可以是任何顺序,不需要与 ID1 匹配

标签: sql oracle sql-update


【解决方案1】:

假设您希望 ID2 值与 ID1 值的顺序相同,并且没有重复的 ID1 值,您可以通过使用带有合适窗口子句的分析函数来执行此操作而无需序列:

select name, dob, id1,
  100 + dense_rank() over (order by trunc(id1))
      + dense_rank() over (partition by trunc(id1) order by id1)/10
      as id2
from table1;

NAME  DOB               ID1        ID2
----- ---------- ---------- ----------
JIM   1991-11-30       23.1      101.1
JIM   1991-11-30       23.2      101.2
JIM   1991-11-30       23.3      101.3
TOM   1993-12-30       30.1      102.1
TOM   1993-12-30       30.2      102.2
HENRY 1994-12-03       34.1      103.1
HENRY 1994-12-03       34.2      103.2

7 rows selected. 

然后您可以将该生成的表用作合并语句的一部分:

merge into table1
using (
  select name, dob, id1,
    100 + dense_rank() over (order by trunc(id1))
        + dense_rank() over (partition by trunc(id1) order by id1)/10
        as id2
  from table1
) tmp on (tmp.id1 = table1.id1)
when matched then
update set table1.id2 = tmp.id2;

7 rows merged.

select * from table1;

NAME  DOB               ID1        ID2
----- ---------- ---------- ----------
JIM   1991-11-30       23.1      101.1
JIM   1991-11-30       23.2      101.2
JIM   1991-11-30       23.3      101.3
TOM   1993-12-30       30.1      102.1
TOM   1993-12-30       30.2      102.2
HENRY 1994-12-03       34.1      103.1
HENRY 1994-12-03       34.2      103.2

7 rows selected. 

db<>fiddle

如果 ID2 不需要与 ID1 相关,那么您可以随意订购:

merge into table1
using (
  select name, dob, id1,
    100 + dense_rank() over (order by name, dob)
        + dense_rank() over (partition by name, dob order by id1)/10
        as id2
  from table1
) tmp on (tmp.id1 = table1.id1)
when matched then
update set table1.id2 = tmp.id2;

select * from table1;

NAME  DOB               ID1        ID2
----- ---------- ---------- ----------
JIM   1991-11-30       23.1      102.1
JIM   1991-11-30       23.2      102.2
JIM   1991-11-30       23.3      102.3
TOM   1993-12-30       30.1      103.1
TOM   1993-12-30       30.2      103.2
HENRY 1994-12-03       34.1      101.1
HENRY 1994-12-03       34.2      101.2

这只有在小数部分不超过 0.9 时才会按原样工作;但是如果确实如此,则很难解释这些值(因为 23.10 与 23.1 相同)。

我还假设这是一次性更新,您不打算在以后的插入中使用该序列;目前尚不清楚您将如何管理它 - 如果名称/DOB 尚不存在,您只想获取下一个序列值,如果存在,您将需要找到最高的现有 ID 并将 0.1 添加到它.无论哪种方式,您都必须序列化插入以防止冲突或差异。

它实际上进入了 ID1 值为 23.10、23.11 而 ID2 将它们显示为 101.1、101.1 的情况。 ...我尝试将其除以 100,并且值 >= .11 小数位的问题已解决,但对于 .10 和 .20,它仍显示为 .1 和 .2。

这表明两个 ID 值都是字符串而不是数字。如果是这样,您仍然可以使用排名函数,但将生成的两个数字视为字符串并将它们连接在一起:

    merge into table1
    using (
      select name, dob, id1,
        to_char(100 + dense_rank() over (order by name, dob))
            ||'.'||
            dense_rank() over (partition by name, dob
              order by to_number(substr(id1, instr(id1, '.') + 1)))
            as id2
      from table1
    ) tmp on (tmp.id1 = table1.id1)
    when matched then
    update set table1.id2 = tmp.id2;

With some additional base data that gives you:

select * from table1;

NAME  DOB        ID1        ID2       
----- ---------- ---------- ----------
JIM   1991-11-30 23.1       103.1     
JIM   1991-11-30 23.2       103.2     
JIM   1991-11-30 23.3       103.3     
TOM   1993-12-30 30.1       104.1     
TOM   1993-12-30 30.3       104.2     
HENRY 1993-12-30 34.1       101.1     
HENRY 1994-12-03 34.5       102.1     
HENRY 1994-12-03 34.6       102.2     
HENRY 1994-12-03 34.7       102.3     
HENRY 1994-12-03 34.8       102.4     
HENRY 1994-12-03 34.9       102.5     
HENRY 1994-12-03 34.10      102.6     
HENRY 1994-12-03 34.11      102.7     
HENRY 1994-12-03 34.12      102.8     
HENRY 1994-12-03 34.13      102.9     
HENRY 1994-12-03 34.14      102.10    
HENRY 1994-12-03 34.15      102.11    
HENRY 1994-12-03 34.16      102.12    

db<>fiddle

当然,这样做会使将 ID2 值视为数字或以有意义的方式对其进行排序变得非常尴尬;但是对于您的 ID1 值,情况必须已经如此。另一种方法可能是将第一部分乘以一个大数,比如 1000,然后加上第二部分 - 所以亨利最终得到 1020001 到 1010012。

【讨论】:

  • 感谢 Alex 提供上述解决方案,但在我的情况下,ID1 的顺序与 ID2 不匹配。在这种情况下你建议我怎么做?
  • 在您的示例中,它们似乎匹配;但您可以在任一/两个 dense_rank() 调用中更改顺序。我已经添加了一个先按姓名/出生日期排序的示例。如果您有关于如何订购它们的规则,并且无法调整此解决方案以匹配您的规则,那么您需要解释它。
  • 哇,这是一个了不起的解决方案,但是是的,它实际上进入了 ID1 值为 23.10、23.11 而 ID2 将它们显示为 101.1、101.1 的情况。在那种情况下,你建议我怎么做?
  • 我尝试将其除以 100 并解决了值 >= .11 小数位的问题,但对于 0.10 和 .20,它仍显示为 0.1 和 0.2。不过,我对解决方案很满意 :)
  • 所以它们是字符串而不是数字? ID2 也会是字符串吗?您可以在句点之后附加 ID1 字符串的一部分,而不是计算新数字。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多