oracle 防止sqlplus截断列名,而不设置单个列的格式

ssgvzors  于 2022-11-03  发布在  Oracle
关注(0)|答案(7)|浏览(197)

默认情况下,sqlplus会将列名截断为基础数据类型的长度。我们数据库中的许多列名都以表名为前缀,因此截断后看起来是相同的。
我需要在锁定的生产环境中向远程DBA指定select * 查询,并拖回假脱机的结果以进行诊断。列太多,无法指定单个列的格式。sqlplus是否提供了任何选项来统一消除列名截断?
(我使用的是SET MARKUP HTML ON,不过我也可以使用其他形式,如csv等,只要它能产生非缩写的输出即可。)

dy2hfwbg

dy2hfwbg1#

您可以尝试动态生成“column x format a20”命令,如下所示:

set termout off
set feedback off

spool t1.sql
select 'column ' || column_name || ' format a' || data_length
from all_tab_cols
where table_name='YOUR_TABLE'
/
spool off

@t1.sql
set pagesize 24
set heading on
spool result.txt
select * 
from  YOUR_TABLE;
and   rownum < 30;
spool off

请注意,此示例仅适用于VARCHAR2。例如,您需要添加decode,以更改为DATE或NUMBER生成的“column”命令。
UPDATE:原来的SQL并没有真正改变SQL*Plus的行为。我唯一能想到的是将字段名重命名为一个字符值A、B、C等,方法如下:

select 'column ' || column_name ||
       ' heading "' ||
       chr(ascii('A') - 1 + column_id) ||
       '"'
from all_tab_cols
where table_name='YOUR_TAB_NAME'

它将生成类似于以下内容的输出:

column DEPT_NO heading "A"
column NAME heading "B"
column SUPERVIS_ID heading "C"
column ADD_DATE heading "D"
column REPORT_TYPE heading "E"
dwbf0jvd

dwbf0jvd2#

没有一个建议的解决方案可以显示原始的列名,所以我不知道为什么人们会投票支持它们......我确实有一个“技巧”可以满足原始请求,但我真的不喜欢它......那就是实际上在每个列的查询上附加或前缀一个字符串,这样它们总是足够长,可以作为列标题。如果您是在HTML模式下,就像海报上说的那样,有一点额外的白色间隔几乎没有伤害...它当然会放慢你的查询有点...
例如:

SET ECHO OFF
SET PAGESIZE 32766
SET LINESIZE 32766
SET NUMW 20
SET VERIFY OFF
SET TERM OFF
SET UNDERLINE OFF
SET MARKUP HTML ON
SET PREFORMAT ON
SET WORD_WRAP ON
SET WRAP ON
SET ENTMAP ON
spool '/tmp/Example.html'
select 
   (s.ID||'                  ') AS ID,
   (s.ORDER_ID||'                  ') AS ORDER_ID,
   (s.ORDER_NUMBER||'                  ') AS ORDER_NUMBER,
   (s.CONTRACT_ID||'                  ') AS CONTRACT_ID,
   (s.CONTRACT_NUMBER||'                  ') AS CONTRACT_NUMBER,
   (s.CONTRACT_START_DATE||'                  ') AS CONTRACT_START_DATE,
   (s.CONTRACT_END_DATE||'                  ') AS CONTRACT_END_DATE,
   (s.CURRENCY_ISO_CODE||'                  ') AS CURRENCY_ISO_CODE,
from Example s
order  by s.order_number, s.contract_number;
spool off;

当然,您可以编写一个存储过程来做一些更好的事情,但对于这个简单的场景来说,这似乎有点大材小用。
这仍然不能满足最初的海报要求。因为它需要手动列在列上,而不使用select *。但至少当你愿意详细说明字段时,它是一个有效的解决方案。
然而,由于HTML中的字段太长并不存在问题,因此有一个相当简单的方法来修正Chris的解决方案,使其在本例中工作。那就是选择一个oracle允许的最大值。遗憾的是,这仍然不能真正适用于每个表的每个字段,除非您显式地为每个数据类型添加格式。该解决方案也不能适用于连接。因为不同的表可以使用相同的列名但不同的数据类型。

SET ECHO OFF
SET TERMOUT OFF
SET FEEDBACK OFF
SET PAGESIZE 32766
SET LINESIZE 32766
SET MARKUP HTML OFF
SET HEADING OFF

spool /tmp/columns_EXAMPLE.sql
select 'column ' || column_name || ' format A32766' 
from all_tab_cols
where data_type = 'VARCHAR2' and table_name = 'EXAMPLE'
/
spool off

SET HEADING ON
SET NUMW 40
SET VERIFY OFF
SET TERM OFF
SET UNDERLINE OFF
SET MARKUP HTML ON
SET PREFORMAT ON
SET WORD_WRAP ON
SET WRAP ON
SET ENTMAP ON
@/tmp/columns_EXAMPLE.sql
spool '/tmp/Example.html'
select *
from Example s
order  by s.order_number, s.contract_number;
spool off;
vsaztqbk

vsaztqbk3#

这应该提供了一些合理的格式。当然,您可以自由地用自己的首选项替换char列的最大宽度,以及如何处理LONG、RAW和LOB列。

SELECT 'COLUMN ' || column_name || ' FORMAT ' ||
       CASE
          WHEN data_type = 'DATE' THEN
           'A9'
          WHEN data_type LIKE '%CHAR%' THEN
           'A' ||
           TRIM(TO_CHAR(LEAST(GREATEST(LENGTH(column_name),
                        data_length), 40))) ||
           CASE
              WHEN data_length > 40 THEN
               ' TRUNC'
              ELSE
               NULL
           END
          WHEN data_type = 'NUMBER' THEN
           LPAD('0', GREATEST(LENGTH(column_name),
           NVL(data_precision, data_length)), '9') ||
           DECODE(data_scale, 0, NULL, NULL, NULL, '.' ||
           LPAD('0', data_scale, '0'))
          WHEN data_type IN ('RAW', 'LONG') THEN
           'A1 NOPRINT'
          WHEN data_type LIKE '%LOB' THEN
           'A1 NOPRINT'
          ELSE
           'A' || TRIM(TO_CHAR(GREATEST(LENGTH(column_name), data_length)))
       END AS format_cols
  FROM dba_tab_columns
 WHERE owner = 'SYS'
   AND table_name = 'DBA_TAB_COLUMNS';
iibxawm4

iibxawm44#

我不认为sqlplus提供了您所要求的功能。您可以使用某种脚本语言(如Perl或Python)自动格式化。换句话说,查询ALL_TAB_COLS视图以获得架构和表,然后使用format列属性动态创建脚本。当然,如果您有查询ALL_TAB_COLS视图(或其他等效视图)的权限。
这是我拼凑的一个快速概念验证:


# !/usr/bin/python

import sys
import cx_Oracle

response=raw_input("Enter schema.table_name:  ")
(schema, table) = response.split('.')
schema = schema.upper()
table = table.upper()
sqlstr = """select column_name,
                   data_type,
                   data_length
              from all_tab_cols
             where owner      = '%s'
               and table_name = '%s'""" % ( schema, table )

## open a connection to databases...

try:
    oracle = cx_Oracle.Connection( oracleLogin )
    oracle_cursor = oracle.cursor()

except cx_Oracle.DatabaseError, exc:
    print "Cannot connect to Oracle database as", oracleLogin
    print "Oracle Error %d:  %s" % ( exc.args[0].code, exc.args[0].message )
    sys.exit(1)

try:
    oracle_cursor.execute( sqlstr )

    # fetch resultset from cursor
    for column_name, data_type, data_length in oracle_cursor.fetchmany(256):
        data_length = data_length + 0
        if data_length < len(column_name):
            if data_type == "CHAR" or data_type == "VARCHAR2":
                print "column %s format a%d" % ( column_name.upper(), len(column_name) )
            else:
                print "-- Handle %s, %s, %d" % (column_name, data_type, data_length)

except cx_Oracle.DatabaseError, e:
    print "[Oracle Error %d: %s]:  %s" % (e.args[0].code, e.args[0].message, sqlstr)
    sys.exit(1)

try:
    oracle_cursor.close()
    oracle.close()
except cx_Oracle.DatabaseError, exc:
    print "Warning: Oracle Error %d:  %s" % ( exc.args[0].code, exc.args[0].message )

print "select *"
print "from %s.%s" % ( schema, table )
pqwbnv8z

pqwbnv8z5#

如果您不需要或不想使用XML格式,那么这可能有点麻烦,但是您应该能够使用DBMS_XMLGEN包。该脚本应该为您提供一个XML文件,用于任意查询,并将完整的列名作为标记名。

VARIABLE resultXML clob;
SET LONG 100000; -- Set to the maximum size of the XML you want to display (in bytes) 
SET PAGESIZE 0;

DECLARE
   qryCtx DBMS_XMLGEN.ctxHandle;
BEGIN
  qryCtx := dbms_xmlgen.newContext('SELECT * from scott.emp');

  -- now get the result
  :resultXML := DBMS_XMLGEN.getXML(qryCtx);

  --close context
  DBMS_XMLGEN.closeContext(qryCtx);
END;
/

print resultXML
ulmd4ohb

ulmd4ohb6#

我在VoraX中尝试实现这个特性时遇到了同样的问题。在下一个版本中,我考虑了以下解决方案:

set feedback off 
set serveroutput on
declare
  l_c number;
  l_col_cnt number;
  l_rec_tab DBMS_SQL.DESC_TAB2;
  l_col_metadata DBMS_SQL.DESC_REC2;
  l_col_num number;
begin
  l_c := dbms_sql.open_cursor;
  dbms_sql.parse(l_c, '<YOUR QUERY HERE>', DBMS_SQL.NATIVE);
  DBMS_SQL.DESCRIBE_COLUMNS2(l_c, l_col_cnt, l_rec_tab);
  for colidx in l_rec_tab.first .. l_rec_tab.last loop
    l_col_metadata := l_rec_tab(colidx);
    dbms_output.put_line('column ' || l_col_metadata.col_name || ' heading ' || l_col_metadata.col_name);
  end loop;
  DBMS_SQL.CLOSE_CURSOR(l_c);
end;

不用调整列的大小,只需要用你想要的列名来强制列标题的格式和其他东西。我认为同样的方法也适用于DBA_TAB_COLUMNS解决方案,但是我更喜欢DBMS_SQL,因为它也考虑别名,并且只得到你查询的列。

**EDIT:**只使用“列标题”是不行的,还需要使用“列格式”语句,所以请忽略我之前的回答。

vh0rcniy

vh0rcniy7#

如果你从一个表或视图中选择所有的列,这将是有效的a a归结为先写列,然后写数据。
下面假设使用制表符分隔。如果您需要逗号或其他分隔符,则需要在两个位置进行更改:colsep和下面的listagg线;逗号分隔符示例:新的COLSEP行将是:set colsep ',',新的列表行调整将用chr(44)替换chr(9)

/*google "sqlplus OPTION" to get the meaning */
    set colsep '    ' --literal TAB; can alter to taste
    set HEADING OFF
    set UNDERLINE OFF
    set PAGESIZE 50000
    set LINESIZE 32767
    set TERMOUT OFF
    set TRIMSPOOL ON
    set FEEDBACK OFF
    set WRAP OFF
    set NEWPAGE none

/*first, write the column names to the file*/
    spool "C:\yourPath\output.txt"
        select listagg(column_name, chr(9) ) within group (order by column_id)
        FROM dba_tab_columns
        WHERE owner = 'SomeOwner'
        AND table_name = upper('ViewOrTable_Name')
        ;

/*now append the data*/
    spool "C:\yourPath\output.txt" append

        select * from  SomeOwner.ViewOrTable_Name
        where 1 = 1
        ;

/*stop spooling*/
spool OFF

相关问题