rubyからsqlite3を扱いたいんです
もちろんkoboクローラ的な意味で。。
# coding: utf-8 require 'sqlite3' begin db = SQLite3::Database.new('jkobo.db') db.execute(<<-DROP) drop table if exists jkobo; DROP db.execute(<<-CREATE) create table jkobo ( id integer primary key autoincrement ,created_at text ,product_no text ,title text ,price integer ,publish_info text ,note text ); CREATE ensure db.close end
このノリでselect/insert/update/deleteできる様子。
まあでもせっかくなのでActiveRecordでやった方がいいよね。
参考
mechanizeでgetしたhtmlが化けているっぽいんです
引き続きkoboをクローリングする文脈で、です。
irb(main):001:0> require 'mechanize' => true irb(main):002:0> agent = Mechanize.new => #<Mechanize:0x410eda0 @agent=#<Mechanize::HTTP::Agent:0x410ed88 @allowed_error_codes=[], @conditional_requests=true, @context=#<Mechanize:0x410eda0 ...>, @content_encoding_hooks=[], @cookie_jar=#<Mechanize::CookieJar:0x410ed40 @store=#<HTTP::CookieJar::HashStore:0x410dbd0 @mon_owner=nil, @mon_count=0, @mon_mutex=#<Mutex:0x410dba0>, @logger=nil, @gc_threshold=150, @jar={}, @gc_index=0>>, @follow_meta_refresh=false, @follow_meta_refresh_self=false, @gzip_enabled=true, @history=[], @ignore_bad_chunking=false, @keep_alive=true, @max_file_buffer=100000, @open_timeout=nil, @post_connect_hooks=[], @pre_connect_hooks=[], @read_timeout=nil, @redirect_ok=true, @redirection_limit=20, @request_headers={}, @robots=false, @user_agent="Mechanize/2.7.2 Ruby/1.9.2p290 (http://github.com/sparklemotion/mechanize/)", @webrobots=nil, @auth_store=#<Mechanize::HTTP::AuthStore:0x410da98 @auth_accounts={}, @default_auth=nil>, @authenticate_parser=#<Mechanize::HTTP::WWWAuthenticateParser:0x410da38 @scanner=nil>, @authenticate_methods={}, @digest_auth=#<Net::HTTP::DigestAuth:0x410d9d8 @mon_owner=nil, @mon_count=0, @mon_mutex=#<Mutex:0x410d9c0>, @nonce_count=-1>, @digest_challenges={}, @pass=nil, @scheme_handlers={"http"=>#<Proc:0x410d948@D:/RailsInstaller/Ruby1.9.2/lib/ruby/gems/1.9.1/gems/mechanize-2.7.2/lib/mechanize/http/agent.rb:172 (lambda)>, "https"=>#<Proc:0x410d948@D:/RailsInstaller/Ruby1.9.2/lib/ruby/gems/1.9.1/gems/mechanize-2.7.2/lib/mechanize/http/agent.rb:172 (lambda)>, "relative"=>#<Proc:0x410d948@D:/RailsInstaller/Ruby1.9.2/lib/ruby/gems/1.9.1/gems/mechanize-2.7.2/lib/mechanize/http/agent.rb:172 (lambda)>, "file"=>#<Proc:0x410d948@D:/RailsInstaller/Ruby1.9.2/lib/ruby/gems/1.9.1/gems/mechanize-2.7.2/lib/mechanize/http/agent.rb:172 (lambda)>}, @http=#<Net::HTTP::Persistent:0x410d828 @name="mechanize", @debug_output=nil, @proxy_uri=nil, @no_proxy=[], @headers={}, @override_headers={}, @http_versions={}, @keep_alive=300, @open_timeout=nil, @read_timeout=nil, @idle_timeout=5, @max_requests=nil, @socket_options=[[6, 1, 1]], @generation_key=:net_http_persistent_mechanize_generations, @ssl_generation_key=:net_http_persistent_mechanize_ssl_generations, @request_key=:net_http_persistent_mechanize_requests, @timeout_key=:net_http_persistent_mechanize_timeouts, @certificate=nil, @ca_file=nil, @private_key=nil, @ssl_version=nil, @verify_callback=nil, @verify_mode=1, @cert_store=nil, @generation=1, @ssl_generation=1, @reuse_ssl_sessions=true, @retry_change_requests=false, @ruby_1=true, @retried_on_ruby_2=false>>, @log=nil, @watch_for_set=nil, @history_added=nil, @pluggable_parser=#<Mechanize::PluggableParser:0x410d510 @parsers={"text/html"=>Mechanize::Page, "application/xhtml+xml"=>Mechanize::Page, "application/vnd.wap.xhtml+xml"=>Mechanize::Page, "image"=>Mechanize::Image, "text/xml"=>Mechanize::XmlFile, "application/xml"=>Mechanize::XmlFile}, @default=Mechanize::File>, @keep_alive_time=0, @proxy_addr=nil, @proxy_port=nil, @proxy_user=nil, @proxy_pass=nil, @html_parse r=Nokogiri::HTML, @default_encoding=nil, @force_default_encoding=false> irb(main):003:0> agent.user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36' => "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36" irb(main):004:0> agent.get('http://rakuten.kobobooks.com/search/search.html?q=&t=all&f=keyword&s=publicationdatedesc&g=both&c=5wgoE9EyJkuWj2DMT8OIZg&l=ja&p=1') => #<Mechanize::Page {url #<URI::HTTP:0x3eba688 URL:http://rakuten.kobobooks.com/search/search.html?q=&t=all&f=keyword&s=publicationdatedesc&g=both&c=5wgoE9EyJkuWj2DMT8OIZg&l=ja&p=1>} : :
ときて
irb(main):013:0* puts agent.page.body <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" ><html xmlns="http://www.w3.org/1999/xhtml"> <head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> : : <div class="browserNotification"> 縺薙・繧ヲ繧ァ繝悶し繧、繝医・縲∫樟蝨
なんでや。。
irb(main):015:0> puts agent.page.body.encode(Encoding::Windows_31J) Encoding::UndefinedConversionError: "\xE3" to UTF-8 in conversion from ASCII-8BIT to UTF-8 to Windows-31J from (irb):15:in `encode' from (irb):15 from D:/RailsInstaller/Ruby1.9.2/bin/irb:12:in `<main>' irb(main):016:0> puts agent.page.body.encode(Encoding::Windows_31J, Encoding::UTF_8) Encoding::UndefinedConversionError: U+00BB from UTF-8 to Windows-31J from (irb):16:in `encode' from (irb):16 from D:/RailsInstaller/Ruby1.9.2/bin/irb:12:in `<main>'
これではだめ。
irb(main):017:0> puts agent.page.body.encode(Encoding::Windows_31J, Encoding::UTF_8, undef: :replace) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" ><html xmlns="http://www.w3.org/1999/xhtml"> <head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> : : <div class="browserNotification"> このウェブサイトは、現在お客様がお使いのInternet Explorer 6 あるいは
(;^ω^)やっとか
参考
楽天koboの検索結果をクローリングしたいんです
1日1回。だって新着がわからないんですもん。「新しい順」でソートすると紙版の出版年月でソートしよるし。
ウェブAPIないし、だったら毎日クローリングして差分を取るしかないじゃないですか。。
mechanizeを使うよ
gem install mechanize
これでおk。
ざっくりした使い方はたぶんこんな感じ。
require 'mechanize' begin agent = Mechanize.new agent.user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36' url = 'http://rakuten.kobobooks.com/search/search.html?q=&t=all&f=keyword&s=publicationdatedesc&g=both&c=&l=ja&p=' agent.get("#{url}1") total_count = agent.page.at('dl.SCSearch span.SCNumResTotalCount').inner_text.strip.to_i total_page = (total_count / 10) + 1 (1..total_page).each do |i| agent.get("#{url}#{i}") # 欲しい情報getだぜ! end ensure agent.shutdown unless agent.nil? end
参考
Oracleのパフォーマンスを気にしなきゃいけないけど何も知識ないんです
わたしのことなんですけどね。。今まで関係なかった(というかDBチームにおまかせ)のでほとんど気にしませんでしたが、そろそろ化けの皮が。。
1~2時間で最低限の中の最低限の基礎知識がほしいならこれでしょうか。
- Oracle SQLチューニング講座(1):パフォーマンス向上の最短コースを知る (1/2) - @IT
- Oracle SQLチューニング講座(2):SQLチューニングの必須知識を総ざらい(前編) (1/2) - @IT
- Oracle SQLチューニング講座(3):SQLチューニングの必須知識を総ざらい(後編) (1/3) - @IT
- Oracle SQLチューニング講座(4):チューニングが必要なSQLを洗い出す (1/3) - @IT
- Oracle SQLチューニング講座(5):SQLチューニングの基盤となる統計情報 (1/3) - @IT
読んでおけばきっと周りの会話がチンプンカンプンという状態にはならないんじゃないかな、と思います。今まで職場でなんとなく聞こえてきた会話を思い出すと、ここに登場しない単語については「なんすかそれ?」と質問してもそこまで恥ずかしい気持ちにはならずに済むでしょう。。
書籍となると、読んだことあるものはありませんがたぶんこのへんでしょうか。
新・門外不出のOracle現場ワザ |
Oracle SQLチューニング |
即戦力のOracle管理術 |
あるいはOracle Master Expertのパフォーマンスチューニングのやつの参考書とか?
Oracle Master Bronzeレベルの基礎知識も最低限ないといけないかなと。。
脱線ですがkindle版「達人に学ぶ SQL徹底指南書」は
目次がないのでとても読みにくいです。「目次から各章へのリンクがない」のではなく「目次ページがない」です。
サンプル版もそうでしたが「まあいっか」と思って買いました。。今は深く後悔しています。さらに全文検索もできない。悪夢ですね。サンプル版と本当に同じでした。
こういう場合はkoboでも同じですがしおりを付けまくって目次ジャンプ代わりにするしかありません。
ヒント句で表結合アルゴリズムを固定したいんです
ヒント句で表結合の仕方を固定したいんです
このSQLに対して実験。
select * from employees e, jobs j where e.job_id = j.job_id;
ヒント句なし
------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 107 | 10914 | 6 (17)| 00:00:01 | | 1 | MERGE JOIN | | 107 | 10914 | 6 (17)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| JOBS | 19 | 627 | 2 (0)| 00:00:01 | | 3 | INDEX FULL SCAN | JOB_ID_PK | 19 | | 1 (0)| 00:00:01 | |* 4 | SORT JOIN | | 107 | 7383 | 4 (25)| 00:00:01 | | 5 | TABLE ACCESS FULL | EMPLOYEES | 107 | 7383 | 3 (0)| 00:00:01 | ------------------------------------------------------------------------------------------
ネステッドループ結合にする
USE_NLに内部表を指定する形で強制できる。
フルスキャンする外部表にEMPLOYEESを、インデックススキャンする内部表にJOBSを指定すると
SQL> select /*+ ORDERED USE_NL(j) */ * from employees e, jobs j where e.job_id = j.job_id; 108行が選択されました。 実行計画 ---------------------------------------------------------- Plan hash value: 1536616604 ------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 107 | 10914 | 110 (0)| 00:00:02 | | 1 | NESTED LOOPS | | | | | | | 2 | NESTED LOOPS | | 107 | 10914 | 110 (0)| 00:00:02 | | 3 | TABLE ACCESS FULL | EMPLOYEES | 107 | 7383 | 3 (0)| 00:00:01 | |* 4 | INDEX UNIQUE SCAN | JOB_ID_PK | 1 | | 0 (0)| 00:00:01 | | 5 | TABLE ACCESS BY INDEX ROWID| JOBS | 1 | 33 | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------
その逆は
SQL> select /*+ ORDERED USE_NL(e) */ * from jobs j, employees e where e.job_id = j.job_id; 108行が選択されました。 実行計画 ---------------------------------------------------------- Plan hash value: 980169617 ------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 107 | 10914 | 22 (0)| 00:00:01 | | 1 | NESTED LOOPS | | | | | | | 2 | NESTED LOOPS | | 107 | 10914 | 22 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL | JOBS | 19 | 627 | 3 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | EMP_JOB_IX | 6 | | 0 (0)| 00:00:01 | | 5 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 6 | 414 | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------
この例では後者のほうがコストが低い。EMPLOYEESの方がレコード数が多いので、外部表としてフルスキャンしている前者の方が単純に遅くなったと考えられる。
後者であっても、EMP_JOB_IXの効率が悪ければ前者より遅くなることはあるだろう。
ポイントは結合するレコード数が少ない方を外部表に、効率的な索引を使える方を内部表にすること。
ソートマージ結合にする
もとからソートマージでしたが……
SQL> select /*+ USE_MERGE(e, j) */ * from employees e, jobs j where e.job_id = j.job_id; 108行が選択されました。 実行計画 ---------------------------------------------------------- Plan hash value: 303035560 ------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 107 | 10914 | 6 (17)| 00:00:01 | | 1 | MERGE JOIN | | 107 | 10914 | 6 (17)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| JOBS | 19 | 627 | 2 (0)| 00:00:01 | | 3 | INDEX FULL SCAN | JOB_ID_PK | 19 | | 1 (0)| 00:00:01 | |* 4 | SORT JOIN | | 107 | 7383 | 4 (25)| 00:00:01 | | 5 | TABLE ACCESS FULL | EMPLOYEES | 107 | 7383 | 3 (0)| 00:00:01 | ------------------------------------------------------------------------------------------
ハッシュ結合にする
SQL> select /*+ USE_HASH(e, j) */ * from employees e, jobs j where e.job_id = j.job_id; 108行が選択されました。 実行計画 ---------------------------------------------------------- Plan hash value: 1300016118 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 107 | 10914 | 7 (15)| 00:00:01 | |* 1 | HASH JOIN | | 107 | 10914 | 7 (15)| 00:00:01 | | 2 | TABLE ACCESS FULL| JOBS | 19 | 627 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| EMPLOYEES | 107 | 7383 | 3 (0)| 00:00:01 | --------------------------------------------------------------------------------
ORDEREDについて少し
ちなみにANSI準拠のSQLでも変わらずORDEREDは必要だった。
SQLの書きっぷりが結合順を表していそうに見えなくもないから勝手に解釈してくれるかと思ったけど、さすがにそんなことはなかった。
あとUSE_NL以外でも効いた。でもネステッドループ結合以外ではあまり使い出がないかも。。
参考
Oracle SQLチューニング講座(10):表の結合を極めるチューニング・テクニック (1/4) - @IT
いいところだけ引用します
ネステッド・ループ結合 ソート/マージ結合 ハッシュ結合 件数の多い表同士を結合し、全レコード出力する 不向き 結果を結合列でソートして出力する場合に有効。双方の結合列にNOT NULL制約が指定されており、索引が存在する場合、非常に効率的 システム・リソースに余裕がある場合には最適 一方の表に絞り込み条件を指定して表を結合し、少数のレコードを出力する 目安として索引を使用して表の15%以内の絞り込みであれば最適 不向き 目安として索引を使用して表の15%以上の絞り込みで、なおかつ等価条件があれば使用を検討
sqlplusでスキーマの持つインデックスの一覧を見たいんです
USER_INDEXESとUSER_IND_COLUMNSから引っ張ってくる。
SQL> conn hr/hr@xe SQL> col table_name format a16 trunc SQL> col index_name format a24 SQL> col uniqueness format a4 trunc SQL> col column_name format a24 SQL> select i.table_name, i.index_name, i.uniqueness, c.column_name 2 from user_indexes i, user_ind_columns c 3 where i.index_name = c.index_name 4 order by index_name,table_name,column_position 5 ; TABLE_NAME INDEX_NAME UNIQ COLUMN_NAME ---------------- ------------------------ ---- ------------------------ COUNTRIES COUNTRY_C_ID_PK UNIQ COUNTRY_ID DEPARTMENTS DEPT_ID_PK UNIQ DEPARTMENT_ID DEPARTMENTS DEPT_LOCATION_IX NONU LOCATION_ID EMPLOYEES EMP_DEPARTMENT_IX NONU DEPARTMENT_ID EMPLOYEES EMP_EMAIL_UK UNIQ EMAIL EMPLOYEES EMP_EMP_ID_PK UNIQ EMPLOYEE_ID EMPLOYEES EMP_JOB_IX NONU JOB_ID EMPLOYEES EMP_MANAGER_IX NONU MANAGER_ID EMPLOYEES EMP_NAME_IX NONU LAST_NAME EMPLOYEES EMP_NAME_IX NONU FIRST_NAME JOB_HISTORY JHIST_DEPARTMENT_IX NONU DEPARTMENT_ID TABLE_NAME INDEX_NAME UNIQ COLUMN_NAME ---------------- ------------------------ ---- ------------------------ JOB_HISTORY JHIST_EMPLOYEE_IX NONU EMPLOYEE_ID JOB_HISTORY JHIST_EMP_ID_ST_DATE_PK UNIQ EMPLOYEE_ID JOB_HISTORY JHIST_EMP_ID_ST_DATE_PK UNIQ START_DATE JOB_HISTORY JHIST_JOB_IX NONU JOB_ID JOBS JOB_ID_PK UNIQ JOB_ID LOCATIONS LOC_CITY_IX NONU CITY LOCATIONS LOC_COUNTRY_IX NONU COUNTRY_ID LOCATIONS LOC_ID_PK UNIQ LOCATION_ID LOCATIONS LOC_STATE_PROVINCE_IX NONU STATE_PROVINCE REGIONS REG_ID_PK UNIQ REGION_ID 21行が選択されました。
参考
BDDってなんですか
いくつか記事を読んだ。読みやすかった順に並べると……
- テスト駆動開発のテストは、テストか?-TDD から BDD へ:An Agile Way:ITmedia オルタナティブ・ブログ
- text.ssig33.com - RSpec の書き方について
- Twitter / kyanny: context 入れ子にしてテストを階層化する -> ...
- UKSTUDIO - BDDについて自分なりにまとめてみた
でもこれでは正直よくわからない。テストの戦略にTDDやBDDはどういう意図・位置づけで組み込まれるのかを知りたい。
これでもまだよくわからない。
これを読むとようやく他のページと内容がリンクしてくる。
まるで仕様書や設計書であるかのごとくテスト結果がアウトプットされることによって、設計のブラッシュアップに大きく貢献する。それがBDD。
アウトプットされた文章がなんだかおかしい場合は、そのクラスにあるべきでない処理になっている可能性がある、とか。
でもこの記事は途中から要件定義/受け入れテストの話に変わっちゃうんだよね。。まあでもそれは、JBehaveを分析行程に応用できるのではないかと気がついたからそうなったのであって、つまりBDD支援ツールたるこのJBehaveは当初「進化したJUnit」のような感じでスタートして受け入れテストに着地したということらしく。。
だから4つめに紹介したページのように「TDDでありATDDである」ということになる。
別に要件定義や受け入れテストの文脈でBDDを調べたのではないので。。とりあえず単体テスト的な部分でまとめ。
BDDはTDDの壮大な言い換え。テストコードにBDD特有の語彙(shouldとか?)を使うことで設計のブラッシュアップに大きく寄与する。
ここから(たぶん)私見。細かいデータのレベルではTDDもBDDもテストで確認することは結局同じ、と言う意味でTDD=BDDといっても間違いではない。ただ、TDDは正にテスト項目を挙げることから全てが始まるイメージ*1なのに対し、BDDは振る舞い(シナリオ)を挙げることから全てが始まるイメージ。
このレベルの話だとどこまでいってもニュアンスの問題になる感じで腑に落ちてこない。だからrspecとか具体的なBDDツールを例示して「ほらこんな感じで設計にも役立つんだよ」って話にした方が理解するのもしてもらうのも省エネな気がした。
それでそれで。今私のいるプロジェクトはScrum+BDDで単体テストがあまりないんだけど、BDDを調べてみてもやっぱりScrum+BDDだから単体テストは緩くていいなんて話は全然出てこなかった。。だいたいBDDとしても不完全でrspecのコードはかなり不足しているし。
プロジェクトが始まる前に「単体テストやりすぎると生産性が上がらない」と言われたので単体テストがなおざりなんだけど、今のプロジェクトって品質担保についてどう考えているんだ。。ScrumというかAgileの方を勉強すれば考え方が見えてくるのかな? そういえば読んでないなScrumの本……