2012-01-09 13 views
5

Używam metody Spring JdbcUtils.extractDatabaseMetaData() do analizy bazy danych. Funkcja wywołuje wywołanie zwrotne i przekazuje obiekt o wartości DatabaseMetaData. Ten obiekt zapewnia getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern).JDBC ze Spring powolne metadane pobrać Oracle

Nazywam to w ten sposób getColumns("",TABLE_OWNER_USERNAME,null,null) i otrzymuję 400 kolumn. Są to dokładnie te wyniki, które chcę, ale prośba zajmuje ponad 1 minutę.

Czy mogę jakoś zoptymalizować to zapytanie, aby było szybkie? Pociągnięcie 400 rzędów powinno nastąpić w ciągu 1 sekundy, a nie jednej minuty.

EDIT: Nie podejrzewam, że część wiosenna jest wolna. Bliższa analiza wykazała, że ​​pobranie DatabaseMetaData trwa kilka sekund, ale wykonanie getColumns() trwa naprawdę długo.

+0

Czy jesteś czy to wina Springs? Czy możesz uruchomić tę samą kwerendę za pomocą zwykłego JDBC lub może bezpośrednio w konsoli bazy danych? –

+0

Nie sądzę, że to jest błąd sprężyny. Problem polega na tym, że nie jest to zapytanie jako takie. Sterownik JDBC ukrywa rzeczywiste zapytanie za pomocą metody 'getColumns()', więc nie widzę, co naprawdę się dzieje. –

+1

@FranzKafka - Czy możesz prześledzić SQL, który generuje Spring, aby dokładnie określić, które tabele słownika danych są sprawdzane? Czy zebrano statystyki w słowniku danych? –

Odpowiedz

2

Może to lepsze podejście do zapytania ALL_TAB_COLUMNS. Oto przykład:

public final List<Column> getColumnsByOwner(final String owner) { 
    final String sql = "SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH, " 
      + " DATA_PRECISION, DATA_SCALE, NULLABLE, DATA_DEFAULT" 
      + " FROM ALL_TAB_COLUMNS" 
      + " WHERE OWNER = ? ORDER BY COLUMN_ID"; 

    return jdbcTemplate.query(sql, 
      new Object[] { owner }, 
      new RowMapper<Column>() { 
       @Override 
       public Column mapRow(final ResultSet res, final int rowNum) 
         throws SQLException { 
        final Column reg = new Column(); 

        reg.setColumnName(res.getString("COLUMN_NAME")); 
        //Read other properties 
        reg.setNullable(res.getString("NULLABLE").equals("Y")); 
        return reg; 
       } 
      }); 
} 

Jeśli chcesz filtrować według tabeli, po prostu dodaj "AND TABLE_NAME =?" do sql i tableName jako kolejny parametr.

Mam nadzieję, że to pomaga.

2

Mając odwrotnej inżynierii faktycznie komunikacji między klientem a serwerem Mogę ujawnić, że DatabaseMetaData.getColumns Oracle() metody wysyła następujące zapytanie SQL (choć to może się zmienić z wersji sterowników ODBC i ustawień):

declare 
    in_owner varchar2(128); 
    in_name varchar2(128); 
    in_column varchar2(128); 
    xyzzy SYS_REFCURSOR; 
    begin 
    in_owner := :1; // Which resolves to the schema (user) name supplied 
    in_name := :2; // Which resolves to the table name supplied 
    in_column := :3; // Which gets set to '%'; 
    open xyzzy for 
    SELECT NULL AS table_cat, 
     t.owner AS table_schem, 
     t.table_name AS table_name, 
     t.column_name AS column_name, 
    DECODE( (SELECT a.typecode 
     FROM ALL_TYPES A 
     WHERE a.type_name = t.data_type), 
    'OBJECT', 2002, 
    'COLLECTION', 2003, 
    DECODE(substr(t.data_type, 1, 9), 
     'TIMESTAMP', 
     DECODE(substr(t.data_type, 10, 1), 
      '(', 
      DECODE(substr(t.data_type, 19, 5), 
       'LOCAL', -102, 'TIME ', -101, 93), 
      DECODE(substr(t.data_type, 16, 5), 
      'LOCAL', -102, 'TIME ', -101, 93)), 
     'INTERVAL ', 
     DECODE(substr(t.data_type, 10, 3), 
     'DAY', -104, 'YEA', -103), 
     DECODE(t.data_type, 
     'BINARY_DOUBLE', 101, 
     'BINARY_FLOAT', 100, 
     'BFILE', -13, 
     'BLOB', 2004, 
     'CHAR', 1, 
     'CLOB', 2005, 
     'COLLECTION', 2003, 
     'DATE', 93, 
     'FLOAT', 6, 
     'LONG', -1, 
     'LONG RAW', -4, 
     'NCHAR', -15, 
     'NCLOB', 2011, 
     'NUMBER', 2, 
     'NVARCHAR', -9, 
     'NVARCHAR2', -9, 
     'OBJECT', 2002, 
     'OPAQUE/XMLTYPE', 2009, 
     'RAW', -3, 
     'REF', 2006, 
     'ROWID', -8, 
     'SQLXML', 2009, 
     'UROWI', -8, 
     'VARCHAR2', 12, 
     'VARRAY', 2003, 
     'XMLTYPE', 2009, 
     1111))) 
    AS data_type, 
     t.data_type AS type_name, 
     DECODE (t.data_precision, null, 
      DECODE(t.data_type, 'NUMBER', 
       DECODE(t.data_scale, null, 0 , 38), 
        DECODE (t.data_type, 'CHAR', t.char_length, 'VARCHAR', t.char_length, 'VARCHAR2', t.char_length, 'NVARCHAR2', t.char_length, 'NCHAR', t.char_length, 'NUMBER', 0, t.data_length)), t.data_precision) 
     AS column_size, 
     0 AS buffer_length, 
     DECODE (t.data_type, 'NUMBER', DECODE(t.data_precision, null, DECODE(t.data_scale, null, -127 , t.data_scale), t.data_scale), t.data_scale) AS decimal_digits, 
     10 AS num_prec_radix, 
     DECODE (t.nullable, 'N', 0, 1) AS nullable, 
     NULL AS remarks, 
     t.data_default AS column_def, 
     0 AS sql_data_type, 
     0 AS sql_datetime_sub, 
     t.data_length AS char_octet_length, 
     t.column_id AS ordinal_position, 
     DECODE (t.nullable, 'N', 'NO', 'YES') AS is_nullable, 
     null as SCOPE_CATALOG, 
     null as SCOPE_SCHEMA, 
     null as SCOPE_TABLE, 
     null as SOURCE_DATA_TYPE, 
     'NO' as IS_AUTOINCREMENT, 
     t.virtual_column as IS_GENERATEDCOLUMN 
    FROM all_tab_cols t 
    WHERE t.owner LIKE in_owner ESCAPE '/' 
    AND t.table_name LIKE in_name ESCAPE '/' 
    AND t.column_name LIKE in_column ESCAPE '/' 
    AND t.user_generated = 'YES' 
    ORDER BY table_schem, table_name, ordinal_position; 
    end; 

Można docenić, dlaczego może to być nieco powolne, zwłaszcza, że ​​tabele ALL_TAB_COLS i ALL_TYPES mogą mieć po 1000 rekordów. Niemniej jednak, podczas gdy Oracle stara się wykonać pierwszą inwokację (minuty), kolejne połączenia zwracają wyniki niemal natychmiast. Jest to klasyczny problem z wydajnością łączenia tabel, w którym mimo że wymagany jest podzbiór danych, silnik łączy się z całym zestawem danych przed obliczeniem i dostarczeniem wymaganego podzbioru. Następnie buforowanie danych/wyników działa w celu poprawy wydajności kolejnych zapytań.

Lepszym rozwiązaniem może być użycie get_ddl() i przeanalizowanie zwróconej definicji tabeli zgodnie z this thread.

Alternatywnie może być w stanie do kwerendy metadane na stole szybciej wykonując obojętne zapytanie następnie używając resultSetMetadata następująco (Uwaga: Metadane Uwagi kolumn nie mogą być natychmiast dostępne):

ResultSet rs = connection.CreateStatement.executeQuery("SELECT * from MyTable WHERE 1=0"); 
    ResultSetMetaData md = rs.getMetaData(); 
    for (int ix = 1; ix <= md.getColumnCount(); ix++) 
    { 
     int colSize = md.getPrecision(ix); 
     String nativeType = md.getColumnTypeName(ix); 
     int jdbcType = md.getColumnType(ix); 
     // and so on.... 
    } 
Powiązane problemy