先连接 - 这是 Oracle 错误吗?

问题描述 投票:0回答:1

所以这要么是 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 中进行了测试。有人可以确认我在这里没有做任何愚蠢的事情吗?我认为这是一个错误吗?

sql oracle connect-by
1个回答
1
投票

对此的正常“黑客”是在

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 护航 蓝色

小提琴

© www.soinside.com 2019 - 2024. All rights reserved.