更新一行以基于另一行的转换 (Oracle)

Posted

技术标签:

【中文标题】更新一行以基于另一行的转换 (Oracle)【英文标题】:Updating a row to be based on a transformation of another row (Oracle) 【发布时间】:2019-07-04 10:53:38 【问题描述】:

我有一张有几行 (~100) 行的表格。其中一列基本上是以逗号分隔的字符串列表,可能包含也可能不包含空格。现在我需要另一列是该列的转换:删除空格,用逗号分隔,按字母顺序排序并再次用逗号连接。

我有以下适用于一行的 SQL(所有其他行都获得相同的值),但我不知道如何让它在整个表上工作。我可能错过了一个地方,但似乎无法正确:

update client set sorted = (select listagg(somedata, ',') 
    within group (order by somedata) somedata from (
    with q as (select ','||regexp_replace(unsorted, '\s+', '') as somedata 
    from client)
select regexp_substr(somedata, ',([^,]+)',1,rownum,'i',1) somedata
from q,
(select 1 from q connect by level <= length(regexp_replace(somedata, '[^,]', '')))
));

我错过了什么? 谢谢。

【问题讨论】:

这是一个糟糕的方法。当存储逗号分隔值本身是一种不好的做法时,与其拥有另一列存储具有排序值的基本相同的东西是没有意义的。相反,您必须考虑通过将单个值存储为行来将它们转换为另一个表,可能与父表中的相同键 我知道,但是当我们最初开发应用程序时,我们必须将其视为单个字符串,逗号分隔对我们来说毫无意义。不过现在情况发生了变化,但切换整个结构是不可行的。 正如我经常告诉 OP 此类问题的那样,现在 或永远不会。 ..或者设计会在未来的岁月里受到伤害。 虽然您可能是正确的,但我需要在一列中排序字符串,因为我确实需要基于排序的列运行相当快的选择。 【参考方案1】:

使用相关子查询:

Oracle 设置

CREATE TABLE client ( unsorted, sorted ) AS
  SELECT 'b , c  ,e f, d, a', CAST( NULL AS VARCHAR2(30) ) FROM DUAL UNION ALL
  SELECT 'z , y, x,w,v ,u', CAST( NULL AS VARCHAR2(30) ) FROM DUAL;

更新

UPDATE client c
SET sorted = (
  SELECT LISTAGG(
           REGEXP_REPLACE( REGEXP_SUBSTR( c.unsorted, '[^,]+', 1, LEVEL ), '\s+' ),
           ','
         ) WITHIN GROUP (
           ORDER BY REGEXP_REPLACE( REGEXP_SUBSTR( c.unsorted, '[^,]+', 1, LEVEL ), '\s+' )
         )
  FROM   DUAL
  CONNECT BY LEVEL <= REGEXP_COUNT( c.unsorted, ',' ) + 1
)

输出

SELECT * FROM client;
未分类 |已排序 :---------------- | :---------- b, c, e f, d, a | a,b,c,d,ef z , y, x,w,v ,u | u,v,w,x,y,z

db小提琴here

【讨论】:

以上是关于更新一行以基于另一行的转换 (Oracle)的主要内容,如果未能解决你的问题,请参考以下文章

查询返回:ORA-01427 单行子查询返回多于一行

Oracle:Sql根据同一表另一行的值更新同一表的行

oracle更新一行数据,另一个会话读取是为啥会产生2个CR块?

Oracle SQL:ORA-01427:单行子查询返回多于一行

具有组功能的 Oracle 更新语句

Oracle APEX 交互式报告中的错误 - ORA-01427:单行子查询返回多于一行