パッケージの情報が古いんだよ。きっと。
M-x package-refresh-contents
パッケージの情報が古いんだよ。きっと。
M-x package-refresh-contents
mustache.elを見つけたので使ってみる。
Emacs Lisp自体ほとんどわかっていないので勉強もかねてやってみた。
*scratch*バッファだから、順番に”C-j”して実行して行けば、テンプレートの{{key}}で書かれたところを埋めたSQLが手に入る。
利用したライブラリは下記の3つ
*scratch*バッファ
;;テンプレートを作る (setq template " update users set user_id = '{{new-id}}' where user_id = '{{old-id}}'; delete from item_info where user_id = '{{old-id}}'; ") ;;ハッシュテーブルを作る (require 'ht) (setq context (list (ht ("new-id" "new-foo") ("old-id" "old-foo")) (ht ("new-id" "new-bar") ("old-id" "old-bar")) (ht ("new-id" "new-baz") ("old-id" "old-baz")))) ;;SQLをmustache.elで作る (require 'dash) (require 'mustache) (-map (lambda (m) (princ (mustache-render template m))) context) ;;再帰でもやってみる (defun my-render (tmpl lst) (cond ((null lst) (princ "commit;\n")) (t (progn (princ (mustache-render tmpl (car lst))) (my-render tmpl (cdr lst)))))) (my-render template context)
業務で単純なSQLをたくさん書かないと行けない時は、Excelを使って作っている。
ただ、Excelだと一対一のSQLは作りやすいけど、一つのキーで複数のテーブルを更新しに行く一対多のSQLは作りづらいから、こういうやり方も覚えておくと便利かもしれない。
READMEを読んだら、リストをループさせるテンプレートの書き方も載ってた。
当たり前だけど、公開されているドキュメントは一通り見ておいて損はないね。
(require 'mustache) mustache (require 'ht) ht (mustache-render " {{#hoge-list}} * {{hoge}} {{/hoge-list}} " (ht ("hoge-list" (list (ht ("hoge" "hogehoge1")) (ht ("hoge" "hogehoge2")))))) " * hogehoge1 * hogehoge2 "
大量に似たようなバッチを作らないといけなくなり、Node.jsでやるかっと思ったら、目の前のPCには入ってない。
WinPythonは入っていて、WinPythonにはpybars3もインストール済みなようなので、日本語の情報がないことに不安を感じつつも使ってみることにした。
入ってなければ
pip install pybars3
WindowsのCMDバッチだから、ファイルのエンコードは「cp932」
settings.json
[ { "file_name": "A.bat", "message": "AAAA", "users": [{"name": "山田"}, {"name": "鈴木"}] }, { "file_name": "B.bat", "message": "BBBB", "users": [{"name": "田中"}, {"name": "佐藤"}, {"name": "山本"}] } ]
template.hbs
@echo off echo. echo message: {{message}} echo. echo users: {{#for users}} echo * {{name}} {{/for}} exit /b
json_to_cmd.py
# -*- coding: utf-8 -*- import json from pybars import Compiler def main(): with open("settings.json", "r", encoding="cp932") as f: settings = json.load(f) with open("template.hbs", "r", encoding="cp932") as f: source = f.read() compiler = Compiler() template = compiler.compile(source) helpers = {"for": _for} for setting in settings: output = template(setting, helpers=helpers) with open(setting["file_name"], "w", encoding="cp932") as f: f.write(output) def _for(this, options, items): result = [] for thing in items: result.extend(options["fn"](thing)) return result if __name__ == "__main__": main()
Handlebarsそのものとは違うような気がしないでもないが、一応できた。
この感じでJSONとテンプレートを用意すれば、ちょっと違うけど別物っていうバッチを簡単に生み出せるから、楽ができそうだ。
やっぱり、退屈なことはPythonにやらせなきゃいけないからね。
Windowsでお手軽にPythonを始めるためにWinPythonをインストールした。
インストーラーはGithubからWinPython-32bit-3.5.4.1Qt5.exをダウンロードしてくる。
インストール先はわかりやすいように「C:\WinPython」にした。
その後、インストールしたフォルダの中に入っている「WinPython Control Panel.exe」をダブルクリックで起動し、
Advanced -> Register distribution...
をクリックして、なんかメッセージがでるので、「Yes」をクリック。
次に
コントロールパネル -> システム -> システムの詳細設定 -> 詳細設定(タブ) -> 環境変数
のシステム環境変数の「PATH」にすでに入っているものを消さないように以下を追加
;C:\WinPython;C:\WinPython\python-3.5.4\Scripts
;(セミコロン)区切りで2つのパスを追加する。
準備が整ったのでおもむろに「Jupyter Notebook.exe」をダブルクリックする。
黒い画面が表示され、おっきたかっと思ってしばらく待ってみると
Traceback (most recent call last): File "C:\WinPython\python-3.5.4\lib\runpy.py", line 193, in _run_module_as_main "__main__", mod_spec) File "C:\WinPython\python-3.5.4\lib\runpy.py", line 85, in _run_code exec(code, run_globals) File "C:\WinPython\python-3.5.4\scripts\jupyter-notebook.exe\__main__.py", line 5, in <module> File "C:\WinPython\python-3.5.4\lib\site-packages\notebook\notebookapp.py", line 42, in <module> from zmq.eventloop import ioloop File "C:\WinPython\python-3.5.4\lib\site-packages\zmq\__init__.py", line 34, in <module> from zmq import backend File "C:\WinPython\python-3.5.4\lib\site-packages\zmq\backend\__init__.py", line 40, in <module> reraise(*exc_info) File "C:\WinPython\python-3.5.4\lib\site-packages\zmq\utils\sixcerpt.py", line 34, in reraise raise value File "C:\WinPython\python-3.5.4\lib\site-packages\zmq\backend\__init__.py", line 27, in <module> _ns = select_backend(first) File "C:\WinPython\python-3.5.4\lib\site-packages\zmq\backend\select.py", line 26, in select_backe nd mod = __import__(name, fromlist=public_api) File "C:\WinPython\python-3.5.4\lib\site-packages\zmq\backend\cython\__init__.py", line 6, in <mod ule> from . import (constants, error, message, context, ImportError: DLL load failed: 指定されたモジュールが見つかりません。 C:\WinPython\notebooks>
なるほど。なんかDLLが足らないらしい、エラーメッセージをもとに「WinPython モジュールが見つかりません」とかググって見ても一致するものがでてこない。
そこで、一旦、WinPythonは忘れてモジュールが見つからんとエラー吐いている「zmq」を調べると、エラーメッセージそのものズバリではないもののバージョンアップの方法がわかった。
とりあえず、ものは試しなのでそこに書かれているコマンドを打ってバージョンアップしてみる。(コマンドを打つのはさっきのエラーメッセージが出たコマンドプロンプトの黒い画面のところでいい)
C:\WinPython\notebooks>pip install --upgrade pyzmq Collecting pyzmq Downloading pyzmq-17.0.0-cp35-cp35m-win32.whl (812kB) 100% |################################| 819kB 109kB/s Installing collected packages: pyzmq Found existing installation: pyzmq 16.0.3 Uninstalling pyzmq-16.0.3: Successfully uninstalled pyzmq-16.0.3 Successfully installed pyzmq-17.0.0 C:\WinPython\notebooks>
今、入ってたのが16.0.3で17.0.0に上がったわけか。
そのコマンドプロンプトは閉じて、もう一度、「Jupyter Notebook.exe」をダブルクリックする。
また黒い画面が起動して来て、
[I 10:50:30.048 NotebookApp] Writing notebook server cookie secret to C:\WinPython\settings\runtime\ notebook_cookie_secret [I 10:50:43.351 NotebookApp] JupyterLab alpha preview extension loaded from C:\WinPython\python-3.5. 4\lib\site-packages\jupyterlab [I 10:50:46.073 NotebookApp] Serving notebooks from local directory: C:\WinPython\notebooks [I 10:50:46.074 NotebookApp] 0 active kernels [I 10:50:46.075 NotebookApp] The Jupyter Notebook is running at: [I 10:50:46.075 NotebookApp] http://localhost:8888/?token=ac22eb5a9a5027ee205ec50bad0cf28f438a76898c c2883d [I 10:50:46.076 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to s kip confirmation). [C 10:50:46.085 NotebookApp] Copy/paste this URL into your browser when you connect for the first time, to login with a token: http://localhost:8888/?token=ac22eb5a9a5027ee205ec50bad0cf28f438a76898cc2883d
っと表示され、暫く待つと、IEも起動して使える状態になった。
まぁ使えるようにはなったけど、「Jupyter Notebook」ってのがなんなのかわかってないので、これから使い方を覚えないと行けないんだけどね。
ちょっとした確認用のSQLを実行するのにSQL Developerを立ち上げるのが重いというのもあり、よく実行するものはシェルスクリプトにしている。
今年に入ってから、いろいろな場所で同じようなSQLばかり書いているので、面倒だと感じ始めたからシェルスプリクトにする。
ただ、それぞれ別の場所で作るから毎回微妙に違うものを書いていたので、次に自分が作るとき用のメモ。
自分がよく作るのはGit for WindowsのBashでOracleに接続して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
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年になってもまだ自分の作ったプログラムが使われ続け、今もなおメンテナンスされているとは思わないだろう。
システムはいつの時も作者の予想よりも長く運用されるもの。
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年、正月三が日の深夜の思い出。