SYS_CONNECT_BY_PATH 与 CLOB
SYS_CONNECT_BY_PATH with CLOB
我有一个 ORA-01489:字符串连接的结果太长 在 Oracle 数据库 11g 企业版版本 11.2.0.4.0 - 64 位生产上执行此查询时出错, PL/SQL 版本 11.2.0.4.0 - 生产,CORE 11.2.0.4.0 生产,Linux 的 TNS:版本 11.2.0.4.0 - 生产,NLSRTL 版本 11.2.0.4.0 - 生产:
SELECT "USER_PRIMARY_UNIT","LOGIN","FIRST_NAME","LAST_NAME","UNIT_ROLE"
FROM (
SELECT user_primary_unit,login, first_name, last_name,
LTRIM(MAX(SYS_CONNECT_BY_PATH(rights,' / '))
KEEP (DENSE_RANK LAST ORDER BY curr),' / ') AS UNIT_ROLE
FROM
(SELECT login,
first_name,
last_name,
user_primary_unit,
rights,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) AS curr,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) -1 AS prev
FROM (select member0_.login, member0_.first_name first_name, unit2.unit_name user_primary_unit, member0_.last_name last_name,
CONCAT(CONCAT(unit.unit_name, ' - '), role3_.role_name) rights
from
IOT_DEVICES.t_member member0_
inner join IOT_DEVICES.t_user member0_1_ on member0_.member_id=member0_1_.user_id
inner join IOT_DEVICES.t_playable_role playedrole1_ on member0_.member_id=playedrole1_.user_id
inner join IOT_DEVICES.t_unit_role unitrole2_ on playedrole1_.unit_role_id=unitrole2_.unit_role_id
inner join IOT_DEVICES.t_role role3_ on unitrole2_.role_id=role3_.role_id
inner join IOT_DEVICES.t_unit unit on unitrole2_.unit_id=unit.unit_id
inner join IOT_DEVICES.t_unit unit2 on unit2.unit_id=member0_1_.primary_unit_id
where current_date between playedrole1_.start_date and playedrole1_.end_date
order by unit.unit_name
))
GROUP BY login, first_name, last_name, user_primary_unit
CONNECT BY prev = PRIOR curr AND login = PRIOR login
START WITH curr = 1
)
ORDER BY user_PRIMARY_UNIT, FIRST_NAME, LAST_NAME;
此查询的问题在于使用了 CONCAT 运算符 (||)。 Concat 运算符 returns char1 与 char2 连接。字符串 returned 与 char1 在同一字符集中。所以这里的 concat 运算符试图 return varchar2,它有 4000 个字符的限制并且已经超过了。当我们尝试使用 CLOB 连接 VARCHAR2 时,也可能会出现此问题。所以在这里我只想将它的第一个字符串转换为 CLOB 并避免这个错误。将第一个字符串转换为 CLOB 后,CONCAT 运算符将 return CLOB 类型的字符串
所以我添加了 TO_CLOB 来转换类型,但是我遇到了下一个错误:
ORA-00932:数据类型不一致:预期 - 得到 CLOB
SELECT "USER_PRIMARY_UNIT","LOGIN","FIRST_NAME","LAST_NAME","UNIT_ROLE"
FROM (
SELECT user_primary_unit,login, first_name, last_name,
LTRIM(MAX(SYS_CONNECT_BY_PATH(rights,' / '))
KEEP (DENSE_RANK LAST ORDER BY curr),' / ') AS UNIT_ROLE
FROM
(SELECT login,
first_name,
last_name,
user_primary_unit,
rights,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) AS curr,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) -1 AS prev
FROM (select member0_.login, member0_.first_name first_name, unit2.unit_name user_primary_unit, member0_.last_name last_name,
TO_CLOB(CONCAT(CONCAT(unit.unit_name, ' - '), role3_.role_name)) rights
from
IOT_DEVICES.t_member member0_
inner join IOT_DEVICES.t_user member0_1_ on member0_.member_id=member0_1_.user_id
inner join IOT_DEVICES.t_playable_role playedrole1_ on member0_.member_id=playedrole1_.user_id
inner join IOT_DEVICES.t_unit_role unitrole2_ on playedrole1_.unit_role_id=unitrole2_.unit_role_id
inner join IOT_DEVICES.t_role role3_ on unitrole2_.role_id=role3_.role_id
inner join IOT_DEVICES.t_unit unit on unitrole2_.unit_id=unit.unit_id
inner join IOT_DEVICES.t_unit unit2 on unit2.unit_id=member0_1_.primary_unit_id
where current_date between playedrole1_.start_date and playedrole1_.end_date
order by unit.unit_name
))
GROUP BY login, first_name, last_name, user_primary_unit
CONNECT BY prev = PRIOR curr AND login = PRIOR login
START WITH curr = 1
)
ORDER BY user_PRIMARY_UNIT, FIRST_NAME, LAST_NAME;
我还尝试使用此处定义的包层次结构,但后来我得到了一个 ORA-00932:不一致的数据类型:预期 - 得到了 CLOB
https://community.oracle.com/thread/965324?start=0&tstart=0
SELECT "USER_PRIMARY_UNIT","LOGIN","FIRST_NAME","LAST_NAME","UNIT_ROLE"
FROM (
SELECT user_primary_unit,login, first_name, last_name,
LTRIM(MAX(hierarchy.branch(level,rights,' / '))
KEEP (DENSE_RANK LAST ORDER BY curr),' / ') AS UNIT_ROLE
FROM
(SELECT login,
first_name,
last_name,
user_primary_unit,
rights,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) AS curr,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) -1 AS prev
FROM (select member0_.login, member0_.first_name first_name, unit2.unit_name user_primary_unit, member0_.last_name last_name,
TO_CLOB(CONCAT(CONCAT(unit.unit_name, ' - '), role3_.role_name)) rights
from
IOT_DEVICES.t_member member0_
inner join IOT_DEVICES.t_user member0_1_ on member0_.member_id=member0_1_.user_id
inner join IOT_DEVICES.t_playable_role playedrole1_ on member0_.member_id=playedrole1_.user_id
inner join IOT_DEVICES.t_unit_role unitrole2_ on playedrole1_.unit_role_id=unitrole2_.unit_role_id
inner join IOT_DEVICES.t_role role3_ on unitrole2_.role_id=role3_.role_id
inner join IOT_DEVICES.t_unit unit on unitrole2_.unit_id=unit.unit_id
inner join IOT_DEVICES.t_unit unit2 on unit2.unit_id=member0_1_.primary_unit_id
where current_date between playedrole1_.start_date and playedrole1_.end_date
order by unit.unit_name
))
GROUP BY login, first_name, last_name, user_primary_unit
CONNECT BY prev = PRIOR curr AND login = PRIOR login
START WITH curr = 1
)
ORDER BY user_PRIMARY_UNIT, FIRST_NAME, LAST_NAME;
然后我也尝试使用 sys.stragg ,但是我得到了一个 ORA-00978: nested group function without GROUP BY
SELECT "USER_PRIMARY_UNIT","LOGIN","FIRST_NAME","LAST_NAME","UNIT_ROLE"
FROM (
SELECT user_primary_unit,login, first_name, last_name,
LTRIM(MAX(SYS_CONNECT_BY_PATH(rights,' / '))
KEEP (DENSE_RANK LAST ORDER BY curr),' / ') AS UNIT_ROLE
FROM
(SELECT login,
first_name,
last_name,
user_primary_unit,
rights,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) AS curr,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) -1 AS prev
FROM (select member0_.login, member0_.first_name first_name, unit2.unit_name user_primary_unit, member0_.last_name last_name,
sys.stragg(sys.stragg(unit.unit_name || ' - ' || role3_.role_name)) rights
from
IOT_DEVICES.t_member member0_
inner join IOT_DEVICES.t_user member0_1_ on member0_.member_id=member0_1_.user_id
inner join IOT_DEVICES.t_playable_role playedrole1_ on member0_.member_id=playedrole1_.user_id
inner join IOT_DEVICES.t_unit_role unitrole2_ on playedrole1_.unit_role_id=unitrole2_.unit_role_id
inner join IOT_DEVICES.t_role role3_ on unitrole2_.role_id=role3_.role_id
inner join IOT_DEVICES.t_unit unit on unitrole2_.unit_id=unit.unit_id
inner join IOT_DEVICES.t_unit unit2 on unit2.unit_id=member0_1_.primary_unit_id
where current_date between playedrole1_.start_date and playedrole1_.end_date
order by unit.unit_name
))
GROUP BY login, first_name, last_name, user_primary_unit
CONNECT BY prev = PRIOR curr AND login = PRIOR login
START WITH curr = 1
)
ORDER BY user_PRIMARY_UNIT, FIRST_NAME, LAST_NAME;
您可以使用 subquery factoring syntax 构建层次结构 CLOB
路径,这可能 运行 真的很慢。考虑有两个路径列 - 一个用于 varchar2
结果,一个用于 CLOB
。构建 varchar2
直到大小允许,并在 CLOB
路径中保留 NULL
,当超出 varchar2
容量时切换到 CLOB
。不过,这是一个不同的问题。
with
base as (
select
level as id,
case when level > 1 then level - 1 end as parent_id,
dbms_random.string('X', 2000) as val
from dual
connect by level <= 50
),
hier(id, parent_id, val, path) as (
select
b.id,
b.parent_id,
b.val,
to_clob(concat('/', b.val)) as path
from base b
where b.parent_id is null
union all
select
b.id,
b.parent_id,
b.val,
concat(h.path, to_clob(' / '||b.val) )
from base b
join hier h on h.id = b.parent_id
)
select rownum, length(h.path)
from hier h;
ROWNUM LENGTH(H.PATH)
1 2001
2 4004
3 6007
4 8010
5 10013
6 12016
7 14019
8 16022
9 18025
10 20028
11 22031
12 24034
13 26037
14 28040
15 30043
16 32046
17 34049
18 36052
19 38055
20 40058
21 42061
22 44064
23 46067
24 48070
25 50073
26 52076
27 54079
28 56082
29 58085
30 60088
31 62091
32 64094
33 66097
34 68100
35 70103
36 72106
37 74109
38 76112
39 78115
40 80118
41 82121
42 84124
43 86127
44 88130
45 90133
46 92136
47 94139
48 96142
49 98145
50 100148
您可能会找到此页面,因为您正在尝试聚合超过 4000 个字符的字符串并记住 the different techniques。
如果是这样,我根据@B Samedi 的回答创建了一个小示例来帮助您解决问题,当您无法使用 user defined aggregates
with dummy_text as (
select 'teststring ' || rownum str from dual connect by rownum < 2
)
, indexed_strings as (
select str, row_number() over (order by 'x') rn, ',' separator from dummy_text
)
, hier (str, lvl) as (
select to_clob(i.str), rn from indexed_strings i where rn = (select max(rn) from indexed_strings)
union all
select concat(to_clob(concat(i.str, i.separator)), h.str), h.lvl - 1 from indexed_strings i join hier h on h.lvl - 1 = i.rn
)
select str from hier where lvl = 1
我有一个 ORA-01489:字符串连接的结果太长 在 Oracle 数据库 11g 企业版版本 11.2.0.4.0 - 64 位生产上执行此查询时出错, PL/SQL 版本 11.2.0.4.0 - 生产,CORE 11.2.0.4.0 生产,Linux 的 TNS:版本 11.2.0.4.0 - 生产,NLSRTL 版本 11.2.0.4.0 - 生产:
SELECT "USER_PRIMARY_UNIT","LOGIN","FIRST_NAME","LAST_NAME","UNIT_ROLE"
FROM (
SELECT user_primary_unit,login, first_name, last_name,
LTRIM(MAX(SYS_CONNECT_BY_PATH(rights,' / '))
KEEP (DENSE_RANK LAST ORDER BY curr),' / ') AS UNIT_ROLE
FROM
(SELECT login,
first_name,
last_name,
user_primary_unit,
rights,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) AS curr,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) -1 AS prev
FROM (select member0_.login, member0_.first_name first_name, unit2.unit_name user_primary_unit, member0_.last_name last_name,
CONCAT(CONCAT(unit.unit_name, ' - '), role3_.role_name) rights
from
IOT_DEVICES.t_member member0_
inner join IOT_DEVICES.t_user member0_1_ on member0_.member_id=member0_1_.user_id
inner join IOT_DEVICES.t_playable_role playedrole1_ on member0_.member_id=playedrole1_.user_id
inner join IOT_DEVICES.t_unit_role unitrole2_ on playedrole1_.unit_role_id=unitrole2_.unit_role_id
inner join IOT_DEVICES.t_role role3_ on unitrole2_.role_id=role3_.role_id
inner join IOT_DEVICES.t_unit unit on unitrole2_.unit_id=unit.unit_id
inner join IOT_DEVICES.t_unit unit2 on unit2.unit_id=member0_1_.primary_unit_id
where current_date between playedrole1_.start_date and playedrole1_.end_date
order by unit.unit_name
))
GROUP BY login, first_name, last_name, user_primary_unit
CONNECT BY prev = PRIOR curr AND login = PRIOR login
START WITH curr = 1
)
ORDER BY user_PRIMARY_UNIT, FIRST_NAME, LAST_NAME;
此查询的问题在于使用了 CONCAT 运算符 (||)。 Concat 运算符 returns char1 与 char2 连接。字符串 returned 与 char1 在同一字符集中。所以这里的 concat 运算符试图 return varchar2,它有 4000 个字符的限制并且已经超过了。当我们尝试使用 CLOB 连接 VARCHAR2 时,也可能会出现此问题。所以在这里我只想将它的第一个字符串转换为 CLOB 并避免这个错误。将第一个字符串转换为 CLOB 后,CONCAT 运算符将 return CLOB 类型的字符串
所以我添加了 TO_CLOB 来转换类型,但是我遇到了下一个错误:
ORA-00932:数据类型不一致:预期 - 得到 CLOB
SELECT "USER_PRIMARY_UNIT","LOGIN","FIRST_NAME","LAST_NAME","UNIT_ROLE"
FROM (
SELECT user_primary_unit,login, first_name, last_name,
LTRIM(MAX(SYS_CONNECT_BY_PATH(rights,' / '))
KEEP (DENSE_RANK LAST ORDER BY curr),' / ') AS UNIT_ROLE
FROM
(SELECT login,
first_name,
last_name,
user_primary_unit,
rights,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) AS curr,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) -1 AS prev
FROM (select member0_.login, member0_.first_name first_name, unit2.unit_name user_primary_unit, member0_.last_name last_name,
TO_CLOB(CONCAT(CONCAT(unit.unit_name, ' - '), role3_.role_name)) rights
from
IOT_DEVICES.t_member member0_
inner join IOT_DEVICES.t_user member0_1_ on member0_.member_id=member0_1_.user_id
inner join IOT_DEVICES.t_playable_role playedrole1_ on member0_.member_id=playedrole1_.user_id
inner join IOT_DEVICES.t_unit_role unitrole2_ on playedrole1_.unit_role_id=unitrole2_.unit_role_id
inner join IOT_DEVICES.t_role role3_ on unitrole2_.role_id=role3_.role_id
inner join IOT_DEVICES.t_unit unit on unitrole2_.unit_id=unit.unit_id
inner join IOT_DEVICES.t_unit unit2 on unit2.unit_id=member0_1_.primary_unit_id
where current_date between playedrole1_.start_date and playedrole1_.end_date
order by unit.unit_name
))
GROUP BY login, first_name, last_name, user_primary_unit
CONNECT BY prev = PRIOR curr AND login = PRIOR login
START WITH curr = 1
)
ORDER BY user_PRIMARY_UNIT, FIRST_NAME, LAST_NAME;
我还尝试使用此处定义的包层次结构,但后来我得到了一个 ORA-00932:不一致的数据类型:预期 - 得到了 CLOB https://community.oracle.com/thread/965324?start=0&tstart=0
SELECT "USER_PRIMARY_UNIT","LOGIN","FIRST_NAME","LAST_NAME","UNIT_ROLE"
FROM (
SELECT user_primary_unit,login, first_name, last_name,
LTRIM(MAX(hierarchy.branch(level,rights,' / '))
KEEP (DENSE_RANK LAST ORDER BY curr),' / ') AS UNIT_ROLE
FROM
(SELECT login,
first_name,
last_name,
user_primary_unit,
rights,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) AS curr,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) -1 AS prev
FROM (select member0_.login, member0_.first_name first_name, unit2.unit_name user_primary_unit, member0_.last_name last_name,
TO_CLOB(CONCAT(CONCAT(unit.unit_name, ' - '), role3_.role_name)) rights
from
IOT_DEVICES.t_member member0_
inner join IOT_DEVICES.t_user member0_1_ on member0_.member_id=member0_1_.user_id
inner join IOT_DEVICES.t_playable_role playedrole1_ on member0_.member_id=playedrole1_.user_id
inner join IOT_DEVICES.t_unit_role unitrole2_ on playedrole1_.unit_role_id=unitrole2_.unit_role_id
inner join IOT_DEVICES.t_role role3_ on unitrole2_.role_id=role3_.role_id
inner join IOT_DEVICES.t_unit unit on unitrole2_.unit_id=unit.unit_id
inner join IOT_DEVICES.t_unit unit2 on unit2.unit_id=member0_1_.primary_unit_id
where current_date between playedrole1_.start_date and playedrole1_.end_date
order by unit.unit_name
))
GROUP BY login, first_name, last_name, user_primary_unit
CONNECT BY prev = PRIOR curr AND login = PRIOR login
START WITH curr = 1
)
ORDER BY user_PRIMARY_UNIT, FIRST_NAME, LAST_NAME;
然后我也尝试使用 sys.stragg ,但是我得到了一个 ORA-00978: nested group function without GROUP BY
SELECT "USER_PRIMARY_UNIT","LOGIN","FIRST_NAME","LAST_NAME","UNIT_ROLE"
FROM (
SELECT user_primary_unit,login, first_name, last_name,
LTRIM(MAX(SYS_CONNECT_BY_PATH(rights,' / '))
KEEP (DENSE_RANK LAST ORDER BY curr),' / ') AS UNIT_ROLE
FROM
(SELECT login,
first_name,
last_name,
user_primary_unit,
rights,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) AS curr,
ROW_NUMBER() OVER (PARTITION BY login ORDER BY rights) -1 AS prev
FROM (select member0_.login, member0_.first_name first_name, unit2.unit_name user_primary_unit, member0_.last_name last_name,
sys.stragg(sys.stragg(unit.unit_name || ' - ' || role3_.role_name)) rights
from
IOT_DEVICES.t_member member0_
inner join IOT_DEVICES.t_user member0_1_ on member0_.member_id=member0_1_.user_id
inner join IOT_DEVICES.t_playable_role playedrole1_ on member0_.member_id=playedrole1_.user_id
inner join IOT_DEVICES.t_unit_role unitrole2_ on playedrole1_.unit_role_id=unitrole2_.unit_role_id
inner join IOT_DEVICES.t_role role3_ on unitrole2_.role_id=role3_.role_id
inner join IOT_DEVICES.t_unit unit on unitrole2_.unit_id=unit.unit_id
inner join IOT_DEVICES.t_unit unit2 on unit2.unit_id=member0_1_.primary_unit_id
where current_date between playedrole1_.start_date and playedrole1_.end_date
order by unit.unit_name
))
GROUP BY login, first_name, last_name, user_primary_unit
CONNECT BY prev = PRIOR curr AND login = PRIOR login
START WITH curr = 1
)
ORDER BY user_PRIMARY_UNIT, FIRST_NAME, LAST_NAME;
您可以使用 subquery factoring syntax 构建层次结构 CLOB
路径,这可能 运行 真的很慢。考虑有两个路径列 - 一个用于 varchar2
结果,一个用于 CLOB
。构建 varchar2
直到大小允许,并在 CLOB
路径中保留 NULL
,当超出 varchar2
容量时切换到 CLOB
。不过,这是一个不同的问题。
with
base as (
select
level as id,
case when level > 1 then level - 1 end as parent_id,
dbms_random.string('X', 2000) as val
from dual
connect by level <= 50
),
hier(id, parent_id, val, path) as (
select
b.id,
b.parent_id,
b.val,
to_clob(concat('/', b.val)) as path
from base b
where b.parent_id is null
union all
select
b.id,
b.parent_id,
b.val,
concat(h.path, to_clob(' / '||b.val) )
from base b
join hier h on h.id = b.parent_id
)
select rownum, length(h.path)
from hier h;
ROWNUM LENGTH(H.PATH)
1 2001
2 4004
3 6007
4 8010
5 10013
6 12016
7 14019
8 16022
9 18025
10 20028
11 22031
12 24034
13 26037
14 28040
15 30043
16 32046
17 34049
18 36052
19 38055
20 40058
21 42061
22 44064
23 46067
24 48070
25 50073
26 52076
27 54079
28 56082
29 58085
30 60088
31 62091
32 64094
33 66097
34 68100
35 70103
36 72106
37 74109
38 76112
39 78115
40 80118
41 82121
42 84124
43 86127
44 88130
45 90133
46 92136
47 94139
48 96142
49 98145
50 100148
您可能会找到此页面,因为您正在尝试聚合超过 4000 个字符的字符串并记住 the different techniques。
如果是这样,我根据@B Samedi 的回答创建了一个小示例来帮助您解决问题,当您无法使用 user defined aggregates
with dummy_text as (
select 'teststring ' || rownum str from dual connect by rownum < 2
)
, indexed_strings as (
select str, row_number() over (order by 'x') rn, ',' separator from dummy_text
)
, hier (str, lvl) as (
select to_clob(i.str), rn from indexed_strings i where rn = (select max(rn) from indexed_strings)
union all
select concat(to_clob(concat(i.str, i.separator)), h.str), h.lvl - 1 from indexed_strings i join hier h on h.lvl - 1 = i.rn
)
select str from hier where lvl = 1