【问题标题】:Get column names of the result of a query Oracle SQL获取查询结果的列名 Oracle SQL
【发布时间】:2021-12-26 21:52:54
【问题描述】:

我需要一个查询来获取另一个查询结果的列名。另一个查询可以是任何东西——我不能对它做出任何假设,但它通常是一些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 中找不到等效函数。

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

【问题讨论】:

  • 如果您无权执行某些操作,这将很难。您可能会告诉您的老板,如果没有这些权限,您将无法解决问题 - 他们要么授予您(即使是临时的),要么将任务分配给拥有权限的其他人。例如:列(和列名)可以在 PIVOT 子句或 MATCH_RECOGNIZE 子句中创建,等等。查询可能是 SELECT * FROM ... 并且列名来自 PIVOT 或 MATCH_RECOGNIZE 或其他的整个世界东西。
  • 我知道的唯一方法是将动态 sql 与 DBMS_SQL 一起使用,这是一种皇家痛苦
  • 一般的解决方案是从您的 select 语句中创建一个视图 - 然后您可以轻松地检查该视图(特别是查看它具有哪些列名);但要做到这一点,您需要能够创建视图。如果你不能创建视图,那么你就不能使用这种方法,即使它可能是最简单的。
  • 从 12c 开始,您可以使用 with function 将列列表构建为带有链接答案中的代码的单个 varchar2/clob,然后使用例如 xmltable 将其转换为行集。这不需要create 权限
  • 这不是 SQL(查询语言)的用途。好吧,astenix 仍然提到了一种从查询中获取列名的方法。然而,通常这不是 SQL 的任务。但是 DBMS 在与您的应用程序的对话中执行此操作。当您的应用向 DBMS 发送类似 SELECT Name, Age FROM MembersSELECT * FROM Members 的查询字符串时,DBMS 不仅会返回一个匿名结果表,还会包含列名。

标签: sql oracle plsql


【解决方案1】:

假设您有这样的查询:

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
;

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

然后您可以通过查询*_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 中,在数据库中“保存”查询的“通常”方式是创建视图。如果它们已经存在于您的数据库中,那么您所需要的就是我向您展示的最后一步。否则,“其他查询”(您需要为其查找列)是否首先来自?

【讨论】:

  • 解释清楚,谢谢!希望我能获得创建视图的权限。
【解决方案2】:

我会使用 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;
/

【讨论】:

    【解决方案3】:

    正如 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多