SlideShare a Scribd company logo
Lisp tutorial for Pythonista.
Day #4 : Work with RDBMS




                                                          Ransui Iso
                           Strategic Technology Group, X-Listing Co, Ltd.
宿題できましたか?
今日は予告通り SQL !

なんだかんだで RDBMS 使えないと
   仕事では困っちゃう
CLSQL を使います
QuickLisp でインストール

clsql と clsql­mysql と clsql­postgresql
       をインストールしてくださいませ
REPL 環境下でテストしてみる

 ●   とりあえず select
CL­USER> (use­package :clsql)
CL­USER> (connect '("achernar" "stats_master" "<user­name>" "<password>")
             :database­type :mysql)
#<CLSQL­MYSQL:MYSQL­DATABASE achernar/stats_master/<user­name> OPEN {100343DEE1}>
CL­USER> (query "select id, client_id from campaign_base limit 2")
((1712 3736) (815 3371))
("id" "client_id")



 ●   多値関数なので…
CL­USER> (multiple­value­bind (rows field­names)
             (query "select * from campaign_base limit 2")
           (dolist (row rows)
             (loop :for value :in row
                   :for field­name :in field­names
                   :do (format t "~a : ~a~%" field­name value))
             (terpri)))
                                                (terpri) :標準出力に改行を送る関数
プログラムで使う場合は

●   例によって asdf でロードする
●
(asdf:oos 'asdf:load­op :clsql)
(asdf:oos 'asdf:load­op :clsql­mysql)
 ●
(asdf:oos 'asdf:load­op :clsql­postgresql)
      ●   SLIME の REPL で使う時も手動でロードしとく
      ●   Backend は使いたいやつだけで OK よ

●   パッケージ名は clsql
      ●   いちいちパッケージ名解決 (clsql:query ... ) とか面倒な人は
(use­package :clsql)

      ●   個人的には use-package はあまりオススメしないけどね。
      ●   Python で言うところの from clsql import * だし…
CLSQL はマニュアルが充実
http://clsql.b9.com/manual/index.html
      ここ読めば普通は困らないと思う
これだけだと
あまりにアレなので
いくつか Tips など
MySQL を使う場合

●   テーブル名の大文字 / 小文字区別問題
      ●   Linux 上で動作している MySQL はデフォルトでテーブル名の大文
            字と小文字を区別する ( 定義時の名前がそのまま使われる )
      ●   標準 SQL では区別しない
      ●   CLSQL は標準準拠 ( 基本的に大文字でサーバに送信する )



           テーブル名が見つからないと怒られる

●   /etc/mysql/my.cnf に以下を追加
      ●   追加場所 [mysqld] セクション
# for Case insensitive
lower_case_table_names          = 1

              詳細: http://dev.mysql.com/doc/refman/5.1/ja/identifier-case-sensitivity.html
connection オブジェクト
●   connect 関数
      ●   データベースへの接続オブジェクトを作成する
      ●   同じ属性の接続を複数作ろうとするとデフォルトではエラー!
      ●   :if­exists キーワード引数でどうするかを決める
          –   :new        新しく接続オブジェクトを作る
          –   :warn­new   警告出すけどオブジェクトを作る
          –   :error      エラーとする
          –   :old        すでに開いている接続オブジェクトを返す
          –   :warn­old   警告出して、既存の接続オブジェクトを返す
          –   デフォルト値は     *connect­if­exists* 変数で設定できる


●   connected-databases 関数
      ●   現時点で有効な全ての接続オブジェクトをリストで返す

●   *default-database* 変数
      ●   既定の接続オブジェクトを保持する
接続を切る

●   disconnect 関数を使う
      ●   :database キーワード引数で対象の接続オブジェクトを指定する
      ●   省略時は clsql:*default­database* が指定されたものとみ
           なされる

CL­USER> (defparameter db­conn nil)

CL­USER> (setf db­conn 
            (connect '("achernar" "stats_master" "xlisting" "xlisting")
               :database­type :mysql))

CL­USER> (eq db­conn *default­database*)

CL­USER> (disconnect :database db­conn)

CL­USER> *default­database*
SQL Quote の方法
●   SQL 関数を使えば簡単
CL­USER> (format nil "select * from foo where bar in ~a"
            (sql '("Hello" "world")))

CL­USER> (format nil "select * from foo where bar = ~a" 
            (sql " 波浪ワールド  'Hello World'"))

CL­USER> (enable­sql­reader­syntax)

CL­USER> (let ((msg "Hello 'lisp' world!"))
           (sql [select [foo] [bar] :from [baz] :where [= [name] msg]]))

CL­USER> (sql [select 
                [client_base name]
                [agency_base name]
                  :from '([client_base] [agency_base])
                  :where [= [client_base agency_id] [agency_base id]]])




          値の埋め込みは format 使うよりも SQL テンプレの方がお勧め
SQL テンプレ

●   Lisp の構文そのものが拡張されちゃうので注意
     ●   グローバルに有効化:   (enable-sql-reader-syntax)
     ●   グローバルに無効化:   (disable-sql-reader-syntax)
     ●   ローカルに有効化:    (locally-enable-sql-reader-syntax)
     ●   ローカルに無効化:    (locally-disable-sql-reader-syntax)

●   [ と ] に意味が追加されてる
     ●   Reader マクロという機能を使っている
取得した行について繰り返し処理

●   do-query とか loop フォームが便利
CL­USER> (do­query ((agency­name client­name)
                    (sql [select [agency_base name] [client_base name]
                      :from '([agency_base] [client_base])
                      :where [and [= [agency_base id] [client_base agency_id]]
                                  [= [agency_base id] 22]]]))
           (format t "~a : ~a~%" agency­name client­name))


CL­USER> (loop :for (agency­name client­name) being each tuple in 
                    (sql [select [agency_base name] [client_base name]
                      :from '([agency_base] [client_base])
                      :where [and [= [agency_base id] [client_base agency_id]]
                                  [= [agency_base id] 22]]])
            :do (format t "~a : ~a~%" agency­name client­name))


結果1行毎にガッツり処理したいとき                                 do-query
カウントしたりちょっと加工して収集しなおしたいとか                         loop

て感じで使い分けるといいかも。
まぁこのへんは好みというかソースが見やすくなるほうを優先するのが吉。
関数に仕立ててみる

●   こんな感じとか
(defun get­client­info­by­ids (ids)
  (let ((result nil))
    (multiple­value­bind (rows field­names)
        (query (sql [select [*] :from [client_base] :where [in [id] ids]]))
       
       (dolist (row rows)
         (push
           (loop :for value in row
                 :for field­name in field­names
                 :collect (cons field­name value)) result)))
    result))



CL­USER> (get­client­info­by­ids '(1 3 5))

CL­USER> (dolist (result (get­client­info­by­ids '(1 3 5)))
           (print (cdr (assoc "name" result :test #'string=))))
connection pool

●   こんな感じでやるといいかも
(defun get­client­info­by­ids (ids &key (database clsql:*default­database*))
  (let ((result nil))
    (multiple­value­bind (rows field­names)
        (query (sql [select [*] :from [client_base] :where [in [id] ids]])
                 :database database)
       (dolist (row rows)
         (push
          (loop :for value in row
             :for field­name in field­names
             :collect (cons field­name value)) result)))
    result))



(with­database
    (db­conn '("achernar" "stats_master" 
               "<user­name>" "<password>") :pool t :database­type :mysql)

           (get­client­info­by­ids '(1 2 3) :database db­conn))
create / drop table

●   ここでもテンプレ使えます
CL­USER> (create­table [test_table]
                       '(([id] integer :not­null :unique :primary­key)
                         ([name] (varchar 255) :not­null)
                         ([status] boolean :not­null)))


CL­USER> (table­exists­p [test_table])

CL­USER> (drop­table [test_table])

CL­USER> (table­exists­p [test_table])
insert / update / delete

●   それぞれ関数があるので使いましょう
CL­USER> (insert­records :into [test_table] 
                         :attribute '(id name status)
                         :values '(1 "James 'random' Hacker" 1))

CL­USER> (select [*] :from [test_table])

CL­USER> (update­records [test_table] 
                         :av­pairs '((name "foo bar")
                                     (status 0))
                         :where [= [id] 1])

CL­USER> (select [*] :from [test_table])

CL­USER> (delete­records :from [test_table] :where [= [name] "foo bar"])

CL­USER> (select [*] :from [test_table])
マニュアルをよく読むべし

    Lisp 系のマニュアルの書かれ方はちょっと独特だけど
CLSQL のマニュアルは例がいっぱいあるので大いに参考にしよう
宿題

前回の宿題掲示板のストレージを
   データベースにしてね

ちょこっと改造すれは OK のハズなので、簡単でしょ?

         connection pool を使って
 with­database フォームを使うのがお勧めかも

More Related Content

PDF
Lisp Tutorial for Pythonista Day 6
PPTX
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
PDF
Ansible入門...?
PPTX
そうだったのか! よくわかる process.nextTick() node.jsのイベントループを理解する
KEY
Fabricでサーバー管理をDRYにしよう
PDF
2日間Fabricを触った俺が
 色々解説してみる
PDF
片手間MySQLチューニング戦略
PPT
HandlerSocket plugin for MySQL
Lisp Tutorial for Pythonista Day 6
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
Ansible入門...?
そうだったのか! よくわかる process.nextTick() node.jsのイベントループを理解する
Fabricでサーバー管理をDRYにしよう
2日間Fabricを触った俺が
 色々解説してみる
片手間MySQLチューニング戦略
HandlerSocket plugin for MySQL

What's hot (20)

PDF
Fabric Essentials
PDF
Ansible入門
PDF
社内勉強会資料(Varnish Module)
PDF
C#次世代非同期処理概観 - Task vs Reactive Extensions
PDF
Webサーバ勉強会03
PPTX
Ansibleで始めるサーバ管理勉強会(2014年10月1日)
PDF
Ansible 入門 #01 (初心者向け)
PDF
Openresty
PPTX
JJUG CCC 2017 Fall オレオレJVM言語を作ってみる
PDF
静的サイトどこにする?
PPT
Google Perf Tools (tcmalloc) の使い方
PPT
いまさら聞けないRake入門
PPTX
SQLチューニング入門 入門編
PDF
Varnish 4.0 Release Party in Tokyo発表資料
PDF
VarnishではじめるESI
PDF
Deep Dive async/await in Unity with UniTask(UniRx.Async)
PPT
Handlersocket etc. 20110906
PDF
Serverspecを自分好みにアレンジ スクリーンショットで証跡保存を撲滅-
PDF
C++ マルチスレッドプログラミング
PPTX
async/await のしくみ
Fabric Essentials
Ansible入門
社内勉強会資料(Varnish Module)
C#次世代非同期処理概観 - Task vs Reactive Extensions
Webサーバ勉強会03
Ansibleで始めるサーバ管理勉強会(2014年10月1日)
Ansible 入門 #01 (初心者向け)
Openresty
JJUG CCC 2017 Fall オレオレJVM言語を作ってみる
静的サイトどこにする?
Google Perf Tools (tcmalloc) の使い方
いまさら聞けないRake入門
SQLチューニング入門 入門編
Varnish 4.0 Release Party in Tokyo発表資料
VarnishではじめるESI
Deep Dive async/await in Unity with UniTask(UniRx.Async)
Handlersocket etc. 20110906
Serverspecを自分好みにアレンジ スクリーンショットで証跡保存を撲滅-
C++ マルチスレッドプログラミング
async/await のしくみ
Ad

Similar to Lisp Tutorial for Pythonista : Day 4 (20)

PDF
SQLQL は GraphQL にとってなんなのか
PDF
とあるDBAの黒い画面(ターミナル)
PDF
tcpdump & xtrabackup @ MySQL Casual Talks #1
PDF
OpenStack + Common Lisp
PDF
PerlとSQLのいろいろ
PDF
MoteMote Compiler Plugin
PDF
配布用Beginnerならきっと役立つmaster slave環境
PPTX
MySQL SQL tuning
PDF
Slick入門
PDF
キメるClojure
KEY
Mysql casial01
PPTX
PostgreSQLのソース・ターゲットエンドポイントとしての利用
PDF
アプリ開発者、DB 管理者視点での Cloud Spanner 活用方法 | 第 10 回 Google Cloud INSIDE Games & App...
PDF
MySQLを割と一人で300台管理する技術
PDF
Slub data structure
PDF
LINEのMySQL運用について
KEY
activerecord-oracle_enhanced-adapterのご紹介
PPT
Handlersocket 20110517
PDF
第8回KPF発表資料
PDF
位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo
SQLQL は GraphQL にとってなんなのか
とあるDBAの黒い画面(ターミナル)
tcpdump & xtrabackup @ MySQL Casual Talks #1
OpenStack + Common Lisp
PerlとSQLのいろいろ
MoteMote Compiler Plugin
配布用Beginnerならきっと役立つmaster slave環境
MySQL SQL tuning
Slick入門
キメるClojure
Mysql casial01
PostgreSQLのソース・ターゲットエンドポイントとしての利用
アプリ開発者、DB 管理者視点での Cloud Spanner 活用方法 | 第 10 回 Google Cloud INSIDE Games & App...
MySQLを割と一人で300台管理する技術
Slub data structure
LINEのMySQL運用について
activerecord-oracle_enhanced-adapterのご紹介
Handlersocket 20110517
第8回KPF発表資料
位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo
Ad

More from Ransui Iso (14)

PPTX
「Pythonでやってみた」~広がるプログラミングの愉しみ~
PDF
Pythonで作る俺様サウンドエフェクター
PDF
アドテクを支える人と技術
PDF
Playing with curses
PDF
小中学生Hack-a-thonにオッサンが乗り込んだ話
PDF
XML-RPC : Pythonが「電池付属」と呼ばれる理由
PDF
ソフトシンセを作りながら学ぶPythonプログラミング
PDF
Introduction of ToySynth
PDF
PyQtではじめるGUIプログラミング
PDF
PySynth : A toy pure python software synthesizer.
PDF
Lisp Tutorial for Pythonista : Day 5
PDF
Lisp Tutorial for Pythonista : Day 3
PDF
Lisp tutorial for Pythonista : Day 2
PDF
Lisp tutorial for Pythonista : Day 1
「Pythonでやってみた」~広がるプログラミングの愉しみ~
Pythonで作る俺様サウンドエフェクター
アドテクを支える人と技術
Playing with curses
小中学生Hack-a-thonにオッサンが乗り込んだ話
XML-RPC : Pythonが「電池付属」と呼ばれる理由
ソフトシンセを作りながら学ぶPythonプログラミング
Introduction of ToySynth
PyQtではじめるGUIプログラミング
PySynth : A toy pure python software synthesizer.
Lisp Tutorial for Pythonista : Day 5
Lisp Tutorial for Pythonista : Day 3
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 1

Lisp Tutorial for Pythonista : Day 4

  • 1. Lisp tutorial for Pythonista. Day #4 : Work with RDBMS Ransui Iso Strategic Technology Group, X-Listing Co, Ltd.
  • 3. 今日は予告通り SQL ! なんだかんだで RDBMS 使えないと 仕事では困っちゃう
  • 5. QuickLisp でインストール clsql と clsql­mysql と clsql­postgresql をインストールしてくださいませ
  • 6. REPL 環境下でテストしてみる ● とりあえず select CL­USER> (use­package :clsql) CL­USER> (connect '("achernar" "stats_master" "<user­name>" "<password>")              :database­type :mysql) #<CLSQL­MYSQL:MYSQL­DATABASE achernar/stats_master/<user­name> OPEN {100343DEE1}> CL­USER> (query "select id, client_id from campaign_base limit 2") ((1712 3736) (815 3371)) ("id" "client_id") ● 多値関数なので… CL­USER> (multiple­value­bind (rows field­names)              (query "select * from campaign_base limit 2")            (dolist (row rows)              (loop :for value :in row                    :for field­name :in field­names                    :do (format t "~a : ~a~%" field­name value))              (terpri))) (terpri) :標準出力に改行を送る関数
  • 7. プログラムで使う場合は ● 例によって asdf でロードする ● (asdf:oos 'asdf:load­op :clsql) (asdf:oos 'asdf:load­op :clsql­mysql) ● (asdf:oos 'asdf:load­op :clsql­postgresql) ● SLIME の REPL で使う時も手動でロードしとく ● Backend は使いたいやつだけで OK よ ● パッケージ名は clsql ● いちいちパッケージ名解決 (clsql:query ... ) とか面倒な人は (use­package :clsql) ● 個人的には use-package はあまりオススメしないけどね。 ● Python で言うところの from clsql import * だし…
  • 10. MySQL を使う場合 ● テーブル名の大文字 / 小文字区別問題 ● Linux 上で動作している MySQL はデフォルトでテーブル名の大文 字と小文字を区別する ( 定義時の名前がそのまま使われる ) ● 標準 SQL では区別しない ● CLSQL は標準準拠 ( 基本的に大文字でサーバに送信する ) テーブル名が見つからないと怒られる ● /etc/mysql/my.cnf に以下を追加 ● 追加場所 [mysqld] セクション # for Case insensitive lower_case_table_names          = 1 詳細: http://dev.mysql.com/doc/refman/5.1/ja/identifier-case-sensitivity.html
  • 11. connection オブジェクト ● connect 関数 ● データベースへの接続オブジェクトを作成する ● 同じ属性の接続を複数作ろうとするとデフォルトではエラー! ● :if­exists キーワード引数でどうするかを決める – :new 新しく接続オブジェクトを作る – :warn­new 警告出すけどオブジェクトを作る – :error エラーとする – :old すでに開いている接続オブジェクトを返す – :warn­old 警告出して、既存の接続オブジェクトを返す – デフォルト値は *connect­if­exists* 変数で設定できる ● connected-databases 関数 ● 現時点で有効な全ての接続オブジェクトをリストで返す ● *default-database* 変数 ● 既定の接続オブジェクトを保持する
  • 12. 接続を切る ● disconnect 関数を使う ● :database キーワード引数で対象の接続オブジェクトを指定する ● 省略時は clsql:*default­database* が指定されたものとみ なされる CL­USER> (defparameter db­conn nil) CL­USER> (setf db­conn              (connect '("achernar" "stats_master" "xlisting" "xlisting")                :database­type :mysql)) CL­USER> (eq db­conn *default­database*) CL­USER> (disconnect :database db­conn) CL­USER> *default­database*
  • 13. SQL Quote の方法 ● SQL 関数を使えば簡単 CL­USER> (format nil "select * from foo where bar in ~a"             (sql '("Hello" "world"))) CL­USER> (format nil "select * from foo where bar = ~a"              (sql " 波浪ワールド  'Hello World'")) CL­USER> (enable­sql­reader­syntax) CL­USER> (let ((msg "Hello 'lisp' world!"))            (sql [select [foo] [bar] :from [baz] :where [= [name] msg]])) CL­USER> (sql [select                  [client_base name]                 [agency_base name]                   :from '([client_base] [agency_base])                   :where [= [client_base agency_id] [agency_base id]]]) 値の埋め込みは format 使うよりも SQL テンプレの方がお勧め
  • 14. SQL テンプレ ● Lisp の構文そのものが拡張されちゃうので注意 ● グローバルに有効化: (enable-sql-reader-syntax) ● グローバルに無効化: (disable-sql-reader-syntax) ● ローカルに有効化: (locally-enable-sql-reader-syntax) ● ローカルに無効化: (locally-disable-sql-reader-syntax) ● [ と ] に意味が追加されてる ● Reader マクロという機能を使っている
  • 15. 取得した行について繰り返し処理 ● do-query とか loop フォームが便利 CL­USER> (do­query ((agency­name client­name)                     (sql [select [agency_base name] [client_base name]                       :from '([agency_base] [client_base])                       :where [and [= [agency_base id] [client_base agency_id]]                                   [= [agency_base id] 22]]]))            (format t "~a : ~a~%" agency­name client­name)) CL­USER> (loop :for (agency­name client­name) being each tuple in                      (sql [select [agency_base name] [client_base name]                       :from '([agency_base] [client_base])                       :where [and [= [agency_base id] [client_base agency_id]]                                   [= [agency_base id] 22]]])             :do (format t "~a : ~a~%" agency­name client­name)) 結果1行毎にガッツり処理したいとき do-query カウントしたりちょっと加工して収集しなおしたいとか loop て感じで使い分けるといいかも。 まぁこのへんは好みというかソースが見やすくなるほうを優先するのが吉。
  • 16. 関数に仕立ててみる ● こんな感じとか (defun get­client­info­by­ids (ids)   (let ((result nil))     (multiple­value­bind (rows field­names)         (query (sql [select [*] :from [client_base] :where [in [id] ids]]))                (dolist (row rows)          (push            (loop :for value in row                  :for field­name in field­names                  :collect (cons field­name value)) result)))     result)) CL­USER> (get­client­info­by­ids '(1 3 5)) CL­USER> (dolist (result (get­client­info­by­ids '(1 3 5)))            (print (cdr (assoc "name" result :test #'string=))))
  • 17. connection pool ● こんな感じでやるといいかも (defun get­client­info­by­ids (ids &key (database clsql:*default­database*))   (let ((result nil))     (multiple­value­bind (rows field­names)         (query (sql [select [*] :from [client_base] :where [in [id] ids]])                  :database database)        (dolist (row rows)          (push           (loop :for value in row              :for field­name in field­names              :collect (cons field­name value)) result)))     result)) (with­database     (db­conn '("achernar" "stats_master"                 "<user­name>" "<password>") :pool t :database­type :mysql)            (get­client­info­by­ids '(1 2 3) :database db­conn))
  • 18. create / drop table ● ここでもテンプレ使えます CL­USER> (create­table [test_table]                        '(([id] integer :not­null :unique :primary­key)                          ([name] (varchar 255) :not­null)                          ([status] boolean :not­null))) CL­USER> (table­exists­p [test_table]) CL­USER> (drop­table [test_table]) CL­USER> (table­exists­p [test_table])
  • 19. insert / update / delete ● それぞれ関数があるので使いましょう CL­USER> (insert­records :into [test_table]                           :attribute '(id name status)                          :values '(1 "James 'random' Hacker" 1)) CL­USER> (select [*] :from [test_table]) CL­USER> (update­records [test_table]                           :av­pairs '((name "foo bar")                                      (status 0))                          :where [= [id] 1]) CL­USER> (select [*] :from [test_table]) CL­USER> (delete­records :from [test_table] :where [= [name] "foo bar"]) CL­USER> (select [*] :from [test_table])
  • 20. マニュアルをよく読むべし Lisp 系のマニュアルの書かれ方はちょっと独特だけど CLSQL のマニュアルは例がいっぱいあるので大いに参考にしよう
  • 21. 宿題 前回の宿題掲示板のストレージを データベースにしてね ちょこっと改造すれは OK のハズなので、簡単でしょ? connection pool を使って with­database フォームを使うのがお勧めかも