Oracle,使用“connect by”语句重复记录

Posted

技术标签:

【中文标题】Oracle,使用“connect by”语句重复记录【英文标题】:Oracle, duplicate records using "connect by" statement 【发布时间】:2016-04-11 14:46:32 【问题描述】:

作为 Oracle 数据库的新手,我恳请您的帮助。

我需要将一个字符串拆分为每个字符的不同记录:

我有一个包含 VARCHAR 字段的查询,我需要将其拆分为多个字符

select
    MAPS.MAP_ID,
    HARD_BIN_LINES.LINE, HARD_BIN_LINES.BINS
from MAPS, HARD_BIN_LINES
where MAPS.MAP_ID = 9595435 and MAPS.MAP_ID = HARD_BIN_LINES.MAP_ID 
order by HARD_BIN_LINES.MAP_ID, HARD_BIN_LINES.LINE


MAP_ID  LINE    BINS
9595435 1       ÿÿÿÿÿÿÿÿÿÿÿþþþþÿÿÿÿÿÿÿÿÿÿÿ
9595435 2       ÿÿÿÿÿÿÿþþ       þþÿÿÿÿÿÿÿÿ
9595435 3       ÿÿÿÿÿþþ2           þÿÿÿÿÿÿ
9595435 4       ÿÿÿÿþ               þþÿÿÿÿ
9595435 5       ÿÿÿþ2                 þÿÿÿ
9595435 6       ÿÿþ                    þÿÿ
9595435 7       ÿÿþ2                   þÿÿ
9595435 8       ÿþþÿ                   þþÿ
9595435 9       ÿ2                      þÿ
9595435 10      þÿ                      þÿ
9595435 11      þ                        þ
9595435 12      ü                        þ
9595435 13      ü2                       þ
9595435 14      þ                        þ
9595435 15      þ                        ÿ
9595435 16      ÿþ             xx      þÿ
9595435 17      ÿþ                      þÿ
9595435 18      ÿÿþ                    þÿÿ
9595435 19      ÿÿþ                    þÿÿ
9595435 20      ÿÿÿþ                  þÿÿÿ
9595435 21      ÿÿÿÿþ               þþÿÿÿÿ
9595435 22      ÿÿÿÿÿþ      þ 2    þÿÿÿÿÿÿ
9595435 23      ÿÿÿÿÿÿÿþ þ  þþ    ÿÿÿÿÿÿÿÿ
9595435 24      ÿÿÿÿÿÿÿÿÿÿÿþþþþÿÿÿÿÿÿÿÿÿÿÿ

我的目标是将 BINS 字符串拆分为几个字母/记录以获得这样的记录集

MAP_ID  LINE    LEVEL   CHR BINCODE
-------+-------+-------+---+--------
9595435 1       2       ÿ   255
9595435 1       3       ÿ   255
9595435 1       4       ÿ   255
9595435 1       5       ÿ   255
9595435 1       6       ÿ   255
9595435 1       7       ÿ   255
9595435 1       8       ÿ   255
9595435 1       9       ÿ   255
9595435 1       10      ÿ   255
9595435 1       11      ÿ   255
9595435 1       12      þ   254
9595435 1       13      þ   254
9595435 1       14      þ   254
9595435 1       15      þ   254
9595435 1       16      ÿ   255
9595435 1       17      ÿ   255
9595435 1       18      ÿ   255
9595435 1       19      ÿ   255
9595435 1       20      ÿ   255
9595435 1       21      ÿ   255
9595435 1       22      ÿ   255
9595435 1       23      ÿ   255
9595435 1       24      ÿ   255
9595435 1       25      ÿ   255
9595435 1       26      ÿ   255
-------+-------+-------+---+--------
9595435 2       2       ÿ   255
9595435 2       3       ÿ   255
9595435 2       4       ÿ   255
9595435 2       5       ÿ   255
9595435 2       6       ÿ   255
9595435 2       7       ÿ   255
9595435 2       8       þ   254
9595435 2       9       þ   254
9595435 2       10          1
9595435 2       11          1
9595435 2       12          10
9595435 2       13          1
9595435 2       14          13
9595435 2       15          17
9595435 2       16          1
9595435 2       17      þ   254
9595435 2       18      þ   254
9595435 2       19      ÿ   255
9595435 2       20      ÿ   255
9595435 2       21      ÿ   255
9595435 2       22      ÿ   255
9595435 2       23      ÿ   255
9595435 2       24      ÿ   255
9595435 2       25      ÿ   255
9595435 2       26      ÿ   25
-------+-------+-------+---+--------
(...)

如果我尝试使用“CONNECT BY”语句解决问题,我会获得许多重复记录,但我无法理解为什么我的查询也没有问题:

with temp as (
    select
        MAPS.MAP_ID,
        HARD_BIN_LINES.LINE, HARD_BIN_LINES.BINS
    from MAPS, HARD_BIN_LINES
    where MAPS.MAP_ID = 9595435 and MAPS.MAP_ID = HARD_BIN_LINES.MAP_ID
    order by HARD_BIN_LINES.MAP_ID, HARD_BIN_LINES.LINE
)
select 
    MAP_ID, LINE, LEVEL,
    substr(BINS,level,1) as CHR, ASCII(substr(BINS,level,1)) as BINCODE
from temp
connect by level <= length(BINS)

【问题讨论】:

Connect by 只能用于递归(猪耳朵)自引用外键关系。连接语法是:先连接 = 。您正在尝试使用没有递归关系的表上的范围进行连接。请分享 MAPS 和 HARD_BIN_LINES 的结构。 CONNECT BY 已被使用,因为它是我发现的按字符分割字符串的独特方式(参考 @Aleksej 回答 ***.com/questions/36542072/…)。 MAPS 和 HARD_BIN_LINES 仅由 MAP_ID 引用,我的目标是将 HARD_BIN_LINES.BINS 字符串拆分为多个记录(每个字符一个)以检索源 ASCII 代码。无论如何,根据要求,我会将表格结构添加到我的问题中。 【参考方案1】:

temp 结果集中的每一行都连接到其他每一行。您需要使用prior line = line 子句将递归限制在同一行。但是你也需要避免循环;一种方法是在另一个 prior 子句中引用非确定性函数:

select 
    MAP_ID, LINE, LEVEL,
    substr(BINS,level,1) as CHR, ASCII(substr(BINS,level,1)) as BINCODE
from temp
connect by prior line = line
and prior dbms_random.value is not null
and level <= length(BINS);

从您发布的临时数据中获取 623 行。

如果您使用的是 11gR2 或更高版本,您也可以使用 recursive subquery factoring:

with temp as (
  select ...
),
r (MAP_ID, LINE, LVL, CHR, BINCODE) AS (
  select MAP_ID, LINE, 1, substr(BINS,1,1), ASCII(substr(BINS,1,1))
  from temp
  union all
  select t.MAP_ID, t.LINE, r.LVL + 1, substr(t.BINS,r.LVL + 1,1),
    ASCII(substr(BINS,r.LVL + 1,1))
  from r
  join temp t on t.MAP_ID = r.MAP_ID and t.LINE = r.LINE and length(t.BINS) > r.LVL
)
select *
from r;

得到相同的结果。

【讨论】:

以上是关于Oracle,使用“connect by”语句重复记录的主要内容,如果未能解决你的问题,请参考以下文章

ORACLE CONNECT BY LEVEL 产生重复行

oracle中connect by语句的优化

oracle 层次查询语句

Oracle高级查询之CONNECT BY

Oracle PL/SQL CONNECT BY PRIOR ... SQL Server 中的 START WITH 语句

Oracle递归查询start with connect by prior