X(Twitter) Zenn GitHub RSS 共有

SQL

作成日時:2025-01-24
更新日時:2025-01-24

最少・最少・最速であれ

システムが遅いのは外部APIの呼び出し。
つまりDBの所為。
なのでパフォーマンスを改善したければDBを何とかしろ。
アクセスプランを取れ。

非機能要件にはねる。

カラム数が大きすぎると、INSERT時にエラーが出る

row is too big: size XXXXX, maximum size 8160

Postgresのカラム数と1レコードサイズの上限に関するメモ #postgres - Qiita

整理するとPostgreSQLでは、

行値構成子

ぎょうち。
または行値式、行式。

WHERE (column1, column2) = (1, 'a')
WHERE (column1, column2) > (1, 'a')
WHERE (column1, column2) IN ((a, b), (c, d))
WHERE (column1, column2) IN (SELECT column1, column2 FROM...)

一番下が便利すぎる。
UPDATEに使う場合は、NULLに注意。

INSERT-SELECT

SELECTの結果をINSERTするやつ。
SELECT-INSERTと呼ぶと思っていたら違った。

セミジョイン

セミ(半分)
INNER JOINして内部表は取得しない的な奴。
下記みたいな。

SELECT * FROM A WHERE EXISTS (SELECT 1 FROM B WHERE A.id = B.id);
SELECT * FROM A WHERE id IN (SELECT id FROM B)

第43回 MySQLの準結合(セミジョイン)について | gihyo.jp
SQLセミジョイン(準結合)

逆にINやEXISTSはプランナによって、セミジョインに変換されうる。

hashジョイン

片方からハッシュテーブルを作成。
もう片方のテーブルを順に読み、結合キーをハッシュ化して、突合。

二分木⇒log₂N
多分木⇒logᴍN

あえて駆動表を大きくする

表AとBの関連が、1:Nの場合、
Aを駆動表としたら、INDEX RANGE SCANとなる。
Bを駆動表とした場合は、INDEX SCANとなる。

外部キー更新時の挙動

外部キー制約を変更した際に、それを参照しているカラムを更新するように設定している場合。
製品によってはパフォーマンスが悪化する可能性あり。
その更新処理が1行ずつの更新だったりする。(Oracle)

OR

UNION使ってORを消す。
インデックスを使えるから。(インデックスマージ)
もしくはプランナが勝手にやってくれる。

Math.MAX/MIN

GREATEST関数/LEAST関数。
MySQLとPostgreSQLにある。

GROUP句の省略

MAXを使えば、項目を省略できる。
下記の受注テーブルと、受注明細テーブルがある。

nameはorder_idに対して一意。
この場合、MAXを使えばGROUP句にしてせずに済む。
(でもパフォーマンスはほぼ変わらんし、後者の方がややメモリの使用量が少ない)

-- 1
SELECT
    order_id
    , MAX(name)
    , SUM(quantity) 
FROM
    orders 
    INNER JOIN order_details 
        USING (order_id) 
GROUP BY
    order_id; 

-- 2
SELECT
    order_id
    , name
    , SUM(quantity) 
FROM
    orders 
    INNER JOIN order_details 
        USING (order_id) 
GROUP BY
    order_id
    , name;

データの存在確認

COUNTよりLIMIT 1。
COUNTはフルスキャンが発生しかねない。

駆動表は小さく

※内部表の結合キーにインデックスが貼られているという前提条件で
レコードのアクセス数は「駆動表のレコード数 × 2」となるので、駆動表を小さくすればするほど早くなる。

らしいが、インデックスを走査するコストは?とは思う。
内部表フルスキャンに比べたら微々たるものだから0としたか。

一応、インデックスなしでも駆動表は小さいほうがやや早いらしい。

外部表サイズ内部表サイズ外部表スキャン内部表スキャン総スキャン数
100件10件1 * 100 = 100100 * 10 = 1,0001,100
10件100件1 * 10 = 1010 * 100 = 1,0001,010

でもRDBMSが駆動表を選んでくれるし。

なので確認することは

だろうか。

SQLの評価順序

但し、あくまで一般的な評価順序であって、RDBMSによっては論理的に異なる可能性がある。

SELECT * FROM 
A JOIN B ON A.a = B.a
WHERE A.xxx = 'value'

-- ↓内部的に書き替えて実行
SELECT * FROM 
(SELECT * FROM A WHERE A.xxx = 'value') A
JOIN B ON A.a = B.a

例えば上記のSQLでは、WHEREの適用場所を変えることで駆動表が小さくなり、結合のコストが減る。
実行計画やログを確認することで、最終的にどのようなクエリが投げられたかを確認できる。

述語のプッシュダウン

このように、WHERE句などの述語条件を、データの取得元であるテーブルやインデックスといったより低いレベルに移動させる処理の事を「述語のプッシュダウン」と言う。

データの加工はAPでやるべきか?

加工の内容によるが。

ビジネスロジックをSQLに持たせたくないというのもある。
負荷が高くなった場合、APは台数を容易に増やせるが、DBはややめんどい。

加工によりデータ量が小さくなるならば、DBで加工するのもあり。

集計とかフィルタリングはDB、その他の加工はAP?

結局はケースバイケースで、最適なものはその都度考える。