ナクナイ

勉強用の備忘録

[mysql] INTO OUTFILE 構文以外でクエリ結果を CSV 出力

mysql でクエリ結果を CSV に出力したい場面に出くわしました。
一応、下記のようなクエリを発行すれば出力は可能です。

SELECT
'カラム名1',
'カラム名2',
'カラム名3',
...
FROM テーブル名
UNION 
SELECT *
INTO OUTFILE '保存したいパス'
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY "\n"
FROM テーブル名
WHERE 1

それぞれ下記の様な意味です。

INTO OUTFILE: 出力先パス
FIELDS TERMINATED BY: 区切り文字
OPTIONALLY ENCLOSED BY: 囲い文字
LINES TERMINATED BY: 行が変わる際の文字

UNION 句以前の SELECT は、カラム名を1行目に挿入するために入れています。ちょっとスマートじゃない感じ。

このクエリだと UNION の関係上件数が多くなると激重になってしまいますし、mysql が稼動しているサーバーにファイルが生成されることになるので、ローカル環境に移動したいときとかに面倒です。ローカル環境に mysql コマンドがあれば、下記のように実行することで移動する手間もかからないしクエリも重くなりにくく、上記と同じ結果が得ることが出来ます。

$ /usr/local/mysql/bin/mysql -h ホスト名 -u ユーザー名 -pパスワード DB名 -e "SELECT * FROM テーブル名 WHERE 1" | awk '{gsub("\"","\\\""); $0="\""$0"\""; gsub("\t", "\",\""); print $0}' > '出力先'

awk はあまり使ったことないので調べながらやってみました。ひとつひとつの処理を説明するとこんな感じ。

gsub("\"","\\\"");

区切り文字を " にするので、まずは値に " が含まれるものをあらかじめエスケープしておく。

$0="\""$0"\"";

次に、行頭と行末に " を加える。

gsub("\t", "\",\"");

最後に、タブ文字を "," に置換。


クエリは軽くなったので mysql への負荷は減りましたが、当然ファイル出力や awk などの負荷は、実行するローカル環境にかかるので留意が必要です。とはいえ、mysql よりローカル環境に負荷をかけたほうがベターなケースがほとんどだと思います。

ただ、ちょっと awk にがんばらせすぎかなとも思っていて、たとえば SELECT で取得してきた際に始めから " 付きでデータを取ってこれたり出来ないかなーとか思い、
文字列を扱う関数:MySQL関数リファレンス
↑ここらへんを調べたのですがなかなかいい方法は見つからず。。なにかもっとスマートな方法があれば教えてください。


ちなみに、最初は下記のように awk ではなく sed でやろうとしてたんですが、タブ文字置換のところで正しく動作しなかったので、awk に切り替えました。

$ /usr/local/mysql/bin/mysql -h ホスト名 -u ユーザー名 -pパスワード DB名 -e "SELECT * FROM テーブル名 WHERE 1" | sed -e 's/\t/,/g

あまり深追いはしてませんが、2 バイト文字あたりでこける場合があったような気がしないでもない・・・



おわり

異なるサーバー間の diff を求める

こんな感じで、ローカルとリモートサーバー先との diff を求めることが出来る。

ssh [リモート先IP] cat /home/tamulapin/telecaster.txt |diff - /home/tamulapin/telecaster.txt


もちろん、オプションも使える。-w はスペース無視、-B は空行無視のオプション。個人的によく使う便利オプションです。

ssh [リモート先IP] cat /home/tamulapin/telecaster.txt |diff -wB - /home/tamulapin/telecaster.txt


さらに、bashzsh には「Process Substitution (プロセス置換) 」という機能があり、これを利用してリモート同士のサーバーで diff を求めることも出来る。

diff <(ssh [リモート先IP1] cat /home/tamulapin/stratocaster.txt) <(ssh [リモート先IP2] cat /home/tamulapin/stratocaster.txt) 

参考:2013-08-15 - 双六工場日誌
参考:漢のzsh (12) 一時ファイルはもういらない - プロセス置換 | マイナビニュース



おしまい。

月末に cron を実行

月末のみ cron を実行したい!そんなときの crontab の書き方はこちら。この例では、毎月月末の朝10時に aiueo.pl を実行する記述。

0 10 * * * /usr/bin/test $( date -d '+1 day' +\%d ) -eq 1 && /home/tamulapin/aiueo.pl > /dev/null 2>&1

まず、ひとつずつ備忘。

test コマンド
test コマンドは、条件式の終了ステータス 0 か 1 のみを返します。

test 123 -eq 123 && ls

こうすれば ls は実行されます。

test 123 -eq 1234 && ls

こうすれば、ls は実行されません。


date コマンド
date コマンドは、時刻の表示や設定を行います。

$ date -d '+1 day'
2014年 12月 26日 金曜日 17:36:21 JST

こんな感じで、1日後の日付が返ってきます。

$ date -d '+1 day' +%d
26

これで、日付だけが返却されます。


改めて crontab を確認

0 10 * * * /usr/bin/test $( date -d '+1 day' +\%d ) -eq 1 && /home/tamulapin/aiueo.pl > /dev/null 2>&1

これはつまり、次の日が1日 (=今日が月末日) だったら aiueo.pl を実行する、ということになります。

ちなみに +%d を +\%d としているのは、エスケープなしで crontab に設定して実行したところ /var/log/cron に

crond[29480]: (tamulapin) CMD (/usr/bin/test $( date -d '+1 day' +)

みたいなエラーが出て、正しく実行されなかったため。

エラーを見ると、どうやら day 以降が切れてしまって crontab の記述が正しく認識されないことが原因みたい。ためしに手動で実行してみると、エスケープせずとも問題なく実行できました。

手動実行したシェルが zsh で crontab のシェルが bash だったので、そこらへんが原因なのかなと思い、ためしに自身のシェルを bash にして実行してみたけど、エスケープせずとも変わらず実行できたので、ちょっと謎。

おしまい

[サーバー構築] screen インストールと .screenrc の設定

便利コマンド screen をインストール。

screen の一番の利点は、たとえば作業中ローカル PC が落ちてリモート接続先のサーバーと通信が切れても、リタッチしてすぐに作業再開できることだと思う。

まずはインストール

# yum install screen


次に、screen インストール時に作成される screen 設定ファイル (~/.screenrc) を編集して使いやすいようにカスタマイズしていく。
まず、ぼくの .screenrc はこんなかんじ。

escape ^Ta

vbell on

autodetach on

startup_message off

shell -/bin/zsh

bind k
bind ^k
bind .
bind ^\
bind \\
bind ^h
bind h
bind 'K' kill
bind 'I' login on
bind 'O' login off
bind '}' history

bind 0 only
bind 1 remove

bindkey -k k1 select 1
termcapinfo xterm* 'k1=\E[11~:k2=\E[12~:k3=\E[13~:k4=\E[14~'
bindkey -k k2 select 2
bindkey -k k3 select 3
bindkey -k k4 select 4
bindkey -k k5 select 5
bindkey -k k6 select 6
bindkey -k k7 select 7
bindkey -k k8 select 8
bindkey -k k9 select 9
bindkey -k k; select 10
bindkey -k F1 select 11
bindkey -k F2 select 12

bind ^] paste [.]

caption always "%{= wk} %-w%{=bu dr} %n %t %{-}%+w%-22=%{= .k}[%Y-%m-%d %c:%s]"

上から順番に、いくつかピックアップして備忘。

escape ^Ta

これは screen のコマンドを実行する際にトリガーとなるキーの設定。
デフォルトは Ctrl-a となっているが、他の操作とかぶってしまうので Ctrl-t に変更。

bind 0 only

Ctrl-t でエスケープした後、0 を押せばスプリットしたリージョンを全て削除される設定

bind 1 remove

こちらはスプリットしたリージョンのうち、アクティブになっているものを削除する設定。
たくさんのウインドウを何分割もしていると整理したくなるので便利。

bindkey -k k1 select 1
termcapinfo xterm* 'k1=\E[11~:k2=\E[12~:k3=\E[13~:k4=\E[14~'
bindkey -k k2 select 2
bindkey -k k3 select 3
bindkey -k k4 select 4
bindkey -k k5 select 5
bindkey -k k6 select 6
bindkey -k k7 select 7
bindkey -k k8 select 8
bindkey -k k9 select 9
bindkey -k k; select 10
bindkey -k F1 select 11
bindkey -k F2 select 12

これらは、ファンクションキーを押せばリージョンに指定したリージョンに移動する設定。
たとえば F5 を押せば 5 のリージョンに移動。
参考screenでファンクションキーを使う|とある技術者の日記


caption always "%{= wk} %-w%{=bu dr} %n %t %{-}%+w%-22=%{= .k}[%Y-%m-%d %c:%s]"

これはウインドウ下部に表示されるキャプションの設定。色々調べて自分なりにカスタマイズ。ちなみにそれぞれ下記のような意味。

caption (キャプション)
always (常時設定)
%{= wk} (w:背景white / k:文字black)
%-w (現在のウィンドウより前のウインドウ番号とウインドウタイトル)
%{=bu db} (b:bold / u:underline / d:背景default / b:文字blue)
%n (現在のウインドウ番号)
%t (現在のウインドウタイトル)
%{-} (直前のカラー設定を復元)
%+w (現在のウィンドウより後ろのウインドウ番号とウインドウタイトル)
%-22 (右端の22文字目から描画開始)
%{= .k} (.:背景変更なし / k:文字black)
%Y (年)
%m (月)
%d (日)
%c (現在時刻を24時表示)
%s (秒数)


起動するとこんな感じ。
f:id:tamulapin:20141225163855p:plain

%lロードアベレージも表示できるっぽくてちょっと気になったけど、screen を起動したサーバーのロードが見れるだけであまり役立ちそうになかったので記述見送り。


ちなみに、下部キャプションのリージョン番号の横に、最後に実行したコマンドもしくはカレントディレクトリ名を表示するようにしています。これは .zshrc に追記して設定しました(使用しているシェルが zsh の場合)。その記述部分だけ抜き出すとこんな感じ。
参考メモの日々(2008-03-31)

case "${TERM}" in screen)                   
    preexec() {                             
        echo -ne "\ek<${1%% *}>\e\\"        
    }                                       
    #precmd() {                             
    chpwd() {                               
        echo -ne "\ek$(basename $(pwd))\e\\"
    }                                       
esac

おしまい。

[サーバー構築] zsh インストールと .zshrc の設定

sakura の vps を契約して、サーバー構築することにした。
シェルは zsh 派なので、zsh をインストールした手順をメモ。


まずは、ユーザーの追加

# useradd tamulapin
# passwd tamulapin

tamulapin ユーザーを sudoers ファイルに記述して、sudo 権限を与える

# visudo
tamulapin  ALL=(ALL)
→追記


ここから zsh のインストール

# yum install ncurses-devel
# cd /usr/local/src
# wget http://sourceforge.net/projects/zsh/files/zsh-dev/4.3.12/zsh-4.3.12.tar.gz
# tar xvzf zsh-4.3.12.tar.gz
# cd zsh-4.3.12
# ./configure
# make
# make install


.zshrc (zsh 設定ファイル) を設定

まずログディレクトリを事前に作成しておく。

# mkdir /root/history_logs/
$ mkdir /home/tamulapin/history_logs/

root ユーザー、tamulapin ユーザーの .zshrc は色々参考にして下記のように設定

export LANG=ja_JP.UTF-8

# 補完機能の強化
autoload -U compinit
compinit

## PATH の設定
PATH=$PATH:/usr/local/bin # パスの追加

##---- ヒストリファイル系設定 -------------------------------##
HISTFILE=$HOME/.zsh-history
HISTSIZE=100000
SAVEHIST=100000

# 直前と同じコマンドをヒストリに追加しない
setopt hist_ignore_dups

# zsh の開始, 終了時刻をヒストリファイルに書き込む
setopt extended_history

# ヒストリを共有
setopt share_history

# 保存ファイルの書式
export HISTFILE=~/history_logs/history_`date +%y%m%d_%H%M`
##-----------------------------------------------------------##

# 出力の文字列末尾に改行コードが無い場合でも表示
unsetopt promptcr

# 色を使う
setopt prompt_subst

# プロンプトのカラー表示を有効
autoload -U colors
colors

# ビープを鳴らさない
setopt nobeep

# 内部コマンド jobs の出力をデフォルトで jobs -l にする
setopt long_list_jobs

# 補完候補一覧でファイルの種別をマーク表示
setopt list_types

# サスペンド中のプロセスと同じコマンド名を実行した場合はリジューム
setopt auto_resume

# 補完候補を一覧表示
setopt auto_list

# cd 時に自動で push
setopt autopushd

# 同じディレクトリを pushd しない
setopt pushd_ignore_dups

# ファイル名で #, ~, ^ の 3 文字を正規表現として扱う
setopt extended_glob

# TAB で順に補完候補を切り替える
setopt auto_menu

# =command を command のパス名に展開する
setopt equals

# --prefix=/usr などの = 以降も補完
setopt magic_equal_subst

# ヒストリを呼び出してから実行する間に一旦編集
setopt hist_verify

# ファイル名の展開で辞書順ではなく数値的にソート
setopt numeric_glob_sort

# 出力時8ビットを通す
setopt print_eight_bit

## 補完候補のカーソル選択を有効に
zstyle ':completion:*:default' menu select=1

## 補完候補の色づけ
zstyle ':completion:*:default' list-colors ${(s.:.)LS_COLORS}

# ディレクトリ名だけで cd
setopt auto_cd

# カッコの対応などを自動的に補完
setopt auto_param_keys

# ディレクトリ名の補完で末尾の / を自動的に付加し、次の補完に備える
setopt auto_param_slash

# Ctrl+wで/までの文字列を削除
WORDCHARS='*?_-.[]~=&;!#$%^(){}<>'

# 候補をタブで選択
zstyle ':completion:*default' menu select=1
# 補完で大文字と小文字を区別しないようにしたい(大文字は展開しない)

zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'

#---- svn commit で vi 起動 ----#
export SVN_EDITOR=vi

#---- alias 設定 ------#
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias sl='ls --color=tty'
alias ls='ls --color=tty'
alias ll='ls -al --color=tty'
alias vi=vim

#---- プロンプト設定 ------#
# 左プロンプト
PROMPT='[%m]${WINDOW:+"[$WINDOW]"}$ '

#PROMPT='%{32+$RANDOM % 5]m%}%U%B$HOST'"{`whoami`}%b%%%{%}%u " # prompt_subst が必要
# 右プロンプト
RPROMPT=$'%{\e[33m%}[%~]%{\e[m%}%*'

ログインシェルを zsh に変更

# which zsh
# chsh -s /usr/local/bin/zsh root
# chsh -s /usr/local/bin/zsh tamulapin

これを打ったら、

警告: "/usr/local/bin/zsh" は /etc/shells リストの中にありません

と怒られたられたので、
/etc/shells に /usr/local/bin/zsh を追記して改めて実行。

# cat /ets/passwd

ログインシェルが変わったことを確認

終わり。

[Xcode] Debug, Release 以外の環境を追加し、各環境ごとに処理を変更

cocos2d-x を使用したアプリ開発で、API 通信先を環境ごとによって変えたい場面に直面した。
開発環境、ステージング(スタブ)環境、リリース環境と分けたかったのだが、調べたところ Xcode のデフォルトではデバッグ環境と本番環境しか用意されていないらしい。


そこで自分でステージング環境を追加したので、その手順のメモ。
Xcode のバージョンは Version 5.1 (5B130a) です。


Build Settings にステージング環境を追加
  • Project Navigator の最上部にあるプロジェクトファイルを選択
  • 「PROJECT」ファイルのアイコンを選択し、「Info」のタブを選択
  • 「Configuration」内の + ボタンを押下し任意の名前で追加。この例では「Staging」として追加

f:id:tamulapin:20140611164807j:plain


追加したステージング環境に定義を追加

環境によって処理内容を変えたいので、ステージング環境であることを判別できる定義を追加します。

  • Project Navigator の最上部にあるプロジェクトファイルを選択
  • 「TARGETS」ファイルのアイコンを選択し、「Build Settings」のタブを選択
  • 「Preprocessor Macros」内に先ほど設定した「Staging」の部分をダブルクリックし、任意の変数を追加する。この例では「COCOS2D_STAGING=1」を追加

f:id:tamulapin:20140611164631j:plain


試してみる

API 接続先を、3 環境によって切り替えるコードを書く
※ COCOS2D_DEBUG=1 はもともと定義済みでした

#ifdef COCOS2D_DEBUG
    #define API_DOMAIN "http://debug_nari.com"
#elif COCOS2D_STAGING
    #define API_DOMAIN "http://staging_desu.com"
#else
    #define API_DOMAIN "http://honban_de_gozaru.com"
#endif


接続先を変更して実行してみたところ、問題なく API_DOMAIN の値が切り替えられていることを確認!

ちなみに接続先の切り替えは、
command + shift + < で、「Run [プロジェクト名]」を選択し、「Info」タブの「Build Configuration」のプルダウンで選択可能


参考
[Xcode] Build Settingsで、Debug, Release以外の設定を追加したい。 | Scenee's blog

XCodeでDebug時とRelease時にLog出力on/off切り替え | memo.donburiburi.com

[MySQL] DDL と トランザクション

MySQL では、DDL を操作するクエリへのトランザクションは無効。
暗黙的コミットが発生する。

参考
MySQL :: MySQL 5.1 リファレンスマニュアル :: 12.4.2 ロールバックできないステートメント
MySQL :: MySQL 5.1 リファレンスマニュアル :: 12.4.3 暗黙のコミットを引き起こすステートメント

おわり。