2008-12-09 24 views
6

De manera predeterminada, sqlplus trunca los nombres de columna a la longitud del tipo de datos subyacente. Muchos de los nombres de columna en nuestra base de datos vienen precedidos del nombre de la tabla y, por lo tanto, se ven idénticos cuando se truncan.Impedir el truncamiento de sqlplus de los nombres de columna, sin formato de columna individual

Necesito especificar * consultas selectivas a DBA remotos en un entorno de producción bloqueado y arrastrar los resultados en spool para el diagnóstico. Hay demasiadas columnas para especificar el formato de columna individual. ¿Sqlplus ofrece alguna opción para derrotar uniformemente el truncamiento de nombre de columna?

(estoy usando el formato HTML SET ON, aunque podría utilizar alguna otra modalidad, csv, etc., siempre y cuando se produce la salida sin abreviar.)

Respuesta

1

No creo sqlplus ofrece la funcionalidad que está solicitando . Es posible que pueda automatizar el formateo, utilizando algún tipo de lenguaje de scripting como Perl o Python. En otras palabras, consulte la vista ALL_TAB_COLS para el esquema y la tabla, y luego cree la secuencia de comandos dinámicamente con un atributo de columna de formato. Esto solo funcionará, por supuesto, si tiene permiso para consultar la vista ALL_TAB_COLS (o algún otro equivalente).

Ésta es una prueba de concepto-rápida Tiré juntos:

#!/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) 
+0

Gracias por el código python, lamentablemente nuestro cliente prohíbe estrictamente la instalación de cualquier otro software en su entorno de producción. Ni siquiera grep o cola. Ugh. Entonces es sqlplus o fracaso. –

+0

Se encontró que esto fue útil por razones completamente no relacionadas. ¡Me alegra que lo hayas publicado! :-D –

3

Una cosa que puede probar es a los comandos "formato de columna x a20" generar dinámicamente. Algo como lo siguiente:

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 

Tenga en cuenta que esta muestra solo funcionará con VARCHAR2. Debería agregar decodificación, por ejemplo, para cambiar el comando "columna" generado para FECHAS o NÚMEROS.

ACTUALIZACIÓN: Resulta que el SQL original en realidad no cambia el comportamiento del SQL * Plus. Lo único que podía pensar es cambiar el nombre de los nombres de campo a un carácter valores A, B, C, etc .. de la siguiente manera:

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

Se generará la salida similar a:

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

Hmm, un poco pesado, pero supongo que eso podría funcionar. Excepto que no creo que te refieras con data_length. ¿No sería esa la longitud del tipo de datos subyacente y, por lo tanto, el comportamiento predeterminado de sqlplus? ¿Existe tal vez una operación de longitud de cadena que se podría aplicar al nombre de la columna? –

+0

Chris, tienes razón. Me di cuenta de que solo afecta el formato de los valores y no el formato del encabezado. La declaración de selección para un enfoque un poco diferente se encuentra en la actualización de la publicación. –

+0

Así que terminaría mostrándome columnas con la etiqueta A, B, C, etc. ... Pero mi objetivo es que la salida muestre los nombres de las columnas originales, sin tergiversar, de modo que pueda decir qué columna es cuál. –

2

Esto debería proporcionar un formato razonable. Puede, por supuesto, sustituir sus propias preferencias por el ancho máximo de las columnas char, y qué hacer con las columnas LONG, RAW y 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'; 
1

Es un poco de un truco si no necesita o desea el formato XML, pero usted debería ser capaz de utilizar el DBMS_XMLGEN package. Este script debe darle un archivo XML para una consulta arbitraria con el nombre completo de la columna como nombre de etiqueta.

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 
0

que tenían el mismo problema al intentar implementar esta característica en VoraX. En la próxima versión que tengo en mente la siguiente solución:

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; 

En lugar de ajustar tamaños de las columnas, el formato y las cosas simplemente hacer cumplir la cabecera de la columna con el nombre de la columna que desee. Creo que el mismo enfoque funcionaría también con la solución DBA_TAB_COLUMNS, pero prefiero el DBMS_SQL, ya que también considera los alias y solo obtiene las columnas que consulta.

EDITAR: El uso de solo "encabezado de columna" no funciona. Todavía es necesario usar declaraciones de "formato de columna". Por lo tanto, ignora mi respuesta anterior.

2

Ninguna de las soluciones propuestas funciona para mostrar los nombres de las columnas originales, así que no estoy seguro de por qué las personas las votan ... Tengo un "truco" que funciona para la solicitud original, pero realmente no me gusta 'Me gusta ... Es que realmente añades o prefijas una cadena a la consulta para cada columna, por lo que siempre son lo suficientemente largas para el encabezado de la columna. Si está en modo HTML, como en el póster, hay poco daño con un poco de espaciado blanco adicional ... Por supuesto, ralentizará su consulta ...

p.

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; 

Por supuesto que podría escribir un procedimiento almacenado para hacer algo mejor, pero realmente lo que parece ser una exageración para este escenario simple.

Esto todavía no cumple con la solicitud original de los carteles. En eso requiere listado manual en las columnas y no usar select *. Pero al menos es una solución que funciona cuando estás dispuesto a detallar los campos.

Sin embargo, como realmente no hay problema para tener demasiados campos en HTML, hay una manera bastante simple de solucionar la solución de Chris para que funcione en este ejemplo. Eso es solo elegir el valor máximo que el oráculo permitirá. Lamentablemente, esto aún no funcionará para CADA campo de cada tabla, a menos que agregue explícitamente el formato para cada tipo de datos. Esta solución tampoco funcionará para combinaciones, ya que diferentes tablas pueden usar el mismo nombre de columna pero un tipo de datos diferente.

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; 
Cuestiones relacionadas