SQL,プログラミング

こんにちは、しきゆらです。

今回は、タイトルに有るようにSQLのJOINに対する個人的な誤解が解けたのでそれをメモしておきます。

前半は事の経緯なので、無視しても大丈夫です。

 

そして、先人である巨人のURLはこちら。

SELECT構文:JOINを使ってテーブルを結合する – SMART

http://rfs.jp/sb/sql/s03/03_3.html#JOIN

また、SQLだけ見せろ!という方向け

今回のSQLサンプル

http://sqlfiddle.com/#!9/0a510a/2/0

 

ことのはじめ

そもそも、私はこれまでSQLでテーブルを作るとき、それぞれのテーブルをどう関連付けて取得すればいいのかわからず1つのテーブルにすべてのデータをぶち込んでいました。

いわゆる非正規の状態。

 

しかし、それではいかん、ということで表を分けるようにしました。

(なお、この段階ではまだJOINと言うものを知りません。)

必要なデータの取得は、Ruby等のプログラムで各テーブルからデータを取得し、結合・ソート等をしていました。

 

しかし、表を結合して必要なデータを表現するためにJOINを用いればいいということを(最近)知り、

何でもかんでもJOINしていたわけです。

 

そんななか、本日ぶつかった壁のお話をしましょう。

 

ほんだい

何でもかんでもテーブルをJOINしてデータを取得していた私。

ぶつかった壁というのは、「JOINすると特定のデータが取得できない」という問題です。

 

例えば、以下のような何かのメモを表すデータベースを考えます。

メモを表現するitemテーブル

[table id=5 /]

 

メモのタグを表現するtagテーブル

[table id=6 /]

 

メモとタグを紐付けるtag_mapテーブル

[table id=7 /]

 

これらをもとに、考えてみます。

 

やりたいこと

3つの表をまとめて、メモの内容とタグ名を一覧で取得したい。

もしタグが付いていなくても、ないことも知りたい。

 

解決案

こんな感じのSQLを書いてみる。

SELECT content, GROUP_CONCAT(tag.name separator ',') as tags FROM item JOIN (tag JOIN tag_map USING(tag_id)) USING(item_id) GROUP BY item_id;

なお、GROUP_CONCATは引数のものをセパレータで区切って結合してくれる関数です。

12.19.1 GROUP BY (集約) 関数 – MySQLリファレンス

https://dev.mysql.com/doc/refman/5.6/ja/group-by-functions.html#function_group-concat

こうすると、「content」と「tags」をいっぺんに取ってこれます。

やったね!!

 

もんだい

全てにタグが設定されていれば問題なく取得できます。

しかし、新しく以下のようなデータが入ってくると話は変わってきます。

[table id=8 /]

 

上記レコートが追加された状態で同じようにSQLを実行します。

 

変化がない!?Why!?!?!?!!?!?

 

かいせつ

単なるJOINや「from hoge, fuga, …」としてテーブルを結合するものを内部結合(INNER JOIN)と言います。

これは、条件にあったもののみを列挙する結合。

今回であれば、「item_idとtag_idが紐づけされているもの」が条件となります。

 

しかし、itemテーブルに追加したものはまだタグと紐づけされていません。

そうなると、内部結合では条件にマッチしていないため取得されません。

 

 

 

解決方法

この問題を解決するには、外部結合(OUTER JOIN)を使います。

これは、条件にあっていないものも含めて取得するもの。

 

上記SQLを外部結合に書き換えて、実行してみます。

SELECT content, GROUP_CONCAT(tag.name separator ',') as tags FROM item LEFT OUTER JOIN (tag JOIN tag_map USING(tag_id)) USING(item_id) GROUP BY item_id

SQL的には、「JOIN」の前に「LEFT OUTER」をつけただけ。

これだけで外部結合になります。

※LEFT OUTER JOINの他に、RIGHT OUTER JOINと言うものもあるらしいです。

つなげる方向が違うようですが、基本的にはどっちか片方に合わせておけば問題ないかと思います。

LEFTとRIGHTで動作上の違いとか取得時の違いとかがあれば教えてください・・・

 

結果は以下の通り。

きちんと、取得できていることがわかります。

タグがあるものは、タグ名も一緒に取得し、タグがないものはnullとして取得しています。

 

 

参考 SELECT構文:JOINを使ってテーブルを結合する – SMART

http://rfs.jp/sb/sql/s03/03_3.html#JOIN

 

今回のSQLサンプル

http://sqlfiddle.com/#!9/0a510a/2/0

 

まとめ

今回はSQLの内部結合と外部結合の違いを知りました。

条件に合うものだけが必要なのか、そうでないのかによって使い分ける必要が有ることを知りました。

 

まだまだSQLとは仲良くなれていないので、もっとSQLと戯れていきたいです。

 

今回はこのへんで。

おわり

Linux,Mac,プログラミング

こんにちは、しきゆらです。

9月の中頃から10月の頭にかけて、学会とインターンシップ参加、さらに学会と

なんだか忙しい日々を過ごしていました。

 

いくつか、メモしておきたいことは溜まっているのでそれを放出していきます。

まずは、タイトル通りTerminalの設定をいじって見たお話を。

 

MacやLinuxではよく使うTerminal。

これの見た目を自分好みに変えることができることを最近知りました。

例えば、

$ cd hoge

とかの「$」の前にはユーザ名とかが書かれていると思います。

ここの文言や表示する色などを変更することができるようです。

 

方法に関しては、以下のサイトを参考にしました。

【mac】ターミナルのプロンプトの変更 – Qiita

https://qiita.com/griffin3104/items/0d610ad1a57fa6ad6a09

 

「.bashrc」に対して以下のように書けばいいようです。

# 上記サイトより
export PS1='\h:\W \u\$ '

 

設定の内容等も、上記サイトに書かれています。

設定項目:

\h ホスト名(最初の.まで)
\H ホスト名
\W ディレクトリ
\w ディレクトリ(フルパス)
\u ユーザ名
\t 時間

色の設定:

\[\e[0;30m\]色をつけたい部分\[\e[0;0m\]
\[\e[0;31m\]色をつけたい部分\[\e[0;0m\]
\[\e[0;32m\]色をつけたい部分\[\e[0;0m\]
\[\e[0;33m\]色をつけたい部分\[\e[0;0m\]
\[\e[0;34m\]色をつけたい部分\[\e[0;0m\]
\[\e[0;35m\]色をつけたい部分\[\e[0;0m\]
\[\e[0;36m\]色をつけたい部分\[\e[0;0m\]
\[\e[0;37m\]色をつけたい部分\[\e[0;0m\]

 

これにより、「$」の前部分を自分好みに変えることができます。

例えば、顔文字を入れたりもできますよ。

 

さて、これらの情報をもとにUbuntuっぽいTerminalを作っていきましょう。

まずは、プロンプトの文言は以下のような感じ。

PS1="\[\e[0;32m\]\u\[\e[0;0m\]:\[\e[0;34m\]\W\[\e[0;0m\]$ "

本当であれば「ユーザ名@PC名」となりますが、長くなるのでユーザ名までしか記載していません。

充実に再現する場合は「\u」のあとに「@\h」とするといいかもしれません。

 

最後に、背景色です。

これに関してはブログで紹介してくれている方が。

Default background color of Terminal in Ubuntu – Code Yarns

https://codeyarns.com/2013/01/21/default-background-color-of-terminal-in-ubuntu/

このサイトによると、UbuntuのTermilan背景色は「RGB = (48, 10, 36)」だそうです。

これをTerminalの環境設定から指定してあげます。

 

Terminalの「環境設定」=> 「プロパティ」タブの中に複数のテンプレがあるので、そのどれかをいじるか、

新しく追加しましょう。

私は新規追加しました。

(新規追加は、左側のリストの下にある「+」ボタンを押すだけ)

適当な名前をつけて、「背景色」を上記のRGB値にします。

 

そして、作成したプロファイルをダブルクリックすれば、作成した設定でTerminalが開きます。

紫っぽい背景色、緑色の文字。

Ubuntuっぽい。

個人的にはUbuntuの背景色が好きで、このためだけにUbuntuをインストールするくらいです。

最近はもっぱらMacしか使いませんが、心の隅っこくらいにはUbuntuのTerminalのことを覚えておいてあげましょう。

 

さらに追加で、「ls」コマンドを実行したとき、UbuntuのTerminalは以下のような感じで、

ファイルとディレクトリを色分けしてくれました。

これを再現するのはかんたん。

プロンプトの設定をした「.bashrc」に以下を追加すればOK。

alias ls='ls -G'

単純に、lsコマンドのオプションを付けるようにするだけ。

これで、上記の画像のようにディレクトリを色分けしてくれます。

 

 

今回はここまで。

需要はないと思いますが、Ubuntu好きな方はTerminalの色をUbuntuっぽくしてみてはいかがでしょうか。

おわり

JavaScript,Ruby,プログラミング

こんにちは、しきゆらです。

今回は、やんごとなき理由により日付を表す文字列をサーバ側からもらい、JSで処理するような状況で困った私が、その時解決した方法をメモしておきます。

 

少しだけ状況を説明すると、

サーバ側ではRubyさんを使って日付を文字列にしてDBへ入れています。

(この段階でSQLのtimestampとか使えばいいのに、と思いうツッコミはなしで)

その日付文字列をJSでパースしてあれこれ処理するようなプログラムを書いていました。

例えば、前回のログイン時間との時間差とか。

 

ここで、問題が起こります。

Rubyで「Time.now」として取得できる日付を表す文字列は、

  • 2017-10-20 23:32:04 +0900

JSで「Date.parse()」で理解できる日付文字列は、

  • Fri Oct 20 2017 23:32:04 GMT+0900

 

これでは、JSは日付を理解できません!

ここからが本題です。

 

JSは日付をパースしたりするとき、IETDF標準日付構文としてパースするようです。

時刻を表す文字列を与えると、parse() は time 値を返します。これは、"Mon, 25 Dec 1995 13:30:00 GMT" のような RFC2822 / IETF 標準の日付構文 (RFC2822 Section 3.3) を受け入れます。

Date.parse() – JavaScript | MDN

JSのDate()で返ってくる文字列は「Fri Oct 06 2017 23:49:19 GMT+0900 (JST)」のような形です。

パースするときも、この文字列形式だと思ってやるようなので、

YYYY-MM-DD HH:MM:SS +0900のような文字列は受け付けてくれません。

※Chrome系のブラウザでは受け付けてくれるようです。

 

何かやんごとなき状況により、「YYYY-MM-DD HH:MM:SS +0900」のような文字列を扱わざるを得ないような場合があるかもしれません。

そこで、サーバ側でJSで理解できる形式に変換してみましょう。

もちろん、Rubyさんでやってみます。

他の言語でも、多分同じような関数があるはずなのでいけるはずです。

time = Time.now
#=> 2017-10-06 23:53:45 +0900
time.strftime("%a %b %d %Y %T GMT%z (%Z)")
#=> "Fri Oct 06 2017 23:53:45 GMT+0900 (JST)"

重要なのは3行目のstrftime。

この書き方で、JSが理解できる日付を表す文字列にできます。

 

日本決め打ちですが、とりあえずこれでJSさんもRubyさんも、この日付をきちんと理解してくれます。
JSさんのDate型には罠が多くて面倒ですね。
早く同じ挙動になってくれ・・・と思う今日このごろ。

とりあえず、Chrome系、Firefox、Safariできちんと読み込めたので大丈夫だと思います。

 

今回はここまで。

おわり。