ぱと隊長日誌

ブログ運用もエンジニアとしての生き方も模索中

SQLの外部結合の実行ステップを理解する

はじめに

プログラマのためのSQL 第4版(以下、「訳書」とする)「25.3.1 外部結合の歴史」には外部結合の実行ステップおよび実行例が記述されています。

なお、該当の記述は4刷で一部訂正が入っています。正誤表は書籍詳細ページよりご確認ください。
プログラマのためのSQL 第4版 すべてを知り尽くしたいあなたに(ジョー・セルコ ミック ミック)|翔泳社の本

ただ、原書である『Joe Celko's SQL for Smarties』第4版(以下、「原書」とする)と訳書では、訂正結果を踏まえても相違する記述があるようです。

また、原書・訳書共に説明のやや分かり辛い点があるように思われます。

そこで、本の説明をベースとして外部結合の実行ステップについて補足を行います。

ANSi-89 Standard in SQL Server 2008
この質問スレッドで "Buy me a beer ot three and I will tell the story of the old ANSI X3H2 committee," から始まる回答の書き込みは本の著者であるセルコ自身によるものと思われ、原書の記述とほぼ同じになっています。併せてご参照ください。

外部結合の実行ステップ

外部結合の実行ステップについて、訳書では以下のように説明しています。

  1. まず2つのテーブルをクロス結合する。そして結果セットの行を1行ずつスキャンする。
  2. もし条件が TRUE になる行があればその行を保持する。
  3. もし条件が FALSE または UNKNOWN になれば、その行については保存対象のテーブルの列を保持し、保存対象でないテーブルのすべての列を NULL に変換する。最後に重複行を削除する。

一方、原書では以下の説明となっています。

  1. We build the CROSS JOIN of the two tables. Scan each row in the result set.
  2. If the predicate tests TRUE for that row, then you keep it. You also remove all rows derived from it from the CROSS JOIN.
  3. If the predicate tests FALSE or UNKNOWN for that row, then keep the columns from the preserved table, convert all the columns from the unpreserved table to NULLs, and remove the duplicates.

原書・訳書共にほぼ同じ説明と見えますが、原書の 2. にある "You also remove all rows derived from it from the CROSS JOIN." が訳書にはありません。そして、これは外部結合で重要な処理の一つです。これを次の実行例で確認します。

外部結合の処理例

外部結合の実行ステップを実行例で確認します。

原書・訳書記載の例

以下に示す2つのテーブルがあるとします。

[Table1]

a b
1 w
2 x
3 y
4 z

[Table2]

a c
1 r
2 s
3 t

本で例に挙げられている以下の外部結合を考えます。

Table1 
LEFT OUTER JOIN 
Table2
ON Table1.a = Table2.a -- JOIN condition
   AND Table2.c = 't'; -- single table condition

なお、訳書4刷以降では異なった例に修正されていることをご注意ください。今回は原書に倣って説明を進めます。

ここでは "LEFT OUTER JOIN" なので、保存対象のテーブルは演算子左側の "Table1" となります。

(1)
2つのテーブルをクロス結合します。そして結果セットの行を1行ずつON句の条件で評価します。

▲ = "Table1.a = Table2.a" が成立する。
● = "Table2.c = 't'" が成立する。

a b a c 評価結果
1 w 1 r
1 w 2 s
1 w 3 t
2 x 1 r
2 x 2 s
2 x 3 t
3 y 1 r
3 y 2 s
3 y 3 t ▲● ON句の条件が TRUE になる行集合
4 z 1 r
4 z 2 s
4 z 3 t

(2)
ON句の条件が TRUE になる行を保持します。また、その行から派生した (derived) 全ての行を削除します。
「派生した」の解釈ですが、保存対象テーブルでON句の条件が TRUE になったのと同じ行からクロス結合結果が生み出され、かつON句の条件を満たさない行です。

a b a c 評価結果
1 w 1 r
1 w 2 s
1 w 3 t
2 x 1 r
2 x 2 s
2 x 3 t
3 y 1 r 派生した行なので削除する
3 y 2 s 派生した行なので削除する
3 y 3 t ON句の条件が TRUE になる行集合
4 z 1 r
4 z 2 s
4 z 3 t

クロス結合結果でON句の条件にて TRUE となった行に保存対象テーブルの {3, y} の行が含まれています。この {3, y} の行からクロス結合が生み出され、かつON句の条件を満たさない行が派生した行として削除対象になります。

(3)
条件が FALSE または UNKNOWN の行について、保存対象のテーブルの列を保持し、保存対象でないテーブルのすべての列を NULL に変換します。最後に重複行を削除します。
重複行の判定は保存対象テーブルの同じ行から生み出されたクロス結合結果の行同士で行います。

a b a c 評価結果
1 w NULL NULL
1 w NULL NULL 重複行なので削除する
1 w NULL NULL 重複行なので削除する
2 x NULL NULL
2 x NULL NULL 重複行なので削除する
2 x NULL NULL 重複行なので削除する
3 y 3 t ON句の条件が TRUE になる行集合
4 z NULL NULL
4 z NULL NULL 重複行なので削除する
4 z NULL NULL 重複行なので削除する

(4)
最終的に以下の結果が得られます。

a b a c
1 w NULL NULL
2 x NULL NULL
3 y 3 t
4 z NULL NULL

この結果は訳書と異なりますが、原書とは一致しており、ジョー・セルコが本来意図したものとなります。

なお、訳書には原書と異なった結果と共に『あとは、この結果に「Table2.c = 't'」というSARGの条件を適用すれば、最終結果は1行に絞り込まれる。』とありますが、この説明は原書に見当たりませんでした。

より複雑な例

同じ値の行が複数あったり、NULLの行が含まれている例を考えます。

[Table3]

k3 v3
1 AAA
2 BBB
2 BBB
NULL NULL

[Table4]

k4 v4
1 aaa
1 aaa
3 ccc
NULL NULL
Table3 
LEFT OUTER JOIN 
Table4
ON Table3.k3 = Table4.k4;

(1)
2つのテーブルをクロス結合します。そして結果セットの行を1行ずつON句の条件で評価します。

k3 v3 k4 v4 評価結果
1 AAA 1 aaa ON句の条件が TRUE になる行集合
1 AAA 1 aaa ON句の条件が TRUE になる行集合
1 AAA 3 ccc
1 AAA NULL NULL
2 BBB 1 aaa
2 BBB 1 aaa
2 BBB 3 ccc
2 BBB NULL NULL
2 BBB 1 aaa
2 BBB 1 aaa
2 BBB 3 ccc
2 BBB NULL NULL
NULL NULL 1 aaa
NULL NULL 1 aaa
NULL NULL 3 ccc
NULL NULL NULL NULL

NULL = NULL は UNKNOWN となります。

(2)
ON句の条件が TRUE になる行を保持します。また、その行から派生した (derived) 全ての行を削除します。

k3 v3 k4 v4 評価結果
1 AAA 1 aaa ON句の条件が TRUE になる行集合
1 AAA 1 aaa ON句の条件が TRUE になる行集合
1 AAA 3 ccc 派生した行なので削除する
1 AAA NULL NULL 派生した行なので削除する
2 BBB 1 aaa
2 BBB 1 aaa
2 BBB 3 ccc
2 BBB NULL NULL
2 BBB 1 aaa
2 BBB 1 aaa
2 BBB 3 ccc
2 BBB NULL NULL
NULL NULL 1 aaa
NULL NULL 1 aaa
NULL NULL 3 ccc
NULL NULL NULL NULL

{1, AAA} は4行ありますが、内2行は「ON句の条件が TRUE になる行集合」のため、残り2行が「派生した行」として削除されます。

(3)
条件が FALSE または UNKNOWN の行について、保存対象のテーブルの列を保持し、保存対象でないテーブルのすべての列を NULL に変換します。最後に重複行を削除します。

k3 v3 k4 v4 評価結果
1 AAA 1 aaa ON句の条件が TRUE になる行集合
1 AAA 1 aaa ON句の条件が TRUE になる行集合
2 BBB NULL NULL
2 BBB NULL NULL 重複行なので削除する
2 BBB NULL NULL 重複行なので削除する
2 BBB NULL NULL 重複行なので削除する
2 BBB NULL NULL
2 BBB NULL NULL 重複行なので削除する
2 BBB NULL NULL 重複行なので削除する
2 BBB NULL NULL 重複行なので削除する
NULL NULL NULL NULL
NULL NULL NULL NULL 重複行なので削除する
NULL NULL NULL NULL 重複行なので削除する
NULL NULL NULL NULL 重複行なので削除する

{2, BBB, NULL, NULL} は8行ありますが、クロス結合の基となった行が異なれば重複行と判定しないため、削除されるのは内6行となります。

(4)
最終的に以下の結果が得られます。

k3 v3 k4 v4
1 AAA 1 aaa
1 AAA 1 aaa
2 BBB NULL NULL
2 BBB NULL NULL
NULL NULL NULL NULL

おわりに

外部結合はクロス結合がベースとなり、そこから保存対象外の行が削除される、という流れを理解することが大切です。これを理解していれば、結果にどのような行が現れるかを説明できます。

ここで取り上げた内容以外にも、ON句の後にWHERE句が実行される、というルールがあります。このため、条件をON句に記述するのか、WHERE句に記述するかで結果が異なってきます。詳細は訳書「25.3.1 外部結合の歴史」の「業者と部品」の例を参照ください。

外部結合の実行ステップについて、原書と訳書の説明に差異があること、また原書においてもクロス結合結果からどの行を削除すべきかの説明にあいまいな点があり、理解し辛くなっていました。この記事が理解の一助となれば幸いです。また、私の解釈に誤りがあればコメントをいただけると大変ありがたいです。

原書と訳書の説明の差異については出版社(翔泳社)に問い合わせを行っています。回答および何かしら公開できる情報がありましたら、ここに追記したいと思います。

NTTDATATC2019「PostgreSQL 12 は ここがスゴイ!」聴講メモ

はじめに

NTTデータ テクノロジーカンファレンス 2019(NTTデータ テクノロジーカンファレンス 2019 ~ 未来を創る NTT DATA の確かな技術力 ~
【テクノロジーPostgreSQL 12 は ここがスゴイ!~性能改善や pluggable storage engine などの新機能を徹底解説~
スピーカー:NTT OSSセンタ Software Engineer 澤田 雅彦 氏 (PostgreSQL コントリビュータ)
の聴講メモです。

現時点で資料の公開はありませんが(今後公開される可能性はあるようです)、澤田さんが別の機会に登壇された際の資料が公開されており、今回の講演と内容の一部が重複していました。こちらをご参照ください。

【参考】としている個所は私が挿入しています(補足や参考資料など)。登壇者の講演内容ではありませんので、その旨ご了承ください。

聴講メモ

PostgreSQL 12 のリリース

PostgreSQL 12(以下、"12"のようにバージョンのみの略記とする)は2019年秋のリリース予定。現時点(2019/09)で12はベータ3の状態。2019/10にはリリースされるかも。

PostgreSQLは毎年メジャーバージョンがリリースされ、ほぼ同じリリーススケジュールになっている。

12のリリースと同時に9.4のコミュニティサポートが切れることに注意が必要となる。


【参考】
PostgreSQL: Versioning Policy
この記事公開時点で9.4のサポート停止は2020年02月13日となっています。最新の情報はリンク先でご確認ください。

12の特徴を下記に挙げる。

  • 既存機能の大幅改善
  • 将来の機能拡張への基盤
  • かゆいところに手の届く機能
  • 珍しく大きめの非互換

テーブル・パーティショニング

テーブル・パーティショニングの改善は12の改善全体の15パーセントを占めるぐらい活発に開発されている。

パーティション・テーブルへの外部キー制約が実現された。

パーティション・プルーニングの性能改善が行われた。これによりプランニングの時間が改善された。11でも早くなっていたが、子テーブル数に比例してプランニングの時間 O(log N) がかかっていた。12では数千の子テーブル数があっても時間がほとんどかからない。12ではプルーニング情報を求めた後に興味のある子テーブルの管理情報だけを作成するようになったため。

子テーブルの数が少ない内は11でも12でも大きな差は無いが、数千以上の子テーブルを持つような大規模な環境では有用となる。


【参考】
第2回「PostgreSQL11でのテーブル・パーティショニング機能の改善」 | NTTデータ先端技術株式会社
11での記事ですが、PostgreSQLパーティション・プルーニングの動作が解説されています。

CTE inlining

CTEとはSQLのWITH句のこと。再帰クエリに利用できたりする。

PostgreSQLの実装ではCTEの結果を常に一時テーブルとして保持している。これはワークメモリに格納され、そのサイズを超えるとディスクに書き出されて遅くなる。

11まではCTEとサブクエリで等価なクエリを書いても実行計画が異なっていた。CTEは常に一時テーブルを作る分遅くなるケースがあった。12ではCTEをインライン展開することでサブクエリの場合と同様に高速で処理することができる。

CTEのインライン展開は他のRDBMSでもデフォルトの動作になっている。なお、再帰などの場合はマテリアライズ(一時テーブル扱い)となる。また、MATERIALIZED句を使えば11以前の動きにできる。


【参考】
PostgreSQL 12の新機能:CTEの高速化
CTEについての概説と12での改善について解説されています。

VACUUM処理の改善

VACUUM処理は以下の順で進む。
(1) テーブルスキャン
Visibility Map でゴミのあるページを特定する。この処理は早い。
(2) インデックスVACUUM
1行でもゴミがあればインデックスをフルスキャンする。この処理は遅い。
(3) テーブルVACUUM
ゴミのあるページのみをVACUUMする。
(4) 末尾の切り詰め
共有バッファをフルスキャンする。この処理の間はテーブルをロックしているため、コンカレントにSELECTやUPDATE処理を行うことができない。この処理も長時間かかることがある。

VACUUMで時間のかかる処理に対応するためのオプションが増えた。
(1) INDEX_CLEANUPオプション
offだとインデックスが肥大化する可能性がある。大規模テーブルで頻繁に更新がかかるとか、定期的にreindexできるなら有効となる。
(2) TRUNCATEオプション
offにするとテーブルの物理サイズが小さくならない。高頻度で更新されたり、共有バッファが大きい場合は有効となる。


【参考】

□ インデックスに対する VACUUM 処理
WITH 句に VACUUM_INDEX_CLEANUP = OFF を指定することで、インデックスに対する VACUUM 処理を無効にすることができるようになりました。デフォルト値は ON で、従来通り VACUUM が行われます。
□ テーブル終端の空きブロック開放
テーブルの属性に VACUUM_TRUNCATE が追加されました。VACUUM 実行時にテーブル終端の空きブロックを解放するかを決定します。デフォルト値は ON で、従来と同様に空き領域を解放します。OFF に指定するとこの動作を行いません。

PostgreSQL 12 新機能検証結果 (Beta 1)



genelated columns

生成列とも呼ばれる。他の列情報から計算式によって値を生成する列のこと。計算した値が実際に格納される。都度計算するオプション(VIRTUAL)には非対応となっている。

create statistics

拡張統計情報について。PostgreSQLは行数推定で列の値の独立性を仮定している。これを事象の独立性という。


【参考】
「行数推定」と「事象の独立性」の関係については以下の資料が分かりやすいです。

例として住所テーブルを考えてみる。PostgreSQLは「都道府県」列と「市町村」列を独立していると仮定しているが、実際には「東京都」と「千代田区」は関連しているように、これらの列が独立とは言えない。結果として行数推定が過少見積もりになってしまう。

この問題に対応するため、相関関係のある複数列にまたがった統計情報をとれるようになった。この機能は10からあったが、12からは最頻値もとれるようになった。

プラガブル・ストレージ・エンジン

PostgreSQLに独自のテーブル構造を組み込めるようになった。Oracleのような更新型(追記型でない)エンジンとか、インメモリのエンジンを組み込めるようになる。

同じくエンジンを差し替えられるMySQLとの違いとして、PostgreSQLではテーブル操作に関わる部分だけ差し替えられるようになっている。これにより、独自エンジンでもPostgreSQLの既存機能を利用可能となっている。

影響の大きそうな非互換

with oids が廃止になる。PostgreSQL の OID は Oracle の ROWNUM のような使い方ができたが、今後はこれができなくなる。with oids があるテーブルはそのままアップグレードできないことに注意。

recovery.conf が postgresql.conf に統合された。これによりリカバリー設定をreloadで反映できるなど恩恵がある。
12以降は recovery.conf があると起動しない。運用やバックアップツールの改修が必要となる。


【参考】
PostgreSQL 12 新機能検証結果 (Beta 1)
3.1.2. recovery.conf ファイルの廃止
この章に解説があります。

その他の資料

本講演とは関係ありませんが、PostgreSQL 12 の新機能説明資料でまとまっており、最近発行されたものを紹介いたします。

PostgreSQL 12 新機能検証結果 (Beta 1)(著者:日本HP 篠田 典良 さん)

PostgreSQL 12 新機能解説(著者:SRA OSS 近藤 雄太 さん)

NTTDATATC2019「横浜銀行のマルチクラウド戦略とそれを支える「A-gate」」聴講メモ

はじめに

NTTデータ テクノロジーカンファレンス 2019(NTTデータ テクノロジーカンファレンス 2019 ~ 未来を創る NTT DATA の確かな技術力 ~
【事例】横浜銀行のマルチクラウド戦略とそれを支える「A-gate」
の聴講メモです。

横浜銀行のA-gate採用はNTTデータニュースリリースにも掲載されています。
横浜銀行にてパブリッククラウド活用ソリューション「A-gate™」を採用 | NTTデータ
この採用に至る経緯を横浜銀行から、A-gateの提供するサービスについてNTTデータから説明が行われました。

聴講メモ

A-gate採用の経緯

スピーカー:横浜銀行 ICT推進部 主任調査役 森口 富裕 氏

横浜銀行クラウドを導入した理由は「アジリティー獲得」と「コスト削減」のため。
(1) アジリティー獲得
自前の仮想化基盤を保有しているが、その基盤のハードウェア増強は物理サーバーと同様のスピード感になってしまう。また、ハードウェア増強に伴う1回の投資額が大きくなる。これを解決したかった。
(2) コスト削減
パブリッククラウドの検討当初、どのベンダーからもクラウドは安いものではなく、構築期間短縮の為に使うものであるといわれてきた。それが2018年後半からコストメリットのある試算が出るようになってきた。自前の仮想化基盤とAzureでのコストを比較すると、システムによってコストがトントンだったり逆に増えるケースもあったが、大幅に減らせるケースもあった。

横浜銀行がマルチクラウドを導入した理由について。
クラウドといえど、いつ障害が起こるかわからない。いつ価格改定があるかわからない。こうしたリスクを踏まえつつ、オンプレミスとのバランスをとりたい。
クラウドベンダーを比較すると、今はAWSが優勢だがAzureが追い上げている。この先の展開はわからない。
クラウドベンダー毎に特徴があり、それぞれに合わせていかないといけない。

クラウドの課題として「ID管理」「ネットワーク」「ストレージ」があった。
(1) ID管理
オンプレミスであればシステム毎にIDを管理すればよかった。
クラウドではアカウント毎に数百以上のアクションを付与すべきか否か決めなければいけない。
(2) ネットワーク
オンプレミスであれば外部とのネットワークは意図して敷設するものであり、管理が明確だった。
クラウドでは外部とのネットワークが設定だけで可能となる。S3のようなストレージのアクセスコントロールであったり、サーバの配置などを逐一考えなければいけない。
(3) ストレージ
オンプレミスであれば物理的なストレージがデータセンター内にあるため、簡単に触れられないし、外部にさらしてしまうリスクも低い。
一方でクラウドでは設定ミスで外部にさらしてしまうリスクが高い。

クラウドであっても正しく管理できれば安全といえる。ただ、その管理を正しく行うにはノウハウやコストが必要となる。これをA-gateで解決した。

「オンプレミス」と「クラウド」における開発体制の違いについて。
(1) オンプレミス時代
横浜銀行SIer - 開発ベンダー(複数)という階層構造になっていた。
SIerが間に入って複数の開発ベンダーに依頼することで、開発をスケールしやすいというメリットがあった。
(2) クラウド時代
開発ベンダーがSIer抜きで直受けしたいと提案してきた。これだと横浜銀行の管理負担が大きい。また、開発基盤がバラバラになる。
ここにA-gateを間に挟むことで開発の共通基盤としつつ、これまでの体制(横浜銀行SIer - 開発ベンダー)を維持することができた。

A-gateで提供するサービス

スピーカー:NTTデータ 第二金融事業本部 金融ソリューション事業部 課長 伊藤 利樹 氏

A-gateは「スタートアップコンサル」「仮想データセンター」「マネージドCCoE」から構成されている。
(1) スタートアップコンサル

  • 基礎知識の提供
  • ガバナンス体制及び構築支援
  • ルール整備支援

これらについて、NTTデータのこれまでの構築ノウハウから落としどころを提示できる。
(2) 仮想データセンター

  • 権限分掌機能
  • ポリシー違反検知修復機能

(3)マネージドCCoE

  • ガイドラインの整備と更新
  • 新サービスの検証と審査
  • 初期払出機能

ID管理の支援について。
アカウントは請求の単位であり、権限分掌の単位でもある。
セキュリティガバナンスを考慮し、システムや環境単位でのアカウント分離を推奨している。これにより、オペミス防止・影響の極小化・請求内容の明確化が期待できる。
組織として「役割の定義」と「各操作に対する権限分離」を考える。例えば、管理レイヤー・許可する操作・リスク管理担当にわけて定義及び管理する。
ユーザー・グループ・ロールという単位で管理を行う。

権限分掌の裏側はNTTデータが得意なパワープレイで行う。実体は数キロステップのJSON設定ファイルとなっている。

A-gateは常に設定を監視しており、ストレージの公開設定を誤った場合でも検知して即座に修正が行われる。
監視ルールに違反し、かつ例外登録がなされていない場合は自動的に直前の状態へ修復される。また、その内容はメールで通知される。

A-gateはガバナンスを効かせるための機能提供がメインとなっている。

A-gate上でのシステム開発はベンダーフリーとなっている。NTTデータが開発を請け負うこともできるが、営業が下手なもので…!

所感

横浜銀行NTTデータの思惑や本音(と自虐)がポロっと漏れてくるのが面白かったです。

パブリッククラウドでのガバナンスの確立・維持を行うため、A-gateがどのような役割を担うのかは理解できました。ただ、タイトルにもあるようなマルチクラウドの導入や活用という観点ではその一部をサポートするにすぎません。今後、NTTデータがどのようなサービス展開をしていくのか注視したいと思います。