風待ち

プログラミングの勉強や日々の出来事のログなど

Oracleの簡単なSQLを実行するBashのシェルスクリプトを今年になってからいろんなところで書くので自分用のテンプレート

ちょっとした確認用のSQLを実行するのにSQL Developerを立ち上げるのが重いというのもあり、よく実行するものはシェルスクリプトにしている。

今年に入ってから、いろいろな場所で同じようなSQLばかり書いているので、面倒だと感じ始めたからシェルスプリクトにする。

ただ、それぞれ別の場所で作るから毎回微妙に違うものを書いていたので、次に自分が作るとき用のメモ。

自分がよく作るのはGit for WindowsBashOracleに接続してSQLを投げるシェルスプリクト。

そのまま実行するとSELECTするSQLが実行され、-aをつけるとINSERTするSQLを実行する。

作るもののほとんどがマスターに登録があるかないかを確認する系のSQLで、EXISTSで確認して無かったら追加する流れなので、値は指定しない事が多い。

#!/bin/bash
#
# 説明

readonly ORACLE_CONNECTION=USER/PASSWORD@SERVER:PORT/SID

function usage() {

cat <<_EOF_

Usage:
  $0 [-a]

Description:
  説明

Option:
  -a ADD DATA

_EOF_

exit 1
}

err() {
  echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $@" >&2
}

function select_data() {

sqlplus -s $ORACLE_CONNECTION << EOF

SELECT *
  FROM USERS A
 WHERE NOT EXISTS (
           SELECT * FROM USERS2 B WHERE A.ID = B.ID
       )
;

EXIT;

EOF

  if [ $? -ne 0 ]; then
    err "ERROR SELECT"
    return 1
  fi

  return 0
}

function insert_data() {

sqlplus -s $ORACLE_CONNECTION << EOF

INSERT INTO USERS(
    ID
  , NAME
)
SELECT A.ID
     , A.NAME
  FROM USERS2 A
 WHERE NOT EXISTS (
           SELECT * FROM USERS B WHERE A.ID = B.ID
       )
;

COMMIT;

EXIT;

EOF

  if [ $? -ne 0 ]; then
      err "ERROR INSERT"
      return 1
  fi

  return 0
}

FLAG_A=""
if [ "$OPTIND" = 1 ]; then
  while getopts abf:h OPT
  do
    case $OPT in
      a)
        FLAG_A="on"
        ;;
      h)
        usage
        ;;
      \?)
        echo "Try to enter the h option." 1>&2
        ;;
    esac
  done
else
  echo "No installed getoptions-command." 1>&2
  exit 1
fi

function main() {

  if [ -n "$FLAG_A" ]; then
    insert_data
  else
    select_data
  fi

  return 0
}

main

emacsでVB6のコードを見る。そのままでも見れるけど、やっぱり白黒ではあじけないのでvisual-basic-mode.elを入れてみる。

EmacsWikiからvisual-basic-mode.elをダウンロードする。

ダウンロードしたらどこでもいいけど「~/.emacs.d/elisp/visual-basic-mode.el」なんかに置く。

おもむろにinit.elに追記する。

(add-to-list 'load-path "~/.emacs.d/elisp")  ;ファイルを置いたところのパスを追加

;;; VB mode
(require 'visual-basic-mode)
(add-to-list 'auto-mode-alist
         '("\\.\\(frm\\|bas\\|cls\\|vbs\\)$" . visual-basic-mode))

これで、VB6のコードも少しは見る気になれるだろうか。

十数年前に、今、私の目の前にあるコードを書いた人は、まさか2018年になってもまだ自分の作ったプログラムが使われ続け、今もなおメンテナンスされているとは思わないだろう。

システムはいつの時も作者の予想よりも長く運用されるもの。

PL/SQLでSELECT INTOをした時にORA-01422が出る場合には、一度、引数とか変数の名前をカラム名と同じにしていないか確認しよう。

ORA-01422がでるのは、PL/SQLで変数にどこかのテーブルの値を検索してセットする時なんかに、戻ってくる値が一意にならないからエラーになる。

例えば下記のようにするとエラーになる。

CREATE OR REPLACE PROCEDURE SAMPLE (
 USER_ID IN NUMBER
) AS

  V_USER_NAME VARCHAR2(32 CHAR)

BEGIN

 SELECT USER_NAME INTO V_USER_NAME
    FROM USERS
  WHERE USER_ID = USER_ID;

  DBMS_OUTPUT.PUT_LINE('USER_NAME: ' || V_USER_NAME);

EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('ERROR CODE: ' || SQLCODE || ', MESSAGE: ' || SQLERRM);
  END;
END;

この場合、引数のUSER_IDとUSERSテーブルのカラム名のUSER_IDがまったく同じなので、エラーになる。

例えば、引数のUSER_IDを「SAMPLE」にするとWHERE句が「'SAMPLE' = 'SAMPLE'」となり、まったく同じ値で比較してしまう。

これだとUSERSテーブルのすべてが返ってきてしまうので、V_USER_NAMEに何をセットしたらいいかわからなくて、ORA-01422が出てしまう。

 SELECT USER_NAME INTO USER_NAME
    FROM USERS
  WHERE 'SAMPLE' = 'SAMPLE';

それを回避するには、単に引数名や変数名をカラム名と同じにしなければいい。

引数は「P_(アンダーバー)」で始め、変数は「V_(アンダーバー)」で始めれば区別もついてわかりやすいと思う。

上のコードの引数の部分を書き換えて、P_USER_IDにすれば問題なく動いてくれる。

CREATE OR REPLACE PROCEDURE SAMPLE (
 P_USER_ID IN NUMBER
) AS

  V_USER_NAME VARCHAR2(32 CHAR)

BEGIN

 SELECT USER_NAME INTO V_USER_NAME
    FROM USERS
  WHERE USER_ID = P_USER_ID;

  DBMS_OUTPUT.PUT_LINE('USER_NAME: ' || V_USER_NAME);

EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('ERROR CODE: ' || SQLCODE || ', MESSAGE: ' || SQLERRM);
  END;
END;

当初はエラーを出力していなかったので、なんでこけるのかわからず、無駄な時間を過ごした。

2018年、正月三が日の深夜の思い出。

WindowsのCMDを使ってCOPYコマンドでファイルを連結する

file1とfile2を連結してfile3を作る

CMD> COPY file1 + file2 file3

ただし、出来上がったfile3の最後に空白行が入る。 なので、例えば、CSVなんかを連結して、SQLLoderに食べさせる時にこれを使うと最後の行がエラーになるので注意が必要。

空白行を入れたくなければCOPYコマンドではなくTYPEコマンドとリダイレクトでやれば最後に空白なんて入らない。

CMD> TYPE file1 file2 > file3

ちょっとした計算をコマンドライン上でやりたい時のPowerShellとBashでのやり方の違いメモ

電卓を開くのが面倒な時に覚えておくと便利かも

PowerShellはそのまま

PS> 100 - 50
50

Bashは算術式展開?で書かないといけないみたい

$ echo $((100 - 50))
50

これだけ見るとPowerShellよりもBashの方が面倒か

ちなみに変数に入れるのは

PowerShell

PS> $x = 100
PS> $x
100

Bash

$ let x=100
$ echo $x
100

vimの:sort uをemacsではどうしたらいいか調べたのでメモ。

20年近く使ってきたvimからemacsに乗り換えようとしている。

けれど、ついつい使い慣れたvimを使ってしまう。

特に、急いでいる時はemacsで開いていても、vimで開き直してしまう事もある。

これは単に慣れの問題だと思うので少しづつでもemacsを使っていこうと思う。

ファイル内の行を並べ替えて重複を削除する

vim

:sort u

emacs ファイルの先頭にカーソルがいる事

M-x sort-lines
M-x delete-duplicate-lines

たまに乗り換えなくてもいいのではないかと思う時もあるけど、久しぶりに「ハッカーと画家」を読んだからさー。熱が冷めるまでは仕方ないね。

年末の大掃除のついでにシステムの監視用にログを取っているSQLiteのファイルも大きくなっていたので、こっちも掃除するかといらなくなったログデータを削除してもファイルサイズが小さくならなかったのでvacuumして小さくした。

複数のシステムを運用していると、バッチなんかで出力されるログが溜まっていく。

定期的に削除されるようにしているし、個別のファイルに吐かれているものだから検索も面倒なので使い勝手が悪い。

なので、そのログのファイル名と内容をけっこう雑にSQLiteに入れて、ログDBとしてあとでWebから検索できるようにしている。

ログ自体はテキトーに入れているが、バッチがエラーになった時なんかに、過去に同じエラーが出たことがないかを調べたり、他のバッチでも同じエラーがないか調べるのには当初考えていたよりも便利に使えている。

そのログDBも放置しているといつの間にかサーバーのディスクを圧迫する容量となっていたので、同僚たちが大掃除している中、一人、サーバー内の掃除をしていた。

最初は単純にDeleteしてしまえば、SQLiteのファイルサイズが小さくなるだろうと思っていたけど、そうはならなかった。 Oracleだと自動拡張とかを考えなければ最初から指定した容量を取ってるからそれと同じノリか。一度、伸びると縮まらないジャージのゴムを思い出した。

調べると「vacuum」ってすると小さくなるらしいので試した。

CMD> sqlite ファイル名.sqlite

sqlite> vacuum;

それほど時間がかかる事もなく、容量が小さくなった。

これで、サーバーの容量が足りなくなったと、夜中に呼び出されることもないだろう。