我有一个表,其中一列的逗号分隔值, (例如:经度,纬度,经度1,纬度1等)..
现在我需要交换像(纬度,经度,纬度1,经度1等)的值。
至于试验目的: 我创建了一个表格如下:
CREATE TABLE string_table
(
slno NUMBER,
old_string VARCHAR2(50),
new_string VARCHAR2(50)
);
/
INSERT INTO STRING_TABLE (SLNO, OLD_STRING)
VALUES (1, '1,2,3,4,5,6');
INSERT INTO STRING_TABLE (SLNO, OLD_STRING)
VALUES (2, '1,2,3,4,5');
INSERT INTO STRING_TABLE (SLNO, OLD_STRING)
VALUES (3, 'a,b,c,d,e,f');
INSERT INTO STRING_TABLE (SLNO, OLD_STRING)
VALUES (4, 'a,b,c,d,e');
COMMIT;
/
现在表格如下:
slno old_string new_string
----- ----------------------
1 1,2,3,4,5,6
2 1,2,3,4,5
3 a,b,c,d,e,f
4 a,b,c,d,e
我需要将交换的值更新为new_string列,结果应如下所示:
slno old_string new_string
----- ----------------------
1 1,2,3,4,5,6 2,1,4,3,6,5
2 1,2,3,4,5 2,1,4,3,5
3 a,b,c,d,e,f b,a,d,c,f,e
4 a,b,c,d,e b,a,d,c,e
到目前为止我所做的是使用如下所示的使用COLLECTION的PL / SQL代码,并且工作正常:
SET serveroutput ON
DECLARE
TYPE my_type IS TABLE OF VARCHAR2(50);
my_obj my_type := my_type();
l_temp_var VARCHAR2(50);
l_string VARCHAR2(200);
BEGIN
FOR i IN
( SELECT slno, old_string FROM string_table
)
loop
FOR j IN
(SELECT regexp_substr(i.old_string,'[^,]+',1, LEVEL) val
FROM dual
CONNECT BY regexp_substr(i.old_string, '[^,]+', 1, LEVEL) IS NOT NULL
)
loop
my_obj.EXTEND;
my_obj(my_obj.LAST) := j.val;
IF mod(my_obj.count,2)= 0 THEN
l_temp_var := my_obj(my_obj.LAST -1);
my_obj(my_obj.LAST-1) := my_obj(my_obj.LAST) ;
my_obj(my_obj.LAST):= l_temp_var;
END IF;
END LOOP;
FOR i IN my_obj.FIRST..my_obj.LAST
loop
l_string := l_string||my_obj(i)||',';
END loop;
l_string := substr(l_string , 1, length(l_string)-1);
update string_table
SET new_string = l_string
WHERE slno = i.slno;
l_string := NULL;
my_obj := my_type();
END loop;
COMMIT;
END;
/
我认为这个解决方案非常冗长,是否还有其他好的/简单/简单的方法来交换预期结果的值?
提前致谢;)
答案 0 :(得分:5)
您可以使用connect by
语法将逗号分隔列表拆分为单独的元素,并以不同的顺序将它们重新组合在一起,所有这些都使用纯SQL。两个稍微棘手的位是交换对,这可以通过将每个位置向上或向下调整一个来完成,具体取决于它是奇数还是偶数;并将此语法同时应用于多行数据,这可以通过使用确定性函数的技巧来完成:
select slno, old_string,
listagg(item, ',') within group (order by new_pos) as new_string
from (
select slno, old_string, regexp_substr(old_string, '[^,]+', 1, level) as item,
case when mod(level, 2) = 1 then level + 1
else level - 1 end as new_pos
from string_table
connect by level <= regexp_count(old_string, '[^,]+')
and prior slno = slno
and prior sys_guid() is not null
)
group by slno, old_string;
SLNO OLD_STRING NEW_STRING
---------- -------------------- --------------------
1 1,2,3,4,5,6 2,1,4,3,6,5
2 1,2,3,4,5 2,1,4,3,5
3 a,b,c,d,e,f b,a,d,c,f,e
4 a,b,c,d,e b,a,d,c,e
然后,您可以将其用作using
的{{1}}子句来更新原始表:
merge
SQL Fiddle包括内部查询产生的内容。
如果你需要更普遍地使用它,你可以改为创建一个函数:
merge into string_table st
using (
select slno, old_string,
listagg(item, ',') within group (order by new_pos) as new_string
from (
select slno, old_string,
regexp_substr(old_string, '[^,]+', 1, level) as item,
case when mod(level, 2) = 1 then level + 1
else level - 1 end as new_pos
from string_table
connect by level <= regexp_count(old_string, '[^,]+')
and prior slno = slno
and prior sys_guid() is not null
)
group by slno, old_string
) tmp
on (tmp.slno = st.slno)
when matched then
update set st.new_string = tmp.new_string;
select * from string_table order by slno;
SLNO OLD_STRING NEW_STRING
---------- -------------------- --------------------
1 1,2,3,4,5,6 2,1,4,3,6,5
2 1,2,3,4,5 2,1,4,3,5
3 a,b,c,d,e,f b,a,d,c,f,e
4 a,b,c,d,e b,a,d,c,e
当然,首先在列中存储以逗号分隔的值不是一个好主意;如果您有多对,则每个值应该是子表中的自己的列。如果您要添加新列,我真的会认真考虑改进数据模型。有时候你会被你所拥有的东西困住,即使你可以将数据分开,这种技术也可以用来进行一次性的练习。
答案 1 :(得分:3)
不,没有。这就是规范化如此重要的原因,如果你有一个看起来像这样的表,那么你可以按照你想要的输出聚合字符串:
create table string_table (
slno number
, position number
, string varchar2(50)
);
然而,虽然没有简短或简单的方法,但这是一个更容易理解的方法。您想要输出的数据的顺序由以下语句描述。这里的重点(以及与你的主要区别)是ORDER BY。 MOD(LEVEL, 2)
为偶数级别返回0,为奇数级别返回1。通过向此添加LEVEL,您最终会得到偶数所描述的每个连续对,即1&amp; 2(或a和b)将是2和3&amp; 4将是4.然后,LEVEL再次排序给我们最高的第一个,交换每对。为方便起见,我包括了这些列中的每一列。
SQL> select regexp_substr('1,2,3,4,5,6', '[^,]+', 1, level) as str
2 , mod(level, 2) as ml
3 , level + mod(level, 2) as lml
4 , level as l
5 from dual
6 connect by regexp_substr('1,2,3,4,5,6', '[^,]+', 1, level) is not null
7 order by level + mod(level, 2), level desc;
STR ML LML L
--- ---- ---- ----
2 0 2 2
1 1 2 1
4 0 4 4
3 1 4 3
6 0 6 6
5 1 6 5
6 rows selected.
然后,您可以使用LISTAGG()
重新聚合。这包括一个ORDER BY子句,因此您不必在子选择中具有显式ORDER BY。
SQL> select listagg(str, ',') within group (order by lvl + mod(lvl, 2), lvl desc)
2 from ( select regexp_substr('1,2,3,4,5,6', '[^,]+', 1, level) as str, level as lvl
3 from dual
4 connect by regexp_substr('1,2,3,4,5,6', '[^,]+', 1, level) is not null
5 );
LISTAGG(STR,',')WITHINGROUP(ORDERBYLVL+MOD(LVL,2),LVLDESC)
--------------------------------------------------------------------------------
2,1,4,3,6,5
SQL>
如果我们将所有这些都放在一个函数中(这样你就不对每个列表中的最大项目数执行分层查询,而是对每个列表执行正确的数量),那么每个输入字符串会得到一个返回值:
create or replace function reverse_string (PInput in varchar2) return varchar2 is
/* Reverse each pair of items in a comma delimited list
*/
l_output string_table.old_string%type;
begin
select listagg(str, ',') within group (order by lvl + mod(lvl, 2), lvl desc)
into l_output
from ( select regexp_substr(PInput, '[^,]+', 1, level) as str, level as lvl
from dual
connect by regexp_substr(PInput, '[^,]+', 1, level) is not null
);
return l_output;
end reverse_string;
这可以通过一个简单的SELECT语句来证明:
SQL> select slno, old_string, reverse_string(old_string) as new_string
2 from string_table;
SLNO OLD_STRING NEW_STRING
---------- --------------- ---------------
1 1,2,3,4,5,6 2,1,4,3,6,5
2 1,2,3,4,5 2,1,4,3,5
3 a,b,c,d,e,f b,a,d,c,f,e
4 a,b,c,d,e b,a,d,c,e
最后,这意味着UPDATE足以更新您的表。这意味着您可以在单个事务中执行它而无需循环等。
SQL> update string_table
2 set new_string = reverse_string(old_string);
4 rows updated.
SQL>
SQL> select *
2 from string_table;
SLNO OLD_STRING NEW_STRING
---------- --------------- ---------------
1 1,2,3,4,5,6 2,1,4,3,6,5
2 1,2,3,4,5 2,1,4,3,5
3 a,b,c,d,e,f b,a,d,c,f,e
4 a,b,c,d,e b,a,d,c,e
答案 2 :(得分:1)
仅使用regexp_replace,
with string_table(slno, old_string)
as (
select 1, '1,2,3,4,5,6' from dual union all
select 2, '1,2,3,4,5' from dual union all
select 3, 'a,b,c,d,e,f' from dual union all
select 4, 'a,b,c,d,e' from dual
)
select
slno,
old_string,
regexp_replace(old_string,'([^,]+),([^,]+)','\2,\1') new_string
from
string_table;
SLNO OLD_STRING NEW_STRING
---------- ----------- ------------------------------------------------------------
1 1,2,3,4,5,6 2,1,4,3,6,5
2 1,2,3,4,5 2,1,4,3,5
3 a,b,c,d,e,f b,a,d,c,f,e
4 a,b,c,d,e b,a,d,c,e
模式:
([^,]+) -- any string without a comma. Enclosed in brackets to form first capture group.
, -- a comma
([^,]+) -- any string without a comma. Enclosed in brackets to form second capture group.
因此,此模式匹配由逗号分隔的两个字符串。
Replace_String:
\2 -- the second capture group from the Pattern
, -- a comma
\1 -- the first capture group from the Pattern
因此,这会将匹配的模式替换为相同的字符串,但会改变位置。