そーくのつれづれぶろぐ

web系エンジニアの勉強したことなど

【読書メモ】マンガでわかる 必ず伝わる!ロジカル会話術

ロジカル・ダイアローグ(論理的に会話をする)方法について具体例とともに紹介している本。

https://www.amazon.co.jp/dp/B07GKW8MLV/r

ロジカル・ダイアローグ = 論理的に考える → 筋道立てて伝える 論理的に考える = 課題・問題を分ける → 分けたものを構造化する ⇛ 課題・問題を理解できる ⇛ 対策・改善ポイントを導きだせる

                           課題・問題を分けるときはMECE(もれなく・だぶりなく)を意識するとよい

筋道立てて伝える = 構造化した課題・問題に対して取捨選択をし、順序を導く → 相手の状況を考えつつ内容を伝える

                           話し方の順序は状況に応じて
                              相談・提案など ... PREP
                              お願い ... 起承転結

【読書メモ】世界一やさしい「思考法」の本 「考える2人」の物語

https://www.amazon.co.jp/dp/B00TFAFIC6/

戦略的=論理的=目的+(手段A+手段B...)
人に順序立てて明確に説明できなければすでに戦略的でない、というサイン

「目的に対してなにを考えればいいかわからない」場合にすること

  1. 「目的」を確認する
    • そもそも「目的」は正しいのか?疑う
      • もっと上位の目的はなにか、それを達成するためにふさわしい細分化した目的になっているか
  2. 「目的」からすべきこと「イシュー」をリスト化する
  3. イシューリストのアイテムを階層化・グルーピングする(=イシューツリー)
  4. 優先順位をつけて整理する

https://www.amazon.co.jp/dp/B00TFAFIC6/

所感

振り返ると過去に読んだ内容だけれども、身についてるかというと全然身についていないことを理解できた。
最近うまく説明できなかったり手戻りが目立つため改めて本を読んでみたが、基礎の部分がなっていないと反省...。目的は理解してないわ、イシューリストをMECEに分けられてないわ...時間は迫るわで余計に基礎がおろそかに...確実に習慣に落とし込む。

【PostgreSQL】やっぱり実行計画を読めるようになりたい!3つのおさらいポイント

はじめに

この記事は「ラクス Advent Calendar 2019」の11日目の投稿です。
昨日は@FlatMountainさんのJavaでArduinoを動かしてみた!でした。Arduino楽しそう...

いきなりですが、今年の3月に弊社技術ブログにて以下の記事を投稿しました。(id違いますが同一人物です)。まえがきを以下のように書いていました。

tech-blog.rakus.co.jp

...実行計画を読み解く記事を書こう!...と思いたったのですが、記事を書くにあたって ...(中略)...備忘も込めて当初予定していた記事を書く前に大量データの投入について述べたいと思います。

見逃してはいけない部分は "備忘も込めて当初予定していた記事を書く前に" ですね。

そうです、当初予定していた実行計画を読み解く記事を書く書く詐欺をして12月を迎えてしまったわけです。これは書かねばならぬと思いアドベントカレンダーの枠で投稿しようと決意しました。

私の経験ですが、入門記事や動画を見て「実行計画完全に理解した」みたいな気持ちで実務で実行計画を出してみた瞬間、己の無力さを噛み締め天を仰ぎました。 実務のSQLの実行計画を見ながら手探りでネットで検索しながら実行計画を見直し...を繰り返しようやくまあまあ読めるようになった、と今は思います。

この記事ではそんな私が振り返ってみて感じた実行計画を読むにあたって押さえるべきポイント3つを紹介したいと思います。

実行計画を読むポイント

  1. 実行計画のネストに注目する
  2. ノードがSQL文のどの句にあたるかを覚える&処理の内容を理解する
  3. 実行計画からSQLとリンクするキーワードを探す

実行計画を読む目的の多くは「パフォーマンス劣化を改善する」かと思います。なのでまずはパフォーマンス劣化している箇所を特定する必要があります。が、特定ができない...特定ができない原因はSQLと実行計画の対応付けができていないことではないでしょうか。この3つのポイントを押さえれば最終的に実行計画のどの行がSQLのどの句にあたるのかがわかる→パフォーマンス劣化を引き起こす箇所の特定ができるのではと考えています。

1. 実行計画のネストに注目する

入門記事ではだいたい書いてある気がしますが、あえてポイントとして挙げています。複雑な実行計画になればなるほどノードとSQLの対応付けが難しいです。30行くらいになるとひとつひとつ虱潰しに...となると心が折れそうになります。ですが、ネストの同じ階層を見ていくとだいたいサブクエリなどのひとかたまりの処理になっていますので、まずはネストを見ることでざっくりSQLのサブクエリや結合テーブルが実行計画のどのあたりに出現するのかを把握することができ、原因箇所の特定がしやすくなります。

2. ノードがSQL文のどの句にあたるかを覚える&処理の内容を理解する

これはかなり重要です。覚えておかないとそもそもどの処理をやっているのか全くわからないので英単語と一緒で最低限は知っておく必要があります。
ノードはかなりの種類があるのですが、SQLの処理からノードを対応付けて覚えると覚えることが少なく済むと思います。 実行計画をSQLに落とし込むことで初めてチューニングができるので、細かいノードの意味よりは先にSQLでいうとどこに当たるか?というところに目を向けるとよいでしょう。

SQLでやっている処理はたいてい「①結合して、②欲しい情報を探索して、③データを演算して、④ソートする」の4種類くらいで、次の表からわかるようにそれぞれ複数のノード(=処理方法)が存在しています。

処理 SQL 代表的なノード名
①結合処理 JOIN Nested Loop Join/Hash Join/Marge Join
②探索処理 WHERE/結合処理過程で内部的に実施など Index Scan/Seq Scan/Index Only Scan
③演算処理 GROUP BY/SUM/LIMITなど Aggregate/Hash Aggregate/Limit
④ソート処理 ORDER BY/結合処理過程で内部的に実施など Sort

ノードによっては別のノードを必ず呼び出すものもあるため、各ノードがどのような処理をしているのかを頭にいれておくことで、想定外のノードが出現する理由がわかるようになります。 SQLがどのように実行されているかを理解することで、パフォーマンス劣化の原因特定に1歩近づくでしょう。

3. 実行計画からSQLとリンクするキーワードを探す

WHERE句やON句などにでてくるカラム名やテーブル名は実行計画に書かれるため、ポイント1,2でアタリをつけたあと、詳細にSQLとの対応関係を把握するには、SQLにあるカラム名やテーブル名を探すことが有効です。また、テーブルやカラムにエイリアスをつけていると実行計画にもそのエイリアスが出現するので検索しやすく、原因特定の助けになります。

最後に

本当はケーススタディ式に簡単なSQLの実行計画を上述のポイントをおさえながら読み解くところまで書こうと思っていたのですが...。(そして更に難しいSQLで読み解くところも書こうと思っていたのですが...)

...ケーススタディ編は今年度中に書き上げてアップしたいと思います。読んでいただきありがとうございます!本日も皆さんにとって良い一日になりますように!

【Postgresql】オブジェクト識別子データ型とは ~C言語のint→charのキャストみたいなもの~【メモ】

前提

  • postgresqlはユーザ/システムが作ったDB関連の部品を'オブジェクト'として管理する
  • データベースクラスタ/スキーマ/テーブル/インデックス/シーケンス/データ型etc
  • 管理するので何かしらの情報をまとめる→postgresqlの世界では「システムカタログ」という(あとでざっくり図解を公開する)
  • オブジェクトにはすべて一意のIDをふる → oid
  • オブジェクト識別子 = oid
  • オブジェクト識別子は数値(10進数なはず)

oidに触れる機会の例

  • テーブルの特定カラムのデータ量が多すぎたらアラートメールを投げるようにする
  • 該当のテーブルがあればデータを取得する

↑の情報を取得するのにシステムで管理しているテーブルをオブジェクト識別子で結合しまくる必要があるが、 ユーザ側からみたらとってもみづらいSQLになる...

→そこで「オブジェクト識別子データ型」

オブジェクト識別子データ型

  • データ型
  • C言語のchar/intのキャストと同じイメージ
(int)'A';  // 65

char型「A」をintでキャストして「65」になるように、

'sales':: regclass

regclassというオブジェクト識別子データ型でキャストして「oid」になる便利なやつでした。

雑感

過去にやった言語のイメージがあったおかげで今回の記憶定着に役立った。
経験って大事。

参考URL

いつもお世話になっています。
lets.postgresql.jp

【PHP】ゆるやかな比較(==)は4ポイント覚えたらマスターできた

動機

https://www.php.net/manual/ja/types.comparisons.php

PHP特有の"ゆるやかな比較"を利用した書き方は簡便にかけるし可読性が高くてよい反面、思わぬ不具合の原因であったり日々の実装で思うように動かないときの原因にも結構なりえるので結構このページを参照している。

文字列→数値変換でハマったのをきっかけに今一度上記の表を眺め見ていたけれども、数点のポイントを覚えておけばあとはその場で考えてぱっと思い出せるなと気づいたため、忘れないうちに書き残すことにする。

覚えておくべき4ポイント

  1. NULLはFALSEと同義
  2. 空はFALSEと同義
  3. 0はFALSEと同義
  4. 式の中にstringと数値が両方存在している場合はstringが数値型に評価されてから比較される
    • stringの数値変換ルールは↓にまとめてます

soachr.hatenablog.com

雑感

入門書では1,2,3は言及されていた(3ヶ月前くらいに読んでいた)。JavaからのPHPだったため記憶に残ったのかもしれない。なのであとは文字列、数値の曖昧さを潰したらほぼほぼ大丈夫だったというのに過去記事を書き終わってから知った。 数値を文字列に変換は特に問題はないはずだからこれで実装時でハマることはなくなった気がする。よし。

【PHP】文字列から数値への変換法則をわかりやすく表してみた(PHPコードwith正規表現)

概要

PHPの本家に書いてあるけれども、文字列→数値の変換ルールを文字だけで説明されてるとすぐに頭に入らないので、擬似コードにした。 https://www.php.net/manual/ja/language.types.type-juggling.php

動機

直接的な動機ではないけれどもヒヤリハットとして頭に入れておいて損はしないだろうと思い記事を書くことにした。

脆弱性ツールによって開発中の画面にXSSが見つけられたので、その対応をすることになった。修正前に一度XSSを発生させよーとXSSを発生させる手順を踏んだはずが、なぜか発生しない事象に出会った。

少し掘り下げて書くと、整数を想定しているhiddenパラメータに悪意のある文字列(alert();だとか)を入れてsubmitすると実行されるという内容だった。そのためXSSを発生させるような入力値をいれてさあ脆弱性でてきてくれ!とsubmitをするが発生しない。なぜだ。といろいろ入力値を変えていたら発生する場合と発生しない場合があるというのが明らかになった。

発生しない場合というのが、その入力値がalert(123);というように数値が途中で入っていたときだった。おそらくフレームワークがフォーム値にバリデートをかけるときに数値が入っていれば数値に変換し、入っていなければ数値変換しない処理を入れているらしくXSSが発生しない、という内容だった。(逆に文字列onlyだったら通るのはフレームワークの不具合ではと思ったがとりあえずここでは割愛する)

PHP特有の「あいまいな表現でもOK」が仇となる場合もままあることと、webアプリの特性として「フォーム値(文字列)を数値として読む」ことは避けられないため、今後実装をする場合に考慮に入れることはそうとして一度整理していざというときにすぐに分かる形にまとめたいと思ったためここに記載する。

文字列から数値への変換法則をPHPプログラムで表現する

前置きが長くなったが、文字列からの数値への変換法則を書いてみる。

$VALID_NUM_PATTERNがいかんせんややこしい正規表現書いているけれどここで本家サイトの引用をば。以下の文章を正規表現で表した次第。

有効な数値データは符号(オプション)の後に、 1 つ以上の数字 (オプションとして小数点を 1 つ含む)、 オプションとして指数部が続きます。指数部は 'e' または 'E' の後に 1 つ以上の数字が続く形式です。

<?php

function convertStr2Num(string $str) {
  $VALID_NUM_PATTERN = '[-+]?[0-9]+([\.][0-9]+)?([e|E][0-9]+)?';
  $pattern = '/^(' . $VALID_NUM_PATTERN . ')/';
  
  // 1. 有効な数値データが文字列の0文字目から存在するか
  if (preg_match($pattern , $str, $matches)) {
    // 2. 小数点がない、指数部がない、integerの最大値より小さい 
    if ((strpos($matches[0], '.') === false) && 
        (strpos($matches[0], 'e') === false) &&
        (strpos($matches[0], 'E') === false) &&
          strlen($matches[0]) < PHP_INT_MAX) {
      return (integer)$matches[0];
    } else {
      return (float)$matches[0];
    }
  } else {
    return (int)0;
  }
}

終わりに

まとめるとこう↓

「整数じゃない値が文字列の最初に来ていたらfloat, 整数が文字列の最初に来ていたらinteger、それ以外は全部(integer)0」

書いている途中からこの1文がすべてと気づいてしまってちょっと消え去りたくなった。 私の正規表現記述能力が向上したのと、コード書かないと書くスピードが上がらないことを実感したから個人としては、有益、だったと...いうことで...