如何使用 Oracle (PL/SQL) 动态 sql 将数据查询到 %rowtype 变量中

Posted

技术标签:

【中文标题】如何使用 Oracle (PL/SQL) 动态 sql 将数据查询到 %rowtype 变量中【英文标题】:How can I use Oracle (PL/SQL) dynamic sql to query data into a %rowtype variable 【发布时间】:2013-08-26 12:02:16 【问题描述】:

问题:

我有一个包含 CLOB 的表,它是来自外部源的固定长度记录。其中位置 1-5 = fieldA,位置 6-12 = fieldB,等等。

我有另一个表 (LayoutDefinition) 定义了记录布局,其中

FieldName = 'fieldA'
FieldStart = 1
FieldLength = 5
FieldName = 'fieldB'
FieldStart = 6
FieldLength = 6

我需要从 CLOB 中检索数据并将其放入 %rowtype 变量中,例如“tableA_rec tableA%rowtype”。

我已经实现了一个使用大量 case 语句的例程,并且 LayoutDefiniton 表中每一行的循环将 CLOB 的区域移动到 tablea_rec 中的适当变量中;

CASE LayoutDefiniton.FieldName
WHEN 'fieldA' THEN tablea_rec.fieldA:= SUBSTR(inputClob,LayoutDefiniton.FieldStart,LayoutDefiniton.FieldLength);
WHEN 'fieldB' THEN tablea_rec.fieldB:= SUBSTR(inputClob,LayoutDefiniton.FieldStart,LayoutDefiniton.FieldLength);

这当然是非常低效的,因为我需要遍历我的布局,以便为每条记录挑选我的数据。

我想做的是创建一个动态 sql 选择语句,该语句将把表中的数据检索到适当的变量中。例如,如果它不是动态的,它可能看起来像;

select substr(inputCLOB,1,5), substr(inputCLOB,6,6) into FieldA, fieldB from CLOBTable;

这可以使用动态 SQL 来完成吗?

如果是这样,语法会是什么样子?

【问题讨论】:

您最初是如何获取数据的?它是在文件中交付的吗?如果是这样,您不应该将它放在 CLOB 中,而是使用外部表或 SQL*Loader。如果它是从另一个数据库收到的,请查看 DBMS_FILE_TRANSFER 和 DBMS_DATAPUMP... 它来自另一个使用 Oracle 的系统。我可以让其他系统将数据写入文件,然后使用 sql*loader 导入数据,但这需要手动干预以及性能损失。 CLOB 超过 20,000 字节,包含数百个字段。 如果它在另一个 Oracle 数据库中,那么我说您应该考虑 DBMS_DATAPUMP... 而不是 SQL*Loader。 20,000 字节真的没那么大,你已经在读取和传输数据了;我只是建议你以一种最终会给你一张漂亮的桌子的方式来做这件事。或者,您可以直接在数据库链接上插入? 对不起,它在同一个数据库的另一个模式中。另一个应用程序构建一个 CLOB,因为在大多数情况下,它会输出要在其他系统中使用的数据。我通过搜索他们的模式找到了 CLOB。我不想导出数据并重新导入它,而是想从 CLOB 中获取数据并将其放入我架构中表的适当列中。 其他应用程序永远不会以 CLOB 以外的任何其他格式保存数据。 【参考方案1】:

以下代码将从 CLOB 列中提取数据并插入到目标表中

DECLARE
  l_sql_str VARCHAR2(4000);
BEGIN
  WITH src_meta_agg AS(
    SELECT LISTAGG(field_name, ',') WITHIN GROUP(ORDER BY field_start) AS field_list
           ,LISTAGG('substr(lob,' || field_start || ', ' || field_length || ')', ',') WITHIN GROUP( ORDER BY field_start) AS field_source
      FROM src_meta)
    SELECT 'INSERT INTO dest(' || field_list || ') SELECT ' || field_source ||
           ' FROM src' INTO l_sql_str
      FROM src_meta_agg;

  EXECUTE IMMEDIATE l_sql_str;
END;

LISTAGG 是 11g 函数,用于对数据进行排序并连接值。对于以前的版本,您可以使用wm_concat 或任何其他approach。在l_sql_str 中你会有以下语句

INSERT INTO dest(field1,field2) SELECT substr(lob,1, 5),substr(lob,6, 6) FROM src

如果您需要在 PL/SQL 中处理数据,此代码可以解决问题

DECLARE
  l_sql_str VARCHAR2(4000);
  TYPE t_dest_tbl IS TABLE OF dest%ROWTYPE;
  l_dest_rows t_dest_tbl;
  l_cursor SYS_REFCURSOR;
BEGIN

  WITH src_meta_agg AS(
    SELECT LISTAGG(field_name, ',') WITHIN GROUP(ORDER BY field_start) AS field_list
           ,LISTAGG('substr(lob,' || field_start || ', ' || field_length || ')', ',') WITHIN GROUP( ORDER BY field_start) AS field_source
      FROM src_meta)
    SELECT 'SELECT ' || field_source ||
           ' FROM src' INTO l_sql_str
      FROM src_meta_agg;

  OPEN l_cursor FOR l_sql_str;
  FETCH l_cursor
  BULK COLLECT INTO l_dest_rows;
  CLOSE l_cursor; 

  FOR i IN 1..l_dest_rows.COUNT LOOP
    dbms_output.put_line(l_dest_rows(i).field1 || ', ' || l_dest_rows(i).field2);
  END LOOP;
END;

这是我使用的架构

create table src(lob clob)
/
insert into src values ('12345ABCDEF')
/
insert into src values ('78901GHIJKL')
/

create table src_meta(field_name varchar2(100), field_start number, field_length number)
/

insert into src_meta values ('field1', 1, 5)
/
insert into src_meta values ('field2', 6, 6)
/

create table dest(field1 varchar2(5), field2 varchar2(6))
/

【讨论】:

我一直喜欢学习东西,但我不知道这个特殊的结构。我不确定它是否对我有帮助,因为我需要将数据插入表中,然后在使用之前将其取回。 我希望现在根据您的特定需求调整代码不会那么难。

以上是关于如何使用 Oracle (PL/SQL) 动态 sql 将数据查询到 %rowtype 变量中的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 pl sql 过程从结构仅在运行时知道的 oracle 表中动态获取数据?

如何在 oracle pl/sql 查询中动态获取字段名称?

如何在 Oracle PL/SQL 中动态地从行参数中获取命名列?

使用嵌套表在 Oracle PL/SQL 中构建动态 SQL

ORACLE PL/SQL:使用集合的动态 SQL 选择

Oracle pl sql 动态使用子句