默认情况下,sqlplus会将列名截断为基础数据类型的长度。我们数据库中的许多列名都以表名为前缀,因此在截断时看起来相同。
我需要在锁定的生产环境中为远程DBA指定select *查询,并拖回假脱机结果以进行诊断。列太多,无法指定单个列格式。 sqlplus是否提供统一阻止列名截断的任何选项?
(我使用SET MARKUP HTML ON,虽然我可以使用其他一些模态,csv等,只要它产生无缩写输出。)
答案 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,因为它也考虑了别名,它只获得您查询的列。
编辑:仅使用“列标题”不起作用。它仍然需要使用“列格式”语句。所以,请忽略我之前的回答。