Как стать автором
Обновить

Комментарии 8

экспортируется в файл/кэш как набор Insert-скриптов

Не надо так делать, а то откат операции может получиться дольше, чем сама операция.

Экспортировать надо в виде CSV-файла и шаблона для SQL*Loader

Дарю код – как новичку на Хабре. Если разберётесь, плюс стопиццот вам в карму. Клянусь, что патча Бармина внутри нет :)

Hidden text
#!/bin/bash

# Usage: export.sh login/password@tnsalias table {table}
# Exports DDL statement, control file for SQL*Loader and the data itself

#=== SETTINGS ===

# quotation mark
_ENC=
#_ENC=\"
#_ENC=\~

# field separator (should be character or hexadecimal code with leading zero)
_SEP=09
#_SEP="|"

# max row count
#_ROWS=
#_ROWS=100000

# end-of-line (for character fields with linefeed)
_EOL=
#_EOL=@@@

RESERVED="'ACCESS','ADD','ALL','ALTER','AND','ANY','AS','ASC','AUDIT','BETWEEN','BY','CHAR','CHECK','CLUSTER','COLUMN','COMMENT','COMPRESS','CONNECT','CREATE','CURRENT','DATE','DECIMAL','DEFAULT','DELETE','DESC','DISTINCT','DROP','ELSE','EXCLUSIVE','EXISTS','FILE','FLOAT','FOR','FROM','GRANT','GROUP','HAVING','IDENTIFIED','IMMEDIATE','IN','INCREMENT','INDEX','INITIAL','INSERT','INTEGER','INTERSECT','INTO','IS','LEVEL','LIKE','LOCK','LONG','MAXEXTENTS','MINUS','MLSLABEL','MODE','MODIFY','NOAUDIT','NOCOMPRESS','NOT','NOWAIT','NULL','NUMBER','OF','OFFLINE','ON','ONLINE','OPTION','OR','ORDER','PCTFREE','PRIOR','PRIVILEGES','PUBLIC','RAW','RENAME','RESOURCE','REVOKE','ROW','ROWID','ROWNUM','ROWS','SELECT','SESSION','SET','SHARE','SIZE','SMALLINT','START','SUCCESSFUL','SYNONYM','SYSDATE','TABLE','THEN','TO','TRIGGER','UID','UNION','UNIQUE','UPDATE','USER','VALIDATE','VALUES','VARCHAR','VARCHAR2','VIEW','WHENEVER','WHERE','WITH'"
#=== SETTINGS ===



IFS="
"
login=$1
shift 1
__UNAME=`uname`


if [[ $_ENC != "" ]]
then
  sql_left_quote="'''${_ENC}''||replace('||"
  sql_right_quote="||',''${_ENC}'',''${_ENC}${_ENC}'')||''${_ENC}'''"
  ldr_quote=" enclosed by ''${_ENC}''"
fi
if [[ $_ROWS != "" ]]
then
  stop_cond=" where rownum<=${_ROWS}"
fi
if [[ "${_SEP#0}" != "${_SEP}" ]]
then
  sql_sep="'chr(to_number(''${_SEP}'',''XX''))'"
  ldr_sep="X'${_SEP}'"
else
  sql_sep="'''${_SEP}'''"
  ldr_sep="'${_SEP}'"
fi
if [[ $_EOL != "" ]]
then
  sql_eol="||'${_EOL}'"
  if [[ ${OSTYPE} == "cygwin" ]]
  then
    ldr_eol=" \"str '${_EOL}\\r\\n'\""
  else
    ldr_eol=" \"str '${_EOL}\\n'\""
  fi
fi

for t
do
  # table definition
  echo "create table $t (" >$t.sql
  sqlplus -s -L $login <<EOF >>$t.sql
set newpage none pagesize 0 linesize 128 underline off feedback off head off timing off tab off
select
  decode(column_id,1,'   ','  ,')||
  case when 
    upper(t.column_name)<>t.column_name or upper(t.column_name) in (${RESERVED})
    then '"'||t.column_name||'"'
    else lower(t.column_name)
  end || ' ' ||
  lower(t.data_type 
    ||decode(decode(t.data_type,'VARCHAR2','C','CHAR','C'),'C','('||t.data_length||')')
    ||decode(decode(t.data_type,'NUMBER','N','FLOAT','N'),'N',decode(nvl(t.data_precision,t.data_scale),null,null,'('||nvl(to_char(t.data_precision),'*')
    ||decode(t.data_scale,null,null,','||t.data_scale)||')'))) ||
  decode(t.nullable,'Y',null,'N',' not null') as field
from all_tab_columns t
where t.data_type in ('CHAR','VARCHAR2','DATE','NUMBER','FLOAT') 
  and (t.owner,t.table_name) in (
    select 
      min(owner) keep (dense_rank first order by priority),
      min(table_name) keep (dense_rank first order by priority)
    from
      (  
        select 
          1 as priority, owner, table_name
        from all_tables
        where
          owner=nvl(substr(upper('$t'),1,instr(upper('$t'),'.',1)-1),user)
          and table_name=substr(upper('$t'),instr(upper('$t'),'.',1)+1)
        union all
        select 
          2 as priority, owner, view_name
        from all_views
        where
          owner=nvl(substr(upper('$t'),1,instr(upper('$t'),'.',1)-1),user)
          and view_name=substr(upper('$t'),instr(upper('$t'),'.',1)+1)
        union all
        select case owner when user then 3 else 4 end as priority, table_owner, table_name 
        from all_synonyms 
        where synonym_name=upper('$t') and owner in (user,'PUBLIC')
      )
    )
order by column_id;
EOF
  echo ");" >>$t.sql

  #SQL*Loader control file
  cat <<EOF >$t.ctl
options (DIRECT=TRUE, STREAMSIZE=8388608, COLUMNARRAYROWS=16384)
LOAD DATA
CHARACTERSET ${NLS_LANG##*.} 
INFILE '$t.txt'${ldr_eol} BADFILE '$t.bad'
TRUNCATE INTO TABLE $t fields terminated by ${ldr_sep} trailing nullcols (
EOF
  sqlplus -s -L $login <<EOF >>$t.ctl
set newpage none pagesize 0 linesize 128 underline off feedback off head off timing off tab off
select
  decode(column_id,1,'   ','  ,')||
  decode(upper(t.column_name),t.column_name,lower(t.column_name),'"'||t.column_name||'"') || ' ' ||
  case 
    when data_type in ('CHAR','VARCHAR2') then 
      case when data_length>255 then 'char('||data_length||')' else 'char' end ||'${ldr_quote}'
    when data_type='NUMBER' then 
      case 
        when data_scale>=0 then 'integer external'
        else 'float external'
      end
    when data_type='FLOAT' then 'float external'
    when data_type='DATE' then 'date ''dd.mm.yyyy hh24:mi:ss'''
  end
from all_tab_columns t
where t.data_type in ('CHAR','VARCHAR2','DATE','NUMBER','FLOAT') 
  and (t.owner,t.table_name) in (
    select 
      min(owner) keep (dense_rank first order by priority),
      min(table_name) keep (dense_rank first order by priority)
    from
      (  
        select 
          1 as priority, owner, table_name
        from all_tables
        where
          owner=nvl(substr(upper('$t'),1,instr(upper('$t'),'.',1)-1),user)
          and table_name=substr(upper('$t'),instr(upper('$t'),'.',1)+1)
        union all
        select 
          2 as priority, owner, view_name
        from all_views
        where
          owner=nvl(substr(upper('$t'),1,instr(upper('$t'),'.',1)-1),user)
          and view_name=substr(upper('$t'),instr(upper('$t'),'.',1)+1)
        union all
        select case owner when user then 3 else 4 end as priority, table_owner, table_name 
        from all_synonyms 
        where synonym_name=upper('$t') and owner in (user,'PUBLIC')
      )
    )
order by column_id;
EOF
  echo ")" >>$t.ctl

  #data
  unset sel
  for c in `sqlplus -s -L $login <<EOF
set newpage none pagesize 0 linesize 128 underline off feedback off head off timing off tab off
select
  decode(column_id,1,null,'||'||${sql_sep}||'||')||
  case 
    when data_type='CHAR' or data_type='VARCHAR2' then ${sql_left_quote}case when upper(t.column_name)<>t.column_name or upper(t.column_name) in (${RESERVED}) then '"'||t.column_name||'"' else lower(t.column_name) end${sql_right_quote}
    when data_type='NUMBER' or data_type='FLOAT' then 'to_char('||case when upper(t.column_name)<>t.column_name or upper(t.column_name) in (${RESERVED}) then '"'||t.column_name||'"' else lower(t.column_name) end||',''TM'',''NLS_NUMERIC_CHARACTERS = ''''. '''''')'
    when data_type='DATE' then 'to_char('||case when upper(t.column_name)<>t.column_name or upper(t.column_name) in (${RESERVED}) then '"'||t.column_name||'"' else lower(t.column_name) end||',''dd.mm.yyyy hh24:mi:ss'')'
  end
from all_tab_columns t
where t.data_type in ('CHAR','VARCHAR2','DATE','NUMBER','FLOAT') 
  and (t.owner,t.table_name) in (
    select 
      min(owner) keep (dense_rank first order by priority),
      min(table_name) keep (dense_rank first order by priority)
    from
      (  
        select 
          1 as priority, owner, table_name
        from all_tables
        where
          owner=nvl(substr(upper('$t'),1,instr(upper('$t'),'.',1)-1),user)
          and table_name=substr(upper('$t'),instr(upper('$t'),'.',1)+1)
        union all
        select 
          2 as priority, owner, view_name
        from all_views
        where
          owner=nvl(substr(upper('$t'),1,instr(upper('$t'),'.',1)-1),user)
          and view_name=substr(upper('$t'),instr(upper('$t'),'.',1)+1)
        union all
        select case owner when user then 3 else 4 end as priority, table_owner, table_name 
        from all_synonyms 
        where synonym_name=upper('$t') and owner in (user,'PUBLIC')
      )
    )
order by column_id;
EOF
`
  do
    sel=$sel$c"\n"
  done

#  sqlplus -s -L $login <<EOF >$t.txt
#  sqlplus -s -L $login <<EOF | gzip >$t.txt.gz
#  cat <<EOF >$t.txt
  sqlplus -s -L $login <<EOF | gzip >$t.txt.gz
set newpage none pagesize 0 linesize 16384 underline off feedback off head off timing off tab off
set recsep off
set arraysize 4096
select
`if [[ ${__UNAME} != AIX ]]; then echo -e ${sel}; else echo ${sel}; fi`${sql_eol}
from
${t}${stop_cond};
EOF

done

Спасибо за задачу со звездочкой )
Уже есть мысли, куда скрипт имплементировать.

sqlplus с версии 12.2 поддерживает csv напрямую, поэтому столько приседаний уже не нужно. А у SQLCl
(как и Oracle SQL Developer, т.к. у них одна кодовая база) еще больше фич для разных выгрузок. Только нужно заметить, что есть ограничения на типы данных (CLOB, BLOB, XMLType, Long, etc...).

Небольшое Code Review, с вашего позволения :)

Я б посоветовал рассмотреть возможность написать консольный скрипт не на bash, а на Python, PHP, node.js или любом другом скриптовом языке и вызывать из консоли таким же образом, как и bash script.

Но почему? На мой взгляд, в этом скрипте нет ничего нетривиального.

Если присмотреться, то увидите набор костылей, которые в более развитом языке программирования не нужны.

Если нужно будет усложнить логику, а ещё и не автору скрипта, то это будет тот ещё challenge и вспоминать вас будут не всегда добрыми словами. Не видел в описаниях вакансий "владение bash script".

Такой скрипт не задебажишь. И, впринципе, мы себя сильно ограничиваем.

Когда я писал этот скрипт, питона в таких количествах ещё не было. Во всяком случае, на промышленных Sun’ах о таком и не слышали. Мало того, даже bash-то там поначалу не очень разрешали, только ksh...

sqlplus с версии 12.2 поддерживает markup csv

Зарегистрируйтесь на Хабре, чтобы оставить комментарий