防止sqlplus截断列名,而不进行单独的列格式化

时间:2008-12-09 02:59:52

标签: oracle sqlplus

默认情况下,sqlplus会将列名截断为基础数据类型的长度。我们数据库中的许多列名都以表名为前缀,因此在截断时看起来相同。

我需要在锁定的生产环境中为远程DBA指定select *查询,并拖回假脱机结果以进行诊断。列太多,无法指定单个列格式。 sqlplus是否提供统一阻止列名截断的任何选项?

(我使用SET MARKUP HTML ON,虽然我可以使用其他一些模态,csv等,只要它产生无缩写输出。)

6 个答案:

答案 0 :(得分:3)

您可以尝试的一件事是动态生成“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。您需要添加解码,例如更改为DATE或NUMBER生成的“column”命令。

更新:事实证明原始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"

答案 1 :(得分:2)

这应该提供一些合理的格式。 当然,您可以自由地将您自己的首选项替换为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';

答案 2 :(得分:2)

所提议的解决方案都没有显示原始列名称,因此我不确定为什么人们会对它们进行投票...我确实有一个“hack”适用于原始请求,但我真的不喜欢喜欢它......那就是你实际上在每个列的查询中附加或前缀字符串,因此它们总是足够长的列标题。如果您处于HTML模式,就像海报一样,有一点额外的白色间距几乎没有什么害处...它当然会减慢您的查询速度......

e.g。

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;

答案 3 :(得分:1)

我认为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 )

答案 4 :(得分:1)

如果您不需要或不想要XML格式化,这有点像黑客,但您应该能够使用DBMS_XMLGEN package。此脚本应为您提供一个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

答案 5 :(得分:0)

我在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,因为它也考虑了别名,它只获得您查询的列。

编辑:仅使用“列标题”不起作用。它仍然需要使用“列格式”语句。所以,请忽略我之前的回答。