Tallman

技術とか読書とかいろいろ

超絶怒涛のゆるふわコードリーディング GraphQL gem篇

GraphQL gemをふんわり読んだ

ふんわり読んで見ました、とりあえずよく使われてそうなDSLをおったメモ
versionはv1.9.12 が最新リリースである時期のmasterブランチ読みました。
ゆるふわの極みです

とりあえず最初に公式のGetting Startedで出てくるやつ

module Types
  class PostType < Types::BaseObject
    field :id, ID, null: false
    field :title, String, null: false
    field :truncated_preview, String, null: false
    field :comments, [Types::CommentType], null: true,
      description: "This post's comments, or null if this post has comments disabled."
  end
end

field is 何?

GraphQL::Schema::ObjectがextendしてるGraphQL::Schema::Member::HasFieldsのメソッド。

def field(*args, **kwargs, &block)
  field_defn = field_class.from_options(*args, owner: self, **kwargs, &block)
  add_field(field_defn)
  nil
end

field_class is 何?

def field_class(new_field_class = nil)
  if new_field_class
    @field_class = new_field_class
  elsif @field_class
    @field_class
  elsif self.is_a?(Class)
    superclass.respond_to?(:field_class) ? superclass.field_class : GraphQL::Schema::Field
  else
    ancestor = ancestors[1..-1].find { |a| a.respond_to?(:field_class) && a.field_class }
    ancestor ? ancestor.field_class : GraphQL::Schema::Field
  end
end

引数はわたしてないので一個目の分岐は飛ばして、@field_classも(多分素のままだと)設定されてないのでGraphQL::Schema::Fieldか何がしかのクラスが返されているっぽい GraphQL::Schema::Field.from_optionsはオプションを色々整えた後newしてインスタンス返してくれるメソッド。

def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)

引数の数すごい

最終的に下のコードはGraphQL::Schema::Fieldかサブクラスかなにかのインスタンス作ってると言えそう。

  field_defn = field_class.from_options(*args, owner: self, **kwargs, &block)

add_field is 何

つぎはこの行

  add_field(field_defn)
def add_field(field_defn)
  if CONFLICT_FIELD_NAMES.include?(field_defn.original_name) && field_defn.original_name == field_defn.resolver_method
    warn "#{self.graphql_name}'s `field :#{field_defn.original_name}` conflicts with a built-in method, use `resolver_method:` to pick a different resolver method for this field (for example, `resolver_method: :resolve_#{field_defn.original_name}` and `def resolve_#{field_defn.original_name}`)"
  end
  own_fields[field_defn.name] = field_defn
  nil
end

:context, :object, :method, :class 書くと警告されるっぽい。 中身はハッシュで名前をキーにしてfield_class.from_optionsでつくったインスタンスをそのまま突っ込んでる。

んでこのハッシュは下記の2つのメソッドで使ってるっぽい

def fields
  # Local overrides take precedence over inherited fields
  all_fields = {}
  ancestors.reverse_each do |ancestor|
    if ancestor.respond_to?(:own_fields)
      all_fields.merge!(ancestor.own_fields)
    end
  end
  all_fields
end

def get_field(field_name)
  if (f = own_fields[field_name])
    f
  else
    for ancestor in ancestors
      if ancestor.respond_to?(:own_fields) && f = ancestor.own_fields[field_name]
        return f
      end
    end
    nil
  end
end

多分実行してるときに使ってるコード

public_sendしてますね。引数も渡せそう。
オブジェクトがもともとメソッドもってればなんでもフィールドにできる感じなのかな?

  def resolve_field_method(obj, ruby_kwargs, ctx)
    if obj.object.is_a?(Hash)
      inner_object = obj.object
      if inner_object.key?(@method_sym)
        inner_object[@method_sym]
      else
        inner_object[@method_str]
      end
    elsif obj.object.respond_to?(@method_sym)
      if ruby_kwargs.any?
        obj.object.public_send(@method_sym, **ruby_kwargs)
      else
        obj.object.public_send(@method_sym)
      end
    else
      raise <<-ERR
    Failed to implement #{@owner.graphql_name}.#{@name}, tried:

    - `#{obj.class}##{@resolver_method}`, which did not exist
    - `#{obj.object.class}##{@method_sym}`, which did not exist
    - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash

    To implement this field, define one of the methods above (and check for typos)
    ERR
    end
  end

前半の分岐がよくわからんけど @method_symはこんな感じでinitializeされるときに入れてる

method_name = method || hash_key || @underscored_name
@method_sym = method_name.to_sym

まとめ

ちょっと読んだ後公式のドキュメントみて使い方知らないとわけわからんことに気づいたのでここで終わりです。
OSSに貢献してる人これ無償で読んで理解して書いてるってすごすぎませんか??????

エンジニアとして働いて

エンジニアとして働き初めて半年がたった。まだまだわからないこと、初めて知ることばかりであり、周りの方の助けを借りながらなんとかやっている。
なんとなく書きたくなったのでエンジニアとして働き初めて感じたこととかをまとめようと思う。

エンジニアとして働くことは幸せなこと

エンジニアは幸せな仕事だと思う。自分がやっていることと仕事を簡単に結びつけやすい職業だ。仕事を人生にしやすいと感じる。
自分が趣味やら素振りやらで培った技術がすぐに仕事に活かせるし、そうでなくても原理原則をしれば仕事のツールとして絶対に効いてくる。
自分は現在教育分野での開発をしているのだけど、教育とITの相性もいいし教育とエンジニアの相性もとてもいいと思う。学んで試してを繰り返している人がエンジニアは多いように思うし、教育も自走できる人間を育てるのが一番の目指すところだと思う。自分ができなかったことができるようになっていくのも、知らなかったことを知るのも楽しい。
僕は大学までノホホンとしていて自分から学ぶことなんてゲーム以外でしなかったが、プログラミングを学び始めてからは知ることも試すことも全て自分から学びに行っている実感がある。誰に言われるでもなく自分が知りたいことを自分から学びに行くことがこんなに楽しいことだとは知らなかった。ましてやそれが実際に収入に結びつくなんて最高だ。
自分が頑張った分だけ良くなっていくという希望はこの仕事を通して初めて感じた。 転職した理由は身近で仕事を楽しそうに話す人がエンジニアだったという理由だったが間違いではなかったと思う。

いわゆる技術と同等に大事だと思うこと

ITエンジニアとしての技術力それ自体ももちろん大切だけど、半年働いてみてこれも重要だなぁと思うこともできた。

チームの雰囲気を良くしていく力

チームの効率を上げたりだとかも重要かもしれないけど、それ以上に雰囲気を和ますというか適度にチャットしたりおしゃべりできるような空気を作る力というのはものすごく大きい思う。心理的安全性というやつだろうか。いつも笑ってるぐらいがちょうどいいと思う。

他業種に対する敬意

売上を出しているのはコードだけではない。営業の人や方針をきめた経営者やユーザーの声を最初に聞いてくれる人、日々プロダクトとユーザーについて真摯に向き合っている人等大勢の人が総合して売上をだしている。
自分も前職のときは自分の組織が使っているシステムを必死に使っていた。他業種の大変さとかを理解するのは人間がわかりあうのは難しいので難しいが、少なくとも敬意をもって考えを聞く姿勢は常に持っているのが大事だと感じた。みんなそれぞれの考えを持って働いているのだ。

人に頼ること

わからないことをきちんとわからないということも大切だし、人間が一人でできることには限界があることを認めることも重要だと思う。
自分が余り知らない機能のコードがあったとして、自分でじっくりそのコードを読むこともいいが、その機能に詳しそうな人に伺うという行為をちゃんとすることも大事だと思う。得意でない事を得意な人に聞いている人というのは素晴らしい人だと思う。

これから

どうなるか知らんけど楽しく働いて行きたい

「試して理解 Linuxのしくみ」を読んだ

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

読んだ理由

せっかく自宅の環境をUbuntuにしたので

sasa5740.hatenablog.com

Linuxの基本的な知識知りたいなぁと思ったのと、A Tour of Goとかruby/rubyの並行テストとか見て並行処理の意味が全くわからんちんだったので、プロセスを詳しく知るために購入した。

全体的な感想

試して理解ということでサンプルコードがたくさん載っていた。最初はGitHubに全部上がってるの知らずにポチポチC言語写経しながら読んでいた。

実際にハードウェアにアクセスするのがカーネルで、普段僕が書いてるアプリケーションはあくまでプロセスとしてシステムコールのラッパー関数を呼び出しているだけ、という事からして初めて知ったので、全編に渡って新しい発見の連続だった。
あと著者の方の環境がメモリ32Gで人権を感じた。

要約感想的なもの

 3章

  • fork()関数もこの本で初めて知った。webサーバーがなんで同時に複数のリクエストをさばけるのか少しわかりかけてきた。

techracho.bpsinc.jp

(この記事の意味がようやく少しわかった。)

 4章 プロセススケジューラー

  • 論理CPU一個が同時に二つプロセス動かすことはない。プロセスはコンテキストスイッチでそのプロセス関係なく処理が一旦止まることがありうる。

  • 単位時間あたりの処理量と結果的な処理にかかる時間はトレードオフ

     5章 メモリ管理

  • メモリアドレスを直接扱うのはプロセスにはまだ早すぎるので仮想アドレスを使う。

  • ページテーブルはルーティングテーブルみたいなもの。コンピュータというのはどこも二分探索にするための工夫が多い気がする。

  • デマンドページング 仮想メモリ確保 != 物理メモリ確保

  • copy on write fork()時にいきなり新しい物理メモリを確保するのではない。 書き込むときに子プロセス用のメモリを確保して書き込む。

  • ヒュージページ 葉を増やすと枝も増える。仮想メモリと物理メモリの関係も増える。単位を大きくして防ぐ。 ubuntu 19.04ではトランスペアレントヒュージページのデフォルトはmadvise()だった。

     6章 記憶階層

  • メモリとキャッシュメモリは違うもの

  • キャッシュメモリに局所性(実際に使うところ)をいかにおさめるか

  • メモリにキャッシュしたものをダーティにして実際に変更してダーティを解除

  • ページテーブルもCPUにあるTLBで

  • Linuxはファイルを可能な限りページキャッシュしようとするって大規模サービス実践技術入門にもあった気がする。実際有無を比べると早さが違う

  • write backは自分の環境でも五秒に一回だった。想像の何百倍も間隔長かった。ダーティのまま中断は普通に起こりそう。 

     7章 ファイルシステム

  • ファイルシステムがないとお前がカーネルになるしかない

  • ファイルシステムでもcopy on write 書き換えるときはコピーする

     8章 ストレージデバイス

  • ファイルは連続あるいは近く

  • 連続する領域へのアクセスは一回で

終わりに

サンプルコードや実際に手を動かすところが多かったのも良かったけど、解説において過程を飛ばさないで一つ一つ全部図解してくれていたことがありがたかった。
どうやって並行処理の整合性保ってるのかはあんまりわかってない。 子プロセスで書き込みしたらcopy on writeで別の物理アドレスになってしまうのでは???
次は「なるほどUNIXプロセス Rubyで学ぶUnixの基礎」を買ったのでまた知識の上塗りをしていきたい。

rails consoleに簡単にオプションを足せるようになるgemを作った

作ったgemはこれです

github.com

使い方

config/application.rbに下のようにオプションをセットします。
Rails::Applicationの上に書くようにしてください。

Bundler.require(*Rails.groups)

D4C::Console.add_option 'hello' do
  puts "hello"
end

module YourRailsApplication
  class Application < Rails::Application

これでrails consoleに渡せるオプションを追加できました。実際にオプションが呼ばれると渡したブロックがcallされます。

rails c --hello
Running via Spring preloader in process 7922
hoge
Loading development environment (Rails 5.2.3)
irb(main):001:0> 

なんで作ったか

rails consoleを叩くときに、「デフォルトで実行されたくないけど実行したいときはさっと実行したい」処理があったからです。
職場でact_as_tenantというgemを使っています。dbのレコードに制限をかけるgemです。
act_as_tenantの都合上、rails consoleからdbのレコードを叩くには

ActsAsTenant.without_tenant do
end

として作業しなくてはなりませんでした。
とはいえRailtieconsoleメソッドでせっかくかけたセキュリティをデフォルトで外してしまうことはしたくありません。
--sandboxみたいにオプションで選択できると良いと考え、このgemを作りました。 1

実装の話

そもそもrails console --sandboxをした時の流れ

bin/rails console自体の具体的な流れはRailsガイドにとっても詳しく書いてあるので読みましょう。

railsguides.jp

では--sandboxはどうやって処理されているのでしょうか。 (注 Railsのコードを載せていますが色々省いています。)

Rails::Command::ConsoleCommandクラスは下のようになっています。

      class_option :sandbox, aliases: "-s", type: :boolean, default: false,
        desc: "Rollback database modifications on exit."

      def perform
        Rails::Console.start(Rails.application, options)
      end

受け取れるコマンドラインのオプションをclass_optionで追加しています。
Rails::Command::ConsoleCommandはThorというコマンドラインツールの作成を助けるgemを継承しています。class_optionはそのgemのメソッドです。

Rails::Applicationクラス内では--sandboxが入力されたかを保存できるようにしてあります。

    attr_accessor :assets, :sandbox

Rails::Consoleクラスでは、コマンドラインから渡されたオプションの有無をアプリケーションのインスタンスに渡しています。

    def initialize(app, options = {})
      @app     = app
      @options = options

      app.sandbox = sandbox?
    end

    def sandbox?
      options[:sandbox]
    end

これによりif app.sandboxで条件分岐できるようになります。

実際にD4Cで行った拡張

module Rails
  Application.prepend D4C::PrependOptionAccessor
  Console.prepend D4C::PrependOptionSetter
  Command::ConsoleCommand.prepend D4C::PrependClassOption
end

D4CのmoduleはD4C::Console.add_optionに渡されたオプションをRailsのクラスが扱えるようにするものです。 それぞれprependしてmoduleの中でsuperすることで処理を足しています。
D4C::Consoleの中身はオプションを構造体にして保存するクラスのインスタンスです。

Gemの名前

ジョジョの奇妙な冒険のスタンド名です。
最初はbaby_faceって名前でした、コンソールなので。
ディ・モールトベネなgemの名前だと思いながら作成して、いざ公開しようと思ったら同じ名前のgemが既にあって泣きました。皆さんはこうならないようにちゃんとrubygemsで名前を検索してから作りましょう。
最終的にたやすくオプションを足せるってところからD4Cにしました。
最後にこれが一番書きたかったんですけど、D4Cってまさに「Ruby」だと思うんですよね。

f:id:sasa5740:20190819222624p:plain


  1. ruby-jpでgemを晒したら「pryrcirbcに書くのも手ですがよりチームでコミットしやすいメリットもあるかも。」というお言葉もらいました。pryrcirbc書くのも手ですね。ありがとうございます。ruby-jpは本当に神。

クラスメソッドをPryのコマンドにできるGemをリリースしました

作ったGemはこれです

github.com

使い方

まず.pryrc内で下のようにクラスをセットします。

PrySingular.make_commands HogeJob, FactoryBot

これでPryの中で設定したクラスの特異メソッドをコマンドとして使うことができます。

pry(main)> perform_now

引数を渡すこともできます(あくまでコマンドなので引数との間はスペースが必要)

pry(main)> create :user

コマンドにしたいメソッドを直接指定することもできます。

PrySingular.make_commands FactoryBot, only: [:build, :attributes_for]

コマンドにしたくないメソッドを指定することもできます。

PrySingular.make_commands FactoryBot, except: :create

コマンドなのでObjectを汚してメソッドを生やすことはありません。

[1] pry(main)> self
=> main
[2] pry(main)> self.respond_to?(:create)
=> false

また、内部ではsingleton_methods(true)を使っているので、classとかrubyのコアな部分まではコマンドを設定しません。

f:id:sasa5740:20190715124804j:plain
超絶怒涛の神issueとPRを立ててくれた@fursichさんありがとうございます!*1

作った理由

FactoryBotのcreateとかActsAsTenantのwithout_tenantとかをいちいちrails cの中で書くのめんどい〜みたいな話が周りで起こり、.pryrcの話がにわかに盛り上がりました。そのとき職場の人がPry::ClassCommandでゴニョゴニョといっていたのを聞いたのが始まりです。
最初はFactoryBotとPryを連携させたいな〜と思って作っていたのですが、書いていくうちに「これクラス設定してもらって全部できるようにしたほうが便利だな」と思って実装してみました。

実装

コアな部分はここです

def import_class_command(klass, options)
  singular_methods = adapt_options_singleton_methods(klass, options)
  set_pry_command do
    singular_methods.each do |klass_method|
      command "#{klass_method}", "#{klass}.#{klass_method}" do
        extend PrySingular::Slop
        klass.class_eval <<-EOS
          #{parse_singular_method_command(Readline::HISTORY.to_a.last)}
        EOS
      end
    end
  end
end

def set_pry_command(&block)
  commands = Pry::CommandSet.new &block
  Pry.config.commands.import(commands)
end
module PrySingular
  module Slop
    def parse_singular_method_command(items)
      method, args = items.split(" ", 2)
      method + ' ' + args.gsub(' ', '')
    end
  end
end

commandはStringオブジェクトをメソッドの名前、メソッドの説明の順に引数に取って、コマンドが呼ばれたらブロックの中身を実行してくれるPryのメソッドです。
仕組みとしてはコマンドが呼ばれたらReadline::HISTORYの最新のものを一度メソッドと引数に分けてから引数の空白を削除し、対象のクラスをレシーバーにclass_evalして直接実行しています。

pry(main)> create :user

と呼ばれたら最終的に

FactoryBot.class_eval <<-EOS
  create :user
EOS

が実行されるといった具合です。 なのでコマンドの実装の中身は全て、クラスにReadlineから受け取った文字列をズルっとわたしているだけです。

Gemを作ってみて

Gem作成は初めてでしたが、RubyGemsのおかげで簡単に公開することができました。
PryのDSLの柔軟性にも助けられましたし、オプションの設定方法もRailsのパクリです。 巨人の肩は高い 。
反省点としてはリリースしてはだめじゃんを繰り返して既にversionが0.1.7なことですね…
まだまだテストもカバレッジ足りないしリファクタもできると思うし、そもそもまだバグもあるかもしれないので、よいGemにできるようやっていきです。
よかったら使ってみてください、バグとかこれがあったら面白いとかあればissueやコメントで教えてくれたら泣いて喜びます。

2019/07/21 追記

インターフェースをmake_commandsにしたり内部のコードいろいろかえました

*1:ブログに乗せることも快く了承いただき本当にありがとうございます

個人開発用マシンをThinkPad X1 Carbon with Ubuntuにしました

経緯

個人の開発マシンはプログラミング始めた去年の6月にMBP2014の13インチをヤフオクで落札して以来ずっとそれを使っていたのですが、5月末くらいにコーヒーを盛大にこぼしてトラックパッドが使用不能に…。
最新のMBPの13インチか迷ったけどLinux使ってみたかったのでThinkPad x1 CarbonにUbuntu入れて使うことにしました。
ThinkPad x1 Carbonにした理由は

です。いろいろいれたら17万くらいになっちゃいました。

スペック

  • Coffee Lake Core i7-8550U
  • 16GB RAM
  • 14.0型WQHD液晶
  • M.2 SSD180 (ぶっちゃけ200もいらないので減らした)
  • 英字キーボード

こうみるとそんなに盛ってないですね

ふぁーすと・いんぷれっしょん

f:id:sasa5740:20190708001839j:plain
ロゴがかっこいい
カーボンの天板の高級感と手触りは◎。キーボードも噂に違わぬ押しやすさです。 そしてなんといっても軽量です。前回のMBP2014が13インチが1.57 kg、職場のMBP2017の15インチが1.83kg。比較してThinkPad X1 Carbon G6は1.13kg。持つとわかりますが本当に軽いです。あと端子がまとも
スピーカーが背面にあるのだけ悲しい

Ubuntu入れてみて

ThinkPadLinuxってポピュラーな使い方だし大丈夫だろ〜と思ってたのですが結構苦労しました…
トラックパッドトラックポイント(赤ポチ)が反応せず、ググりまくって色々試してもどこか治るとどこか動かなくなる状態で途方にくれていました。
結局18.04から19.04にアップデートしたら治ったので良くわからない。 18.04でも使いたいって人がいたらこれをいれるのがいいかと思います。 https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1791427/comments/94
スリープ状態でもバッテリーがガンガン消費される問題もありました。BIOSLinux用の設定に変えれば解決しますが。
現在もbluetoothの音質がかなり悪く、なんとかならんかと試行錯誤しています。

Macとの違い

そんなにないです。⌘キーって便利だったんだなぁというくらい。 tweak tool入れてaltをctrにするといい感じ。現状はかなり快適です。
Dockerいれるのに19.04だと公式通りにやってもうまく行かなくてちょっとハマりましたが、Dockerそのものは明らかにMacより軽いです。キビキビ。 ワークスペースやウインドウの管理なんかも楽ですしスムーズです。
とりあえず入れて正常に動くことを確認したもの

chrome入れれば環境はほとんど同期したも同然だし、シェルもfish使ってるのでチョチョっとプラグイン入れるだけですみました。 今はlinuxbrewもあるのでこれからも困ることはそんなにないかと思います。

f:id:sasa5740:20190708002905p:plain
テーマもデフォルトで十分いい感じだと思います

まとめ

やっぱり黒一色っていいですね。秋葉に黒い人が多いのもしょうがない。
Linuxのしくみ」とかこれを使って実践しながら読んでいきたいと思います。
後いざThinkPadで競プロや!ってABCでたらB以降さっぱりできなくて泣いた。

SQL実践入門を読みました

SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)

SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)

SQLアンチパターンや失敗から学ぶRDBの正しい歩き方を読んでいて、ウインドウ関数や結合などまだまだ浅い理解だなーと思って買いました。
以下、気になったことと感想を書きます。

where句で条件分岐するのは素人、プロはSELECT句で条件分岐する。

どういうことかというと 例えば 名前、性別、部署を持つテーブルがあったとして

社員名簿テーブル

名前 性別 部署
太郎 営業
花子 営業

男性と女性人数を部署ごとに出したいとき whereを使うと

SELECT  部署, 
COUNT(*)
FROM 社員名簿
WHERE 性別= ''
GROUP BY 部署;

SELECT  部署, 
COUNT(*)
FROM 社員名簿
WHERE 性別= ''
GROUP BY 部署;

という感じでUNION使っても二回テーブルスキャンしなくてはいけないところを

SELECT  部署, 
  SUM( CASE WHEN sex = '' THEN 1 ELSE 0 END),
  SUM( CASE WHEN sex = '' THEN 1 ELSE 0 END)
FROM 社員名簿
WHERE 性別= ''
GROUP BY 部署;

というように書くことができます CASE式はWHENに当てはまればそこで評価を終了するので先程のWHERE句での分岐より処理も早いです。

Joinの際に駆動表はなぜ小さくするのか

ネステッドループで結合する際に、駆動表はフルテーブルスキャンが行われますが内部表はインデックスが使われます。
どうせフルテーブルスキャンするなら小さいほうが良いし、大きなテーブルスキャンするならインデックス使ったほうが良いので、駆動表は小さいほうがいいんですね。
ハッシュで結合するのは結合するテーブルが同じくらい大きいサイズのときにも有効ですが、メモリを大量に消費するので注意ともありました。

ぐるぐる系とガツン系

ぐるぐる系というのはいわゆる繰り返し処理(ループ)のことです。 一行ずつレコードを引っ張ってきて簡単な処理する、という行為のことですね。
対してガツン系はCASEやウインドウ関数等を駆使して処理を一回で記述してしまうことです。
シンプルでトランザクションの制御が容易だが、チューニングしにくくパフォーマンスもでにくいぐるぐる系と、チューニングしやすくパフォーマンスもでやすいが、トランザクションの制御やオプティマイザに委ねる部分が大きいガツン系か、そのトレードオフを認識する必要があるとのことです。
もともとSQLはループをできるだけ排除したい、という思想の元で生まれたそうで、手続き型言語になれている人ほど処理を全部自分で書いてループさせがちなので、ガツン系のメリットをしっかり知っておくのが重要です。

インデックスが使えないとき、役にたたない時

「失敗から学ぶRDBの正しい歩き方」のありましたが、

  • IS NULL

  • 列に直接演算

これらで検索するときはインデックスが使えません。
なぜかというと、インデックスの中に存在する値はあくまでそのセルの中にある値であって、演算後の値でも、NULLでもないからです。
選択率が高い(取り出すレコードがテーブル全体に対して割合が高い)ときも要注意です。

感想

SQLの当たり前に使われている用語に対して説明、考え方が乗っている良書でした。
ウインドウ関数や結合についてはなんとなくで使っていることが多かったので、クロス結合やパーティショニングのカットのイメージから説明してくれる本書はありがたかったです。 また全体を通してトレードオフというものを意識して書かれてあるとも感じました。
それぞれのアルゴリズムや手法にメリットデメリットが書かれていて、それを把握した上で活用するのが大事だということが繰り返し述べられていた印象です。