将层次化Oracle查询转换为DB2查询

将层次化Oracle查询转换为DB2查询,oracle,sas,db2,Oracle,Sas,Db2,我主要与SAS和Oracle合作,对DB2还是新手。Im面临着需要一个分层查询来将clob分割成可以拉入sas的块。SAS对字符变量的限制是32K,所以我不能正常地拉入数据集 我发现了一个旧的stackoverflow问题,关于将clob拉入sas数据集中的最佳方法,但它是用Oracle编写的。 因为我是DB2新手,而且这种类型的连接的语法似乎非常不同,所以我希望能找到人帮助转换它并解释语法。我发现Oracle语法更容易理解。我不确定在DB2中是使用这样的CTE递归,还是使用这样的分层查询 下

我主要与SAS和Oracle合作,对DB2还是新手。Im面临着需要一个分层查询来将clob分割成可以拉入sas的块。SAS对字符变量的限制是32K,所以我不能正常地拉入数据集

我发现了一个旧的stackoverflow问题,关于将clob拉入sas数据集中的最佳方法,但它是用Oracle编写的。

因为我是DB2新手,而且这种类型的连接的语法似乎非常不同,所以我希望能找到人帮助转换它并解释语法。我发现Oracle语法更容易理解。我不确定在DB2中是使用这样的CTE递归,还是使用这样的分层查询

下面是Oracle查询

SELECT    
      id  
    , level as chunk_id
    , regexp_substr(clob_value, '.{1,32767}', 1, level, 'n') as clob_chunk
FROM (
    SELECT id, clob_value
    FROM schema.table
    WHERE id = 1
)
CONNECT BY LEVEL <= regexp_count(clob_value, '.{1,32767}',1,'n')
order by id, chunk_id;
我的想法是我想要这个结果。我一次只能做一行,其中id=我正在处理的行

ID  CHUNK_ID   CLOB
1   1       clob_chunk1of3
1   2       clob_chunk2of3
1   3       clob_chunk3of3

感谢您花时间阅读和帮助。

这里有一个解决方案,它应该可以在DB2中工作,只需很少改动,但请注意,我对DB2一无所知;我只是在使用SQL标准中的Oracle特性,所以它们应该在DB2中以相同的方式实现,或者几乎是相同的方式实现

下面我创建了一个包含示例数据的表;然后,我将展示如何将其分块为长度最多为8个字符的子字符串。虽然字符串很短,但我将列定义为CLOB,并且使用CLOB工具;这应该适用于更大的CLOB

如果需要,可以将块大小和id都设置为绑定参数。在下面的演示中,我硬编码了块大小,并显示了表中所有ID的结果。如果CLOB为空,我会返回一个空块

请注意,在查询中触摸CLOB非常昂贵;因此,大部分工作都是在不接触地板的情况下完成的。我只做尽可能少的工作

准备工作

质疑


另一个选项是启用,只需发出

这包括Oracle风格的语法和语法,两者都适用于Db2示例数据库:

-- both queries are against the SAMPLE database
-- and should return the same result
SELECT LEVEL, CAST(SPACE((LEVEL - 1) * 4) || '/' || DEPTNAME
       AS VARCHAR(40)) AS DEPTNAME
FROM DEPARTMENT
     START WITH DEPTNO = 'A00'
     CONNECT BY NOCYCLE PRIOR DEPTNO = ADMRDEPT;


WITH tdep(level, deptname, deptno) as (
    SELECT 1, CAST( DEPTNAME AS VARCHAR(40)) AS DEPTNAME, deptno
    FROM department 
    WHERE DEPTNO = 'A00'
    UNION ALL
    SELECT t.LEVEL+1, CAST(SPACE(t.LEVEL  * 4) || '/' || d.DEPTNAME
       AS VARCHAR(40)) AS DEPTNAME, d.deptno
    FROM DEPARTMENT d, tdep t
    WHERE d.admrdept=t.deptno and d.deptno<>'A00')
SELECT level, deptname
FROM tdep;

有几个问题。。。显示出我对DB2的明显无知。32767是DB2中Oracle称为VARCHAR2的VARCHAR的最大大小吗?字符和字节长度语义之间是否有区别,就像在Oracle中一样?如果是这样的话,您的字符集不太可能是单字节的吗?如果不是,请记住正则表达式量词总是以字符而不是字节为单位,因此您的代码可能无法正常工作,就像它在Oracle中无法正常工作一样,原因也是一样的。或者您确定所有CLOB都是100%ASCII字符吗?除此之外,转换到DB2的明显方法是使用递归with子句重写查询,该子句是SQL标准,我假设是在DB2中实现的。不,32767是SAS对我的限制。SAS作为一种编程语言没有clob,最大的字符结构是一个最大为32k的varchar。因此,在DB2中,列将保持clob,但当我将其读入SAS时,它将更改为varchar.Hmm。。。直到现在我还没有详细阅读你的帖子;我刚刚发现DB2支持分层的CONNECT BY查询,我认为这是Oracle专有的。正如您在文章中所展示的,它们不是——它们也存在于DB2中,而且它们的语法似乎与Oracle中的语法相同。所以您是否需要帮助修复您发布的查询?如果您添加where子句以仅挑出一个id,它将按原样工作;要处理整个表,您需要向connect by子句添加一些条件。非常感谢这个答案,它非常有效!我只是添加了一个一次只为一个id创建的位置。此外,我没有使用DBMS_LOB模块的授权,因此我将它们替换为字符_lengthclob_值、八位字节和常规子对象_值pos 32767。我的项目的DBA很难让人信服,当涉及到拨款时,所以如果我能解决这个问题,我通常会这样做。
drop table tbl purge;    -- If needed

create table tbl (id number, clob_value clob);

insert into tbl (id, clob_value)
  select 1, 'really large clob'  from dual union all
  select 2, 'medium clob'        from dual union all
  select 3, 'another large clob' from dual union all
  select 4, null                 from dual             -- added to check handling
;

commit;
with
  prep(id, len) as (
    select id, dbms_lob.getlength(clob_value)
    from   tbl
  )
,  rec(id, len, ord, pos) as (
    select  id, len, 1, 1
      from  prep
    union all
    select  id, len, ord + 1, pos + 8
      from  rec
      where len >= pos + 8
  )
select   id, ord, dbms_lob.substr(clob_value, 8, pos)
from     tbl inner join rec using (id)
order by id, ord
;

  ID  ORD CHUNK   
---- ---- --------
   1    1 really l
   1    2 arge clo
   1    3 b       
   2    1 medium c
   2    2 lob     
   3    1 another 
   3    2 large cl
   3    3 ob      
   4    1        
-- both queries are against the SAMPLE database
-- and should return the same result
SELECT LEVEL, CAST(SPACE((LEVEL - 1) * 4) || '/' || DEPTNAME
       AS VARCHAR(40)) AS DEPTNAME
FROM DEPARTMENT
     START WITH DEPTNO = 'A00'
     CONNECT BY NOCYCLE PRIOR DEPTNO = ADMRDEPT;


WITH tdep(level, deptname, deptno) as (
    SELECT 1, CAST( DEPTNAME AS VARCHAR(40)) AS DEPTNAME, deptno
    FROM department 
    WHERE DEPTNO = 'A00'
    UNION ALL
    SELECT t.LEVEL+1, CAST(SPACE(t.LEVEL  * 4) || '/' || d.DEPTNAME
       AS VARCHAR(40)) AS DEPTNAME, d.deptno
    FROM DEPARTMENT d, tdep t
    WHERE d.admrdept=t.deptno and d.deptno<>'A00')
SELECT level, deptname
FROM tdep;