【问题标题】:How to dynamically create a variable with the data type of table?如何动态创建表数据类型的变量?
【发布时间】:2014-10-11 03:23:37
【问题描述】:

在以下查询中,select_string 可以返回任意数量的列和行以及任何可能的数据类型。

Execute immediate 'select_string' into v_table_variable;

例如 select_string 可以是 'select name, last name from student''select date, subject, address , phone from booking' 等等。

有谁知道如何定义 v_table_variable 以便立即执行我想要的运行?!我打算之后做一个循环来读取这个变量的值。

非常感谢!

【问题讨论】:

  • 你实际上可能做不到。您可能能够动态地构建整个 PL/SQL 块(包括后续循环),但使用动态 PL/SQL 通常是一种让您自己(以及任何必须维护您的代码的人)发疯的快速方法。您可能想使用dbms_sql 而不是execute immediate,但您很少真正想要编写通用的代码。您要解决的根本问题是什么?
  • 感谢您的快速回复!我想构建一个函数来获取表的名称和作为其 PK 之一的值,然后删除子表中的所有相应行,以及子表的子表中的所有相应行,依此类推。我不允许用级联改变我的表。
  • 你确定你真的需要一个完全通用的函数吗?而不是,比如说,一个delete_foo 过程,它通过首先从所有foo 的子表中删除数据来删除foo?您可以构建一个从一堆数据字典表中读取并创建一堆动态 SQL 的过程。但这比简单的程序选项要复杂一个数量级。你确定额外的复杂性真的值得吗?
  • 所以你有一些表有外键约束但没有ON DELETE CASCADE,你想在PL/SQL中“模拟”级联?

标签: oracle plsql


【解决方案1】:

“成熟”的动态版本将是这个(未经测试):

DECLARE  
  v_stmt_str       VARCHAR2(200);
  v_cur            INTEGER;
  v_rows_processed INTEGER;

  col_cnt     INTEGER;
  rec_tab     DBMS_SQL.DESC_TAB;
  rec         DBMS_SQL.DESC_REC;

  num_var NUMBER;
  string_var VARCHAR2(4000);
  date_var DATE;
  -- .. some more variables if needed 
BEGIN
  v_cur := DBMS_SQL.OPEN_CURSOR; -- open cursor 
  v_stmt_str := 'SELECT whatever from ....';
  DBMS_SQL.PARSE(v_cur, v_stmt_str, DBMS_SQL.NATIVE); 
  DBMS_SQL.DESCRIBE_COLUMNS(v_cur, col_cnt, rec_tab);  

  FOR c in 1..col_cnt LOOP
     rec := rec_tab(c);
     IF rec.col_type = DBMS_TYPES.TYPECODE_NUMBER THEN
         DBMS_SQL.DEFINE_COLUMN(v_cur, c, num_var); 
     ELSIF rec.col_type = DBMS_TYPES.TYPECODE_VARCHAR2 THEN
         DBMS_SQL.DEFINE_COLUMN(v_cur, c, string_var, rec.col_max_len); 
     ELSIF rec.col_type = DBMS_TYPES.TYPECODE_DATE THEN
         DBMS_SQL.DEFINE_COLUMN(v_cur, c, date_var); 
     -- .. some more data types if needed
     END IF;
  END LOOP;

  -- Execute
  v_rows_processed := DBMS_SQL.EXECUTE(v_cur);  
  LOOP 
    -- Fetch a row 
    IF DBMS_SQL.FETCH_ROWS(v_cur) > 0 THEN 
      FOR c in 1..col_cnt LOOP
        rec := rec_tab(c);
        IF rec.col_type = DBMS_TYPES.TYPECODE_NUMBER THEN
            DBMS_SQL.COLUMN_VALUE(v_cur, c, num_var); 
        ELSIF rec.col_type = DBMS_TYPES.TYPECODE_VARCHAR2 THEN
            DBMS_SQL.COLUMN_VALUE(v_cur, c, string_var); 
        ELSIF rec.col_type = DBMS_TYPES.TYPECODE_DATE THEN
            DBMS_SQL.COLUMN_VALUE(v_cur, c, date_var); 
        -- .. some more data types if needed
        END IF;
      END LOOP;
      -- Process: do something with num_var or string_var or date_var values
    ELSE
      EXIT; 
    END IF; 
  END LOOP; 
  DBMS_SQL.CLOSE_CURSOR(v_cur); -- close cursor
END;
/

但正如 cmets 中所述,如果您真的需要这样的动态,请检查您的要求 - 可能不需要。

查看 Oracle 文档 Coding Dynamic SQLDBMS_SQL 以获取其他示例。有多种方法提供不同“级别”的动态。

【讨论】:

  • 感谢您的精彩提示 :-)。我不熟悉 DBMS_SQL。只是一件事,不应该“用 num_var 或......”在最新的结束循环之前?!如果我们的查询返回例如 4 列的数据类型为 number,那么我猜,这样我们将只有 num_var 中的最后一个值,对吧?!只是想确保我做对了。这对我帮助很大,非常感谢:-)
  • 是的,如果您的表包含多个 NUMBER 类型的列,您必须在循环内处理它们。或者你可以把它们放到一个嵌套表中,在循环之后处理嵌套表。
猜你喜欢
  • 1970-01-01
  • 2017-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-08
  • 2016-03-19
相关资源
最近更新 更多