繁体   English   中英

获取 Oracle SQL 查询结果的列名

[英]Get column names of the result of a query Oracle SQL

我需要一个查询来获取另一个查询结果的列名。 另一个查询可以是任何东西——我不能对它做任何假设,但它通常是一些SELECT语句。

例如,如果我有这个表Members

Id | Name | Age
---|------|----
1  | John | 25
2  | Amir | 13

而这个SELECT语句SELECT Name, Age FROM Members

然后我尝试编写的查询结果将是

Name
Age

在 SQL Server 中,有一个函数 - sys.dm_exec_describe_first_result_set - 可以执行此操作,但我在 Oracle 中找不到等效函数。

我尝试使用此答案,但由于权限问题,我无法使用CREATE TYPE语句,并且出于同样的原因,我可能无法使用CREATE FUNCTION语句。

假设你有一个这样的查询:

select *
from   (select deptno, job, sal from scott.emp)
pivot  (avg(sal) as avg_sal for job in 
           ('ANALYST' as analyst, 'CLERK' as clerk, 'SALESMAN' as salesman)
       )
order by deptno
;

这产生了结果:

    DEPTNO ANALYST_AVG_SAL CLERK_AVG_SAL SALESMAN_AVG_SAL
---------- --------------- ------------- ----------------
        10                          1300                 
        20            3000           950                 
        30                           950             1400

注意列名(如ANALYST_AVG_SAL )——它们在查询中的任何地方都没有完全以这种形式出现! 它们由两个独立的部分组成,并用下划线组合在一起。

现在,如果您被允许创建视图(请注意,这不会在您的数据库中创建任何数据——它只是保存查询的文本),您可以这样做:

创建视图(只需将第一行代码添加到我们已有的内容中):

create view q201028_vw as
select *
from   (select deptno, job, sal from scott.emp)
pivot  (avg(sal) as avg_sal for job in 
            ('ANALYST' as analyst, 'CLERK' as clerk, 'SALESMAN' as salesman)
       )
order by deptno
;

(这里我假设您有某种方法来识别查询,一个 id 像Q201028 ,并在视图名称中使用它。这并不重要,除非您需要经常这样做并且同时进行大量查询。 )

然后您可以通过查询*_TAB_COLUMNS找到列名(以及它们的顺序,以及 - 如果需要 - 它们的数据类型等)。 例如:

select column_id, column_name
from   user_tab_columns
where  table_name = 'Q201028_VW'
order  by column_id
;

 COLUMN_ID COLUMN_NAME         
---------- --------------------
         1 DEPTNO              
         2 ANALYST_AVG_SAL     
         3 CLERK_AVG_SAL       
         4 SALESMAN_AVG_SAL 

现在,如果您不需要其他任何东西,您可以删除该视图。

顺便说一句:在 Oracle 中,在数据库中“保存”查询的“通常”方法是创建视图。 如果它们已经存在于您的数据库中,那么您所需要的就是我向您展示的最后一步。 否则,首先是“其他查询”(您需要为其查找列)吗?

我将使用 dbms_sql 包,以下代码示例应向您展示如何开始:

DECLARE
    cursorID            INTEGER;
    status              INTEGER;
    colCount            INTEGER;
    rowCount            INTEGER;
  description       dbms_sql.desc_tab;
    colType             INTEGER;
    stringValue     VARCHAR2(32676);
    sqlCmd              VARCHAR2(32767);
BEGIN
    -- open cursor
    cursorID := dbms_sql.open_cursor;

    -- parse statement
    dbms_sql.parse(cursorID, 'select * from user_tables', dbms_sql.native);

    -- describe columns
    dbms_sql.describe_columns(cursorID, colCount, description);

    -- cursor close
    dbms_sql.close_cursor(cursorID);

    -- open cursor
    cursorID := dbms_sql.open_cursor;

    -- assemble a new select only using up to 5 the "text" columns
    FOR i IN 1 .. description.COUNT LOOP
        IF (i > 5) THEN
            EXIT;
        END IF;
        IF (description(i).col_type IN (1, 112)) THEN
            IF (sqlCmd IS NOT NULL) THEN
                 sqlCmd := sqlCmd || ', ';
            END IF;
            sqlCmd := sqlCmd || description(i).col_name;
        END IF;
    END LOOP;
    sqlCmd := 'SELECT ' || sqlCmd || ' FROM user_tables';
    dbms_output.put_line(sqlCmd);

    -- parse statement
    dbms_sql.parse(cursorID, sqlCmd, dbms_sql.native);

    -- describe columns
    dbms_sql.describe_columns(cursorID, colCount, description);

    -- define columns
    FOR i IN 1 .. description.COUNT LOOP
        dbms_sql.define_column(cursorID, i, stringValue, 4000);
    END LOOP;

    -- execute
    status := dbms_sql.execute(cursorID);

    -- fetch up to 5 rows
    rowCount := 0;
    WHILE (dbms_sql.fetch_rows(cursorID) > 0) LOOP
        rowCount := rowCount + 1;
        IF (rowCount > 5) THEN
            EXIT;
        END IF;
        dbms_output.put_line('row # ' || rowCount);
        FOR i IN 1 .. description.COUNT LOOP
            dbms_sql.column_value(cursorID, i, stringValue);
            dbms_output.put_line('column "' || description(i).col_name || '" = "' || stringValue || '"');
        END LOOP;
    END LOOP;

    -- cursor close
    dbms_sql.close_cursor(cursorID);
END;
/

正如 astentx 所建议的,您可以使用公共表表达式函数将 PL/SQL 代码打包成 SQL 语句。 该解决方案只是一条 SQL 语句,不需要非默认权限,也不会创建任何永久对象。

(唯一的缺点是并非所有 SQL 工具都能理解这些类型的 WITH 子句,并且它们可能会抛出错误,期望使用不同的语句终止符。)

SQL> create table members(id number, name varchar2(100), age number);

Table created.

SQL> with function get_result_column_names(p_sql varchar2) return sys.odcivarchar2list is
  2      v_cursor_id integer;
  3      v_col_cnt integer;
  4      v_columns dbms_sql.desc_tab;
  5  v_column_names sys.odcivarchar2list := sys.odcivarchar2list();
  6  begin
  7      v_cursor_id := dbms_sql.open_cursor;
  8      dbms_sql.parse(v_cursor_id, p_sql, dbms_sql.native);
  9      dbms_sql.describe_columns(v_cursor_id, v_col_cnt, v_columns);
 10
 11      for i in 1 .. v_columns.count loop
 12  v_column_names.extend;
 13  v_column_names(v_column_names.count) := v_columns(i).col_name;
 14      end loop;
 15
 16      dbms_sql.close_cursor(v_cursor_id);
 17
 18  return v_column_names;
 19  exception when others then
 20      dbms_sql.close_cursor(v_cursor_id);
 21      raise;
 22  end;
 23  select *
 24  from table(get_result_column_names(q'[select name, age from members]'));
 25  /

COLUMN_VALUE
--------------------------------------------------------------------------------
NAME
AGE

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM