所以这要么是 Oracle 的错误,要么是我今天有点慢。
这个 SQL 执行得很好:
WITH car_paint_options AS (
SELECT 'Escort' car_model, 'red,blue' paint_opts FROM dual UNION
SELECT 'Puma' car_model, 'black' paint_opts FROM dual
)
SELECT row_number() over(order by level) rn, level, ep.car_model,
regexp_substr(ep.paint_opts, '[^,]+', 1, level) paint_opt
FROM car_paint_options ep
CONNECT BY regexp_substr (ep.paint_opts, '[^,]+', 1, level) is not null
但是,它给了我错误的答案(4行数据):
rn level car_model paint_opt
--- ----- --------- ---------
1 1 Puma black
2 1 Focus red
3 2 Focus blue
4 2 Focus blue
所需的输出只有 3 行数据,如下所示:
rn level car_model paint_opt
--- ----- --------- ---------
1 1 Puma black
2 1 Focus red
3 2 Focus blue
我明白为什么会出现问题。 2 级记录尝试连接回 1 级记录,所发生的情况是
Focus:blue
选项成功匹配回 Puma:black
和 Focus:red
父行。
所以现在我在想:“很好,足够简单的修复,让我们约束
level 2
对象,以便它们只链接回相同 car_model
的父对象”:
WITH car_paint_options AS (
SELECT 'Escort' car_model, 'red,blue' paint_opts FROM dual UNION
SELECT 'Puma' car_model, 'black' paint_opts FROM dual
)
SELECT row_number() over(order by level) rn, level, ep.car_model,
regexp_substr(ep.paint_opts, '[^,]+', 1, level) paint_opt
FROM car_paint_options ep
CONNECT BY regexp_substr (ep.paint_opts, '[^,]+', 1, level) is not null
AND ep.car_model = prior ep.car_model
但它会导致错误:
ORA-01436:用户数据中的 CONNECT BY 循环
在 Oracle 12c + 19c 中进行了测试。有人可以确认我在这里没有做任何愚蠢的事情吗?我认为这是一个错误吗?
对此的正常“黑客”是在
CONNECT BY
子句中添加一些内容,为每一行提供唯一值,例如 SYS_GUID()
或 DBMS_RANDOM.VALUE()
并防止分层查询检测循环:
WITH car_paint_options (car_model, paint_opts) AS (
SELECT 'Escort', 'red,blue' FROM dual UNION ALL
SELECT 'Puma', 'black' FROM dual
)
SELECT row_number() over(order by level) rn, level, ep.car_model,
regexp_substr(ep.paint_opts, '[^,]+', 1, level) paint_opt
FROM car_paint_options ep
CONNECT BY regexp_substr (ep.paint_opts, '[^,]+', 1, level) is not null
AND ep.car_model = prior ep.car_model
AND PRIOR SYS_GUID() IS NOT NULL
哪个输出:
RN | 级别 | 汽车型号 | PAINT_OPT |
---|---|---|---|
1 | 1 | 护送 | 红色 |
2 | 1 | 美洲狮 | 黑色 |
3 | 2 | 护航 | 蓝色 |
但是,正则表达式函数很慢,并且使用简单的字符串函数通常会更快,即使这意味着您需要输入更多内容(如果简单的字符串函数比正则表达式更快,那么它们甚至比正则表达式还要快,再加上生成每行的 GUID):
WITH car_paint_options (car_model, paint_opts) AS (
SELECT 'Escort', 'red,blue' FROM dual UNION ALL
SELECT 'Puma', 'black' FROM dual
),
bounds (car_model, paint_opts, lvl, spos, epos) AS (
SELECT car_model,
paint_opts,
1,
1,
INSTR(paint_opts, ',', 1)
FROM car_paint_options
UNION ALL
SELECT car_model,
paint_opts,
lvl + 1,
epos + 1,
INSTR(paint_opts, ',', epos + 1)
FROM bounds
WHERE epos > 0
)
SELECT ROW_NUMBER() OVER (ORDER BY lvl) AS rn,
lvl,
car_model,
CASE epos
WHEN 0
THEN SUBSTR(paint_opts, spos)
ELSE SUBSTR(paint_opts, spos, epos - spos)
END AS paint_opt
FROM bounds
输出相同。
RN | LVL | 汽车型号 | PAINT_OPT |
---|---|---|---|
1 | 1 | 护航 | 红色 |
2 | 1 | 美洲狮 | 黑色 |
3 | 2 | 护航 | 蓝色 |