具有动态列名的存储过程

时间:2015-12-12 17:54:33

标签: plsql oracle11g

我试图打印多个表格行,而我们不知道列名称。我从一天开始尝试这个,我想出了这个错误的过程,并且必须声明emp_dummy_col'。请帮帮我。 谢谢。

create or replace procedure sp_display
as
   CURSOR cur_emp is select EMP_ID,EMP_NAME,DEPT_IT,DOJ,LOCATION from employee;
   emp_rows cur_emp%rowtype;
   type emp_table is table of emp_rows%type;
   emp_dummy_table emp_table;
   CURSOR cur_col is select column_name from user_tab_cols where table_name ='EMPLOYEE';
   emp_row cur_col%rowtype;
   type emp_table1 is table of emp_row%type;
   emp_dummy_col emp_table1;
begin
   open cur_emp;
      fetch cur_emp bulk collect into emp_dummy_table;
   close cur_emp;
   open cur_col;
      fetch cur_col bulk COLLECT into emp_dummy_col; 
   close cur_col;
   for i in 1..emp_dummy_table.count
   loop
      for j in 1..emp_dummy_col.count
      loop
         DBMS_OUTPUT.PUT_LINE(emp_dummy_table(i).emp_dummy_col(j));
      end loop;
   end loop;
end;

2 个答案:

答案 0 :(得分:1)

如Justin Cave所述,您可以使用包DBMS_SQL进行大量工作。

以下只是一个简单的例子。

SET SERVEROUTPUT ON
SET FEEDBACK OFF
CLEAR

DECLARE
  -- The SQL Statement
  V_SQL VARCHAR2(4000) := 'select EMP_ID,EMP_NAME,DEPT_IT,DOJ,LOCATION from employee';
  -- The cursor (number) variable
  V_CURSOR INTEGER;
  -- Variable to hold the return value of DBMS_SQL.EXECUTE (Number of processed rows)
  V_NUM_ROWS NUMBER;
  -- Variable to hold the return value of DBMS_SQL.DESCRIBE_COLUMNS
  V_COL_CNT INTEGER;
  -- Column description table. Outcome from DBMS_SQL.DESCRIBE_COLUMNS
  V_DESC DBMS_SQL.DESC_TAB;
  -- Variable for the current column number when iterating over the column description collection
  V_COL_NUM NUMBER;
  -- (generic) Variable for a VARCHAR2-Column
  V_VARC_COL VARCHAR2(4000);
  -- (generic) Variable for a NUMBER-Column
  V_NUM_COL NUMBER;
BEGIN

  -- Open a cursor
  V_CURSOR := DBMS_SQL.OPEN_CURSOR;
  -- Parse the SQL-Query (without any bindings etc.)
  DBMS_SQL.PARSE(V_CURSOR, V_SQL, DBMS_SQL.NATIVE);

  -- Execute the parsed query and return the number of processed rows. 
  -- May be 0 or undefined on SELECT- and DDL-Statements and should be ignored
  V_NUM_ROWS := DBMS_SQL.EXECUTE(V_CURSOR);

  -- Get the columns description table
  DBMS_SQL.DESCRIBE_COLUMNS(V_CURSOR, V_COL_CNT, V_DESC);

  V_COL_NUM := V_DESC.FIRST;

  IF (V_COL_NUM IS NOT NULL) THEN
    LOOP
      -- Assign variables to column types
      CASE V_DESC(V_COL_NUM).COL_TYPE
        WHEN DBMS_SQL.NUMBER_TYPE THEN
          DBMS_SQL.DEFINE_COLUMN(V_CURSOR, V_COL_NUM, V_NUM_COL);
        WHEN DBMS_SQL.VARCHAR2_TYPE THEN
          DBMS_SQL.DEFINE_COLUMN(V_CURSOR, V_COL_NUM, V_VARC_COL, 4000);
          -- more branches if other column types are expected/supported (Date, LOBs etc.)
        ELSE
          NULL;
      END CASE;
      V_COL_NUM := V_DESC.NEXT(V_COL_NUM);
      EXIT WHEN(V_COL_NUM IS NULL);
    END LOOP;

    -- Fetch the rows
    V_NUM_ROWS := DBMS_SQL.FETCH_ROWS(V_CURSOR);
    IF (V_NUM_ROWS > 0) THEN
      LOOP
        -- Column handling
        FOR L_I IN V_DESC.FIRST .. V_DESC.LAST LOOP
          CASE V_DESC(L_I).COL_TYPE
          -- NUMBER column type value handler
            WHEN DBMS_SQL.NUMBER_TYPE THEN
              DBMS_SQL.COLUMN_VALUE(V_CURSOR, L_I, V_NUM_COL);
              V_VARC_COL := TO_CHAR(V_NUM_COL);
              -- VARCHAR2 column type value handler
            WHEN DBMS_SQL.VARCHAR2_TYPE THEN
              DBMS_SQL.COLUMN_VALUE(V_CURSOR, L_I, V_VARC_COL);
              -- more branches if other column types are expected/supported (Date, LOBs etc.)
            ELSE
              --Handling for unsupported column types (if necessary)
              NULL;
          END CASE;
          DBMS_OUTPUT.PUT(V_DESC(L_I).COL_NAME || '" = "' || V_VARC_COL || '"');
          IF (L_I < V_DESC.COUNT) THEN
            DBMS_OUTPUT.PUT('  |  ');
          END IF;
        END LOOP;
        DBMS_OUTPUT.PUT_LINE('');
        -- Fetch the next row
        V_NUM_ROWS := DBMS_SQL.FETCH_ROWS(V_CURSOR);
        -- Exit loop if no more row is fetched
        EXIT WHEN V_NUM_ROWS = 0;
      END LOOP;
    END IF;
  END IF;
  -- Close the cursor (important!)
  DBMS_SQL.CLOSE_CURSOR(V_CURSOR);

EXCEPTION
  WHEN OTHERS THEN
    -- Close the cursor (important!) if any error occurs
    IF (DBMS_SQL.IS_OPEN(V_CURSOR)) THEN
      DBMS_SQL.CLOSE_CURSOR(V_CURSOR);
    END IF;
    -- Rethrow the exception
    RAISE;
END;
/

有关DBMS_SQL的详细/进一步信息:

!!安全警告!!

使用这种动态SQL容易受到SQL注入和其他技术的攻击!

答案 1 :(得分:1)

嗯,你所拥有的代码示例并没有真正满足你的要求。它希望您告诉列,请参阅:

  

从员工中选择EMP_ID,EMP_NAME,DEPT_IT,DOJ,LOCATION;

1)你可以得到像这样的表的未知列(我在这个例子中使用表NEWS)

begin
    for rec in
           (select column_name from user_tab_cols
           where table_name='NEWS') loop
        dbms_output.put_line(rec.column_name);
    end loop;
end;

2)如果你想要打印出数据,那就是非常讨厌PLSQL,请参阅Cursor For Loop with dynamic SQL-Statement

3)如果您对某些妥协做得很好,请尝试这种方法。我从user_tab_cols中学习的字段构建了一个动态SQL语句。动态SQL将每列的字符串值连接成一行,最后我在此处获取一个游标。

为方便起见,我还在标题变量中收集列名。

declare
    statement varchar2(4000);
    header varchar2(4000);
    TYPE TCur IS REF CURSOR;
    cur TCur;
    TYPE TRec IS RECORD (
      line varchar2(4000)
    );
    rec TRec;
begin

    for rec in
           (select column_name from user_tab_cols
           where table_name='NEWS') loop
        if statement is not null then
            statement:=statement||'||'',''||';
            header:=header||',';
        end if;
        statement:=statement||rec.column_name;
        header:=header||rec.column_name;
    end loop;
    statement:='select '||statement||' as line from '||'NEWS';
    dbms_output.put_line(header);
    open cur for statement;
    loop
        fetch cur into rec;
        exit when cur%notfound;
        dbms_output.put_line(rec.line);
    end loop;
    close cur;
end;

给予:

NEWS_ID,NEWS
0,Some new PLSQL hack has been done
1,And the day is not over yet!