DuckDB in Actionの読書メモ
manning.comより発売されているDuckDB in Actionを読み始めたので、読書メモを残す。
1章 イントロダクション
DuckDBとは
- シングルノードでinmemoryな組み込みデータベース
- 処理中のデータが永続化されない
- 個人情報、機密データの扱いに困らない
- 処理中のデータが永続化されない
- データパイプライン (ETL)
- ギガバイトなデータを効果的に扱える
- 数秒でハンドリング可能な処理機構
- でもテラバイト級だとさすがに無理
- MITライセンス
入出力として扱えるデータソース
-
csv
-
Json
-
Parquet
-
Arrow
-
MySQL
-
SQLite
-
PostgreSQL
-
Jupyterを経由して、PandasやPolarsから問い合わせできる
-
並列化されたクエリエンジン
-
費用面で高コストなAWS Athena SQLを使わずともすむ
DuckDBのSQL
- 標準ANSI SQL
- Window関数, CTEも扱える
- 分析関数
- count, min, max
- 独自拡張されたSQL構文
- select exclude() / replace()
- group by all
- pivot / unpivot
- structデータ型、UDF
- describe
- データ構造の確認
- 分析関数
- std 標準偏差
- approx_unique
- percentile
- null_percentage
- histgram
- bitstring_agg
- approx_count_dustinct
DuckDBのメリット
- 分析処理基盤としてSQLiteよりずっと速い
- Apache Sparkよりもセットアップが楽
- エラーハンドリングに悩まされない
- ほかのRDBよりもセットアップが楽
- Talendよりも強力なデータ変換
ユースケース
- ログファイルの分析
- 個人的な医学データ(心拍数等)の定量化
- IOT機器からのデータの集計
- 乗用車の走行距離の最適化
- 機械学習の前処理
- BigQuery, RedDhift, Athenaの置き換え
DuckDBには向かないユースケース
- トランザクション処理 (OLTP)
- メインメモリを超えるデータの扱い
- テラバイト級のデータ処理
- 行指向のデータ
- バルクで入力し、列指向で扱うため
処理フロー
- データロード
- テーブルへの展開
- RDBとは異なりあらかじめスキーマを定義する必要はない
- 入力から柔軟に判断する
- SQLによるデータ処理
- 結果のエクスポート
- create table <name> as seltctによる永続化
- 入力同様の出力フォーマット
- matplot, ggplot, d3, nivoによるビジュアル化
扱えるデータ構造
- varcharのような標準SQLで提供される型
- 日付リテラル
-
YYYY-MM-DDThh:mm:ss
なIS0 8601
フォーマットも扱える模様 (ドキュメントの記載なし)
-
- 日付リテラル
- enum
- 名前づけられた順序型
- リスト, 配列
- マップ
- キーバリューペア
- 構造型
- 仮装列, 継承列
2章 Getting start
インストール方法、対話/非対話モードでの実行、機能拡張を解説する章
- 多くのOSをサポートしている
- Linux, Windows
- MacOS
- homebrewからいける
- Intel/AMDやArm CPUもOK
- 多くの言語バインディング
- Python, R, Java, JS, Golang, Rust, Nodejs, Julia, C/C++, Swift
- ODBC, JDBC,
- wasm
インストール
※DuckDBは組み込みDBのため、クライアントCLIのみで事足りる
* インストールは実行バイナリをコピーするだけ
使い方
以下のコマンドを叩くと、対話モードで起動する。
duckdb
- インメモリDBのため、プロセスを終了させるとDB本体消える。
- SQLite同様、ドット(.)で始まる特殊コマンドを有する。
- コマンド引数は空白区切りで記述する。
- 一行で完結させること。
- 終端のセミコロンは不要。
- 主要な特殊コマンド
- .open 今扱ってるDBを閉じ、新たに作成する
- .read SQLファイルを読み込む
- .tables テーブルとビューの列挙
- .timer on/off 実行時間の表示?
- .mode 出力モード
- duckbox 表組みで整形する
- json
- line
- .maxrows 最大表示行
- .excel Excelで結果表示する
- .quit プロセスを終わらせる
- .help ヘルプメッセージの表示
コマンドライン引数
- -csv 出力結果をcsvファイルとして保存する
- -jdon jsonファイルとして保存する
- -line lineモードの結果をファイルに保存する
- -s <SQL> 非対話モードでクエリを実行する
$HOME/.duckdbrc ファイルに初期設定を用意しておくと、起動時に読み込んでくれる。
SQLコマンド
-
copy / to
*非対話モードで以下のクエリを実行すると標準出力に出力されるcopy (<SQL>) to ‘/dev/stdout’ (format <FORMAT>)
機能拡張
以下のクエリを対話モードで実行すると機能拡張が列挙される
describe select * from duckdb_extensions();
以下のクエリを対話モードで実行すると機能拡張が有効化されているか確認できる
select * from duckdb_extensions();
- install 機能拡張のインストール
- load インストールされて機能拡張のロード
デフォでインターネット先のファイルの読み込みは制限されている
httpfs
機能拡張を有効化すると読めるようになる。
- ファイルをローカルファイルシステムに保存することなくオンザフライで読み込む
select count(*) from <URL>
- ファイルの種類は拡張子で判断する
- 拡張子がない場合、以下のクエリで読み込める
seltct count(*) from read_csv_auto(“<URL>”)
3章 SQL
DDLとDMLの説明
PostgreSQL、MySQL、Oracleのいいとこ取りをしているなぁという感想
加えて、独自追加した構文が強い!
CLI
- コマンドはセミコロン(;)で区切られる
- 空白は自由に使用可能
- sqlは大文字、小文字を区別しない
この章は、単位時間あたりのエネルギー消費量の分析を例にして解説している。
DDL
- create table …
- スキーマを作成する
- if not existsを付与すると、ない場合だけ作成
- ほかのRDBとは異なり、カジュアルに主キーを変更できないので設計は慎重にね
- create or replace table …
- なかったらスキーマ作成、存在してたら置き換え
- create table <NAME> as select …
- 問い合わせ結果からスキーマ作って、レコード入れてくれる
- 問い合わせにlimit 0 付けると、スキーマのみの作成も可能
- キーまでは作ってくれないとは思うが・・・
- alter table …
- 既存のスキーマの置き換え
- 構造だけでなく、列名の変更も可能
- drop table …
- 既存のスキーマの削除
- create sequence …
- 単調増加する系列の作成
- if not existsを付与すると、ない場合だけ作成
- 値はnextval()で取得
- create view …
- ビューを作成する
- where句の中でビューを使う場合、ビューがロードされるのではなく、ビューのクエリが埋め込まれる (push down predicates)
- describe …
- あらゆるリレーションの構造を問い合わせれる
- テーブル、ビュー、クエリの結果セット
DML
- insert
- レコードの作成
- values句で属性のセットを指定する
- select クエリの結果セットを属性として追加する
- on conflict コンフリクトした際の手段を提供する
- do nothing コンフリクトした際に追加しない
- do update set updateを行う
- by name
- insert into <NAME> by name とすることで、列名を明示しなくても値タプルの名前(エイリアス)でよきにはかってくれる
- insert or ignore …
- on conflict do nothingと同等
- insert or replace …
- upsert する
- on comflict fo updateの簡易版
- delete from …
- レコードの削除
- select
- 問い合わせ
- group by all
- 集約関数に渡された列以外をグループ化してくれる。便利!
- select句でのエイリアス名をgroup by に指定できる
- mysqlがそんなことできた気がする
- havingも同様に指定できる
- values句でリレーションを通さない固定値を結果セットに与える
- postgresqlにあるやつ
- カンマ区切りで複数列与えられる
- valuesこ構成要素にタプル指定すると構造型にしてくれる
- union
- 結果セットの和集合を組み立てる
- intersect
- 結果セットの積集合を組み立てる
- except
- 結果セットの補集合を組み立てる
- join
- 同じ列名でjoinする場合、using (<列名>)が使える
- oracleで使えるやつ
- 外部結合は、left, right, fullが用意されている
- 同じ列名でjoinする場合、using (<列名>)が使える
- cte
- 問い合わせの中のみで使えるビュー
- ビューとは異なりネストできる
- withの中にcte書けるってこと?
- 公式ドキュメントにも詳細は書かれてない・・・
- with recursiveで再帰cte
- exclude
- select * exclude(<列名>)とすることで、特定の列を除いた結果を問い合わせできる
- index-only scanフレンドリー
- replace
- seltct * replace(<式>)とすることで、特定の列だけ射影を変更できる。
- 標準sqlだと、1列だけ変えたい時にクッソめんどくなるやつ
- 個人的にinsert selectで頻発するやつ
- columns
- 正規表現で列名をマッチング
- 結果を関数の引数にすると、列名を動的に決定できる
- 正規表現の代わりにラムダ式も使える。強い!
- replaceやexcludeも使える
- sample
- 取得するサンプリングレートを指定できる
組み込み関数
※文中に登場したもののみ
- date_part
- タイムスタンプの部分フィールドの取得
- 公式サイト見るとextractも用意されてる模様
- list_append
- リスト化した列に追加する
- 結果もリスト
集約関数
※文中に登場したもののみ
- list
- グループ内の全ての値を列挙した列を作る
- any_vslue
- グループ内で最初に見つかった値を返す
- arg_max, arg_min
- 最大の第2引数の値を持つ第1引数の値を返す
- 標準sqlで書くとクッソめんどいやつ
- bit_and, but_or, bit_xor
- ビット演算
4章 集約に関するあれこれ
サブクエリ
サブクエリは用途に応じて4つに分類される
- 無相関サブクエリ
- ほかのRDBで、derive tableやinline viewと呼ばれるやつ
- 外側のラップする問い合わせの値を使用しない
- というか基本できない。
- ↑lateral join除く[1]
- where句にてin, any, allと共に使用される
select * from T
where col = any (select unnest(range(1, 10)))
- 相関サブクエリ
- where句にて、existsとともに使用される
- スカラ無相関サブクエリ
- select句でのサブクエリによる射影
- where句でのサブクエリによる集計結果でのフィルタ
- 外側のラップする問い合わせを使用しないケース
- 個人的にあまり遭遇したことない
- スカラ相関サブクエリ
- スカラ無相関サブクエリ同様、select句で使用するもの
- ↑個人的にwhere句で使うケースに遭遇した記憶はない
- 外側のラップする問い合わせの値を使用し、絞り込みを行う
- スカラサブクエリだと圧倒的にこのケースが多い
- スカラ無相関サブクエリ同様、select句で使用するもの
スカラ相関サブクエリはjoinに置き換え可能
- 非相関化と呼ばれる
- 巨大テーブルでの相関サブクエリの使用は性能に悪影響がでるため良く用いられる
- ↑個人的にはどちらが良い性能となるかはケースバイケース
- 内部結合なら非相関化、外部結合ならサブクエリの方が良くなるイメッジ
- 巨大テーブルでの相関サブクエリの使用は性能に悪影響がでるため良く用いられる
DuckDBでは空気を読んで、非相関化するかどうかの最適化を選択してくれる。
in演算子とany演算子の使い分け
- inは等値で判定
- anyは不等値(>, <)で判定したい場合
- 等値判定を使用すればin演算子の代わりになる
all演算子
サブクエリの結果セットの全てと合致するかを判定
- where句で絞り込みとして使われる
- 不等値での判定も可
グループ集計
- group set
- group byで使用できる
- 引数で指定したそれぞれのグループで集計を行う
- 引数にタプルを渡すと複数列のグループとなる
- rollup句
- group setの簡易指定版
- 引数で指定した列を組と見て、もっと詳細なレベルから全体までのそれぞれで集計を行う
- rollup(a, b) == group set((a, b), a, ())
- cube句
- group setの簡易指定版
- 引数で指定した列の全ての組み合わせで集計を行う
- cube(a, b) == group set((a, b), a, b, ())
ウインドウ関数
- 現在行からほかの行に由来する集計結果を取り込む際に使用する
- partition句で指定した列でグループ化できる
- 現在行が属するグループで集計を行う
- 指定しない場合、リレーション全体が一つのパーティションとして取り込まれる
- ウインドウ関数は取り込んだパーティションの行のみが集計対象となる
- 隣接行からの集計結果を取り込むときはウインドウフレームを使用する
- order by句で指定した列で関数が実行される
ウインドウフレーム
前後3日、7日の単位の移動平均の集計する場合、パーティション内で現在行の前後3日を集計対象にする必要がある。
この境界づけられた集計対象はフレームと呼ばれる。
フレームはウィンドウ関数内でorder by の後に以下のように指定する。
<MODE> betewwn <START> and <END>
- MODE 範囲の基準(SQL2003分のみサポート)
- rows 現在行を基準に±何行で範囲を構築する
- range 現在行の値を基準に値の範囲で構築する
- ↑SQL2011で追加されたgroupsはまだサポートされてない(どうもPostgreSQLだけとの噂)
- START 範囲の開始位置
- unbound preceding
- パーティションの開始を範囲の開始とする
- <OFFSET> preceding
- 現在行より前の指定した値を範囲の開始とする
- rowsモードの場合は相対的な行数を指定する
- <OFFSET> following
- 現在行より後の指定した値を範囲の開始とする
- rowsモードの場合は相対的な行数を指定する
- current row 現在行を範囲の開始とする
- unbound preceding
- END 範囲の終了位置
- unbound preceding
- パーティションの終端を範囲の終端とする
- <OFFSET> preceding
- 現在行より前の指定した値を範囲の終端とする
- rowsモードの場合は相対的な行数を指定する
- <OFFSET> following
- 現在行より後の指定した値を範囲の終端とする
- rowsモードの場合は相対的な行数を指定する
- current row 現在行を範囲の終端とする
- unbound preceding
↑書籍にはここまで詳しくは書かれてなく、公式ドキュメントやほかのRDBのドキュメントからの内容で補っていることに注意
PostgreSQL
だけど、澤田さんのまとめが詳しい
前後3日であれば、以下のようになる
order by “date” range between interval 3 days and interval 3 days
window句
- windows関数のover句の記述を外出しし、再利用できる
- 記述位置はgroup byとorder byの間
qualify句
- window関数は、group byやhavingが適用された後に実行される
- だもんで、window関数の結果をフィルタする目的には使えない
- window関数の結果に対するフィルタはqualify句で指定する
- qualify句の記述位置はwindow句の後
filter句
- window関数の適用対象となる個々の値のフィルタを指定する
- 記述位置はoverの前
- 通常の集約関数にも使用可能
以下の記述は同じ結果をもたらす。
sum(value) filter (year = 2003) as yearly_val_1,
sum(case when year = 2003 then value end) as yearly_val_2
↑後者は、filter句がないRDBで多用してた。
pivot文
グループ単位の集計結果を横並びで出力する
SQL標準とは異なり単独の文として提供
asof join句
- 不等価結合の一種
- 駆動表で指定する列に一番近い行を結合してくれる
- 直近の単価をマスタ引きする場合に asof join一発で行ける
- ↑ほかのRDBだと、where句でサブクエリ駆使する羽目になってたやつ
- 対象値より小さい最大値の行を取得する的な
表関数
- 一つのスカラ値ではなくレコードセットを返す関数
- ↑oracleだと、パイプライン表関数って呼ばれるやつ
range/generate_series
系列を返す表関数
rangeは、範囲の終端に指定した値を含めない(inclusive)
generate_seriesは、終端を含める(exclusive)
どちらも
3番目の引数で刻み幅を指定できる
↑generate_seriesは、postgresqlで随分昔から使える関数
lateral join
- 相関サブクエリできるjoin(雑)
- oracle やsql serverだと
spplyapply/outer applyと呼ばれるやつ
- oracle やsql serverだと
- 各種join句と併用可能join <RELATION> lateral onと指定する
- cross join
- (inner) join
- ↑postgresql感覚で結合条件にon trueいけた
- outer join
- ↑inner join同様、on trueいけた
- ↑公式ドキュメントや書籍には記載なかったが、外側の表の値をサブクエリの中で使えた。
- 相関サブクエリっつてんだから、そりゃそうなんだけどね
- ↑なぜか公式ドキュメントには、直積の例しかなくて悲しい
-
他のRDBとは異なり、DuckDBはlateral なしでも相関サブクエリのJoinは可能。ただ紛らわしいのでlateralつけてねってスタンス。 https://duckdb.org/docs/sql/query_syntax/from#lateral-joins ↩︎
5章 外部リソースのインポート
外部リソースのスキャン
- from “<URL>”で、永続化することなしに取り込める。
- ただし要拡張子
- 拡張子がない場合、フォーマットごとに用意された表関数を使用する。
- load_csv_autoなど
- https://duckdb.org/docs/data/overview
- いずれもスキーマは空気を読んで推論してくれる
- 明示的に型を指定することも可能
- load_csvなど
- ファイルロードをviewにカプセル化することでも型の明示的な指定のサポートとなる
- 複数の異なるファイルタイプの外部リソースを一括でロードできる
- スキーマのマージは、列位置と列名の2つの戦略が提供されている
- 列位置が既定の戦略
- スキーマのマージは、列位置と列名の2つの戦略が提供されている
- パスに*が指定された場合、マッチするファイルをgrobして一括でロードしてくれる
-
**
を使えばディレクトリもglob - https://duckdb.org/docs/data/multiple_files/overview#glob-syntax
-
csvのパース戦略
- 先頭から一定数サンプリングしてデリミタ、スキーマの型、ヘッダ行の有無を決定する
- 20480が規定値
- 以下がサポートする型(下に行くほど優先度が下がる)
- boolean
- bigint
- double
- time/date/timestamp
- varchar
parquet
- csvやjsonよりも効率的なデータ構造
- copy toコマンドを使用してparquetフォーマットねの変更おススメとのこと
- parquet_schema関数で、内部スキーマを丸裸にできる
SQLiteからのスキャン
- sqliteモジュールをインストール/有効化する
- attach文でswliteのDBファイルをアタッチできる
- 一度アタッチすれば、DuckDB内のテーブルとして扱える
- SQLiteの型付けは弱いため、intといいながらfloat突っ込める
- 結果、selectの際DuckDB内で型のミスマッチエラーご発生する
- 著者曰くバグらしい
- 読んでる時点の最新バージョン0.10.1でもまだ解決してなさそう
- 回避策は文字列として読む設定をした上で、sqlite_scanで読み込むクエリをviewでラップ
set global sqlite_all_varchar = true
seltct <ここで型変換> from sqlite_scan(<PATH>) - SQLiteの型付けは弱いため、intといいながらfloat突っ込める
PostgreSQLからのスキャン
- postgresモジュールのインストール/有効化
- 書籍ではpostgresql_scannerとなっているが、多分廃止されてる
- attach文でDBに接続する
- 書籍ではcall postgres_attachを呼ぶことと書かれているが、公式ドキュメントで廃止されてattach文でOKと書かれてた
- https://duckdb.org/docs/extensions/postgres.html のページ最後あたり
- アタッチ後は、SQLiteと同じ
6章 Python binding
DuckDBのPython API
- Pythonオブジェクトにhydrateしてくれる
- pandasのdataframesけの変換と楽ちん
インストール
pip duckdb
基本的な使い方
- 以下のコードを書いて使えるようにする
- import duckdb
- duckdb.sql(“<SQL>”)を呼ぶとハンドルを返す
- クエリの実行のみを行う
- まだfetchはしない
- デフォでin-memory が選択される
- クエリの実行のみを行う
- fuckdb.execute(“<SQL>”)を呼ぶと、PreparedStatementのハンドルを返す
- handleを介してプレースホルダに動的なパラメータを渡すことができる
- このAPIはDB-API 2.0準拠
- create_functionはpython上で定義した関数をUDFとして振る舞わせれる
リレーションの操作
- テーブルのロード
- handleから、table(“<NAME>”)メソッドを呼ぶ
- ORM的な使い心地のAPIが生えている
- Apache Arrowへの変換
- to_arrow_table
外部リソースの読み込み
- 利用可能なファイルタイプごとにread_xxxxな関数が提供されている
- csvならread_csv
- handleから各種変換メソッドが提供されている
- to_table(“<NAME>”)
- 指定した名前のテーブル作ってインポート
- to_table(“<NAME>”)
パターンマッチ
以下の3が提供されている
- like
- _ 任意の一文字のマッチ
- % 任意の複数文字のマッチ
- postgressl互換の表記もサポートしている
- similar to 正規表現マッチ
- posixスタイルの表記もサポート
- grob grobパターンによるマッチング
↑書籍上、この章に書かれているが、CLIからももちろん呼べる
7章 MotherDuck
何ができるかを中心に整理。
操作手順に多くページが割かれていたが、そこはカットしていることに注意
MotherDuckとは
- 単純化されたスケールアップ戦略
- その戦略上でSQLを使った分析を増強するソリューション
- APIを使ってクラウドストレージからクラウドデータべースデータの問い合わせや分析を実行する協調的なサーバレスデータ解析プラットフォーム
- 2023年9月、一般公開
- データべースとnotebook実行基盤の管理
- CLI
- python開発基盤の提供
- opt-inで機能拡張の適用
- md: やmotherduck:プロトコルを使用する
- ローカル実行環境とクラウド実行環境をシームレスに繋ぐ
- ローカルで問い合わせてクラウドでフィルタ
- またはその逆
- 永続的にコールドデータを保持しないことによる低コスト
- parquet, csv
アーキテクチャ
- サービスレイヤ
- 認証、認可、管理者機能、請求、モニタリングなどなど
- Ducklings
- サーバレス計算環境
- カタログ
- データベース、テーブル
- ストレージ
- 内部ストレージの管理
- 冗長化、セキュリティ保護、オートスケール
機能
- データベースのアップロード
- データベース管理
- URLベースのデータベース共有
- ローカルDuckDBインスタンスのアタッチ
- ちょっとしたAIエージェント
8章 データパイプライン
-
dlt
を使用したデータの取り込み、dbt
を使用したパイプラインステージの作成手順について、多くのページが割かれているが省略 -
dagster
の操作手順も省略
データパイプライン
- データベースやログなどのフラットファイルの取り込み
- 取り込んだデータの処理、変換
- 変換には、データセット間の結合も含む
- 標準化や適切な統合化を目的としたフィルタリング、集約、マスキング
- マスキングとは、情報統制や機密データの匿名化
バッチ処理
- 連続するコマンドで組み立てられたワークフロー
- ワークフローの完結をもってバッチ処理の完了
- リアルタイム性を求められない場面に有用
ETL (Extract / Transform / Load)
- データパイプラインのサブカテゴリ
- データを取り込み、処理して、外部に出力
- push型
ELT (Extract / Load / Transform)
- データパイプラインのサブカテゴリ
- データを取り込み、処理して、結果を外部公開
- Pull型
DuckDBは、Extractパート専用としても、Transformパート専用としても、またはその両方としても有用。
- Extract
- さまざまなデータフォーマットを扱える
- Transform
- データフォーマットを超えた強力なSQLエンジン
- Load
- 入力同様、扱えるデータフォーマットの多さ
- とくにデータレイクの出力フォーマットととしてParquetは有用
オーケストレーション
- cron
- Airflow
- Luigi
- Kestra
- Prefect
- Dagster
- クラウドネイティブツール
- software defined assetという考え方
- python製
- pythonの関数で構成を記述できる
- Data linageの提供
- データリネージュとは変換履歴
- Data provenanceの提供
- Provensnceは出自、履歴
9章 BI
- ツールの使い方がメインのため、何を使ったかを記録するにとどめる
Streamlit
- インタラクティブなWebインターフェースを構築するためのツール
- SQLを知らなかったりデータベースに直接アクセスできないユーザに対する可視化
- python製ライブラリ
- データ駆動アプリを素早く組み立て出たい人向け
Apache Superset
- データ探索と可視化プラットフォーム
- オープンソース
- DuckDBとの統合もバッチリサポート
- ローカルマシンで手軽に試せる
依存関係
- ダッシュボード
- 構成要素としてのチャート
- 依存元としてのデータセット
- 依存元としてのテーブル
- 提供元としてのデータベース
- 依存元としてのテーブル
- 依存元としてのデータセット
- 構成要素としてのチャート
- データベースへの接続
- テーブルを選択してデータセットの作成
- データセットを使ってチャートの作成
- ダッシュボードでインポート
11章 パフォーマンス
Stack OverflowとNew York Taxi dataset のデータ解析手順は省略
- read_csv関数は、gzig圧縮されたファイルも直接読むことができる
- bar関数は、問合せ結果にバーチャートを載せてくれる
- enum型を用意しておくと、DuckDBが文字列を自動的にenumに変換してくれる
- enum型の内部は整数値のためより操作感が増す
- 著者らの環境では、enumにすることで2倍速くなってた
- enum_range関数を使うと、すべてのenumをlistとして取得できる
- データセット内で文字列の組として表現されていたとしても、組み込み関数を駆使すればenumのlistにすることも可能
- 型として配列を提供しているため、テーブルのフィールドにそのまま持たせられる
- enum型の内部は整数値のためより操作感が増す
- summarizeをクエリの前に付与することで、リレーションの輪郭を観察できる
SQL実行エンジン
- SQLのパース
- パーザはpostgres由来
- いくつかのステージを経てASTを構築する
- テーブル、ビュー、型、列名の解決
- パース結果から論理的な実行計画を求める
- 実行計画は一連のクエリオペレータで構成される
- scan, filter, projection, etc...
- 統計情報(インデックス等)やキャッシュ情報などを活用する
- explainをクエリの前につけて呼ぶことで確認できる
- 実行計画は一連のクエリオペレータで構成される
- 実行計画の最適化を行う
- 最適化には統計情報が用いられる
- join順序の調整、サブクエリの平坦化
- 実データを取り出すための物理的な実行計画を求める
- explain analyze をクエリの前につけて呼ぶことで確認できる
ランタイム
- DuckDBはベクタ化ベース
- 行ベースの一般的なRDBとは違う箇所
- 1列(1ベクタ)あたり最大122880行分を保持
- 選択、フィルタ、データスキャン、並び替えにメリット
- CPUの分岐予測の最適化
- CPUキャッシュが最大限の活用
- SIMDも活用しやすい
- morselアプローチを通した並列化