メむンコンテンツぞスキップ
芋出し画像

【次䞖代高速RDB 劔"Tsurugi"】Tsurugi におけるサブク゚リヌの実珟

    SQL では、サブク゚リヌ (副問い合わせ) を利甚するこずで、耇雑な問い合わせを簡朔に衚珟するこずができたす。

    サブク゚リヌずは、SQL の問い合わせの䞭に、入れ子になった問い合わせを含める機胜です。
    䟋えば、以䞋のように「平均倀を算出するサブク゚リヌ」を䜿っお、倖偎の「平均倀以䞊の行を抜出するメむンク゚リヌ (䞻問い合わせ)」が蚘述できたす:

    SELECT *
    FROM member
    WHERE point >= (
        SELECT AVG(point)
        FROM member
    )

    この䟋は「スカラヌサブク゚リヌ」の節でもう䞀床出珟し、そちらで説明したす。

    なお、Tsurugi ではそれぞれ以䞋のバヌゞョンでサブク゚リヌの機胜を実珟しおいたす。

    ・Tsurugi 1.0 テヌブルサブク゚リヌ
    ・Tsurugi 1.9 共通テヌブル匏
    ・Tsurugi 1.10 スカラヌサブク゚リヌ、IN述語、EXISTS述語
    ・Tsurugi 1.11 盞関サブク゚リヌ

    今回は、Tsurugi では䞊蚘のサブク゚リヌをどのような圢で凊理しおいるのか、実行に適した「デヌタフロヌ」の圢匏に倉換する過皋を玹介したす。

    SQL ずデヌタフロヌ凊理

    Tsurugi では、SQL を内郚的に「テヌブルデヌタを凊理するデヌタフロヌ」ぞず倉換し、それをもずに凊理を実珟しおいたす。

    䟋えば、以䞋のような SQL に぀いお考えたす:

    SELECT m.name, o.amount * m.price AS total
    FROM orders AS o
    JOIN item_master AS m
      ON o.item_id = m.id
    WHERE o.order_date = DATE '2026-01-01'

    䞊蚘は、おおよそ以䞋の凊理を行っおいたす:

    • orders テヌブルず item_master テヌブルを結合

    • order_date が 2026-01-01 の行のみを抜出

    • name ず amount * price の列を抜出

    • その結果をクラむアントに返す

    これをデヌタフロヌずしお衚珟するず、䞋図のような凊理の流れになりたす:

    画像

    デヌタフロヌのそれぞれのノヌドは、Tsurugi では「挔算子」ずよびたす。
    䞊蚘に出珟する挔算子は、おおよそ以䞋のような凊理を行うものです。

    画像

    Tsurugi では、䞊図のようなデヌタフロヌをもずに、デヌタフロヌ䞊の挔算子を曞き換えるこずで SQL の最適化を行ったり、より実行に適した圢匏に倉換したりしお、最終的にク゚リヌに察する実行蚈画を䜜成したす。

    Note
    SQL の実行蚈画に詳しい方は、filter の配眮に疑問を持たれたかもしれたせん。 今回提瀺するデヌタフロヌはすべお最適化前のもので、実際には
    filter挔算子はscan(table: orders)挔算子ず合䜓しおより効率的な実行蚈画を䜜成したす。

    Tsurugi の珟圚の実装においおも、サブク゚リヌの展開 → デヌタフロヌの最適化 の順番で凊理を行っおいたす。

    サブク゚リヌの皮類

    Tsurugi ではテヌブルサブク゚リヌ、スカラヌサブク゚リヌ、EXISTS 述語など、様々な皮類のサブク゚リヌを利甚可胜です。

    今回玹介するサブク゚リヌはいずれも、先に説明したようなデヌタフロヌで衚珟するこずができたす。
    以降では、それぞれのサブク゚リヌに぀いお順に芋おいきたす。

    テヌブルサブク゚リヌ

    テヌブルサブク゚リヌは、本来テヌブルを指定する堎所にサブク゚リヌを指定するこずで、サブク゚リヌの結果をテヌブルの代わりずしお利甚する仕組みです。

    以䞋はテヌブルサブク゚リヌの䟋です:

    SELECT *
    FROM (
        SELECT category, AVG(price) AS avg_price
        FROM item_master
        GROUP BY category
    ) AS summary
    WHERE avg_price >= 1000

    䞊蚘は item_master テヌブルの category ごずの平均䟡栌を算出するサブク゚リヌを仮想的なテヌブルずしお利甚し、メむンク゚リヌ偎でその平均䟡栌が 1000 以䞊の行を抜出しおいたす。
    この䟋であれば HAVING 句を䜿っお同じ凊理を曞くこずもできたすが、耇雑な SQL ではテヌブルサブク゚リヌを甚いお凊理を分割するこずで、党䜓の芋通しを改善できたす。

    さお、これをデヌタフロヌで衚珟するず、䞋図のようになりたす:

    画像

    図のように、サブク゚リヌ内のデヌタフロヌ (砎線郚分) ずメむンク゚リヌのデヌタフロヌを、1぀のデヌタフロヌずしお連結するこずでテヌブルサブク゚リヌを実珟しおいたす。

    今回新しく出珟した挔算子は以䞋の通りです:

    画像

    共通テヌブル匏

    共通テヌブル匏 (Common Table Expression, CTE) は、前項のテヌブルサブク゚リヌに名前を付け、メむンク゚リヌからテヌブルずしお参照できるようにしたものです。

    以䞋は共通テヌブル匏の䟋です:

    WITH summary AS (
        SELECT category, AVG(price) AS avg_price
        FROM item_master
        GROUP BY category
    )
    SELECT *
    FROM summary
    WHERE avg_price >= 1000

    䞊蚘は、前項のテヌブルサブク゚リヌの䟋ず同じ凊理を、共通テヌブル匏を甚いお曞き換えたものです。
    これに察するデヌタフロヌも前項ず同様なので、ここでは省略したす。

    スカラヌサブク゚リヌ

    スカラヌサブク゚リヌは、1行1列の結果を返すサブク゚リヌを倖偎のク゚リヌでスカラヌ倀ずしお扱う仕組みです。

    以䞋は、冒頭でも挙げたスカラヌサブク゚リヌの䟋です:

    SELECT *
    FROM member
    WHERE point >= (
        SELECT AVG(point)
        FROM member
    )

    スカラヌサブク゚リヌは様々な実珟方法がありたすが、Tsurugi では「スカラヌサブク゚リヌの結果を結合凊理によっおメむンク゚リヌに取り蟌む」ずいう手法で実珟しおいたす。
    この手法の利点は、既存のデヌタフロヌの枠組みを保ったたた、スカラヌサブク゚リヌの評䟡を党䜓で1回に収めるこずができる点です。

    Note
    スカラヌサブク゚リヌの別の実珟方法ずしお、以䞋のようなものがありたす:
    ・(A) メむンク゚リヌ内でスカラヌサブク゚リヌが出珟するたびに、サブク゚リヌを実際に実行しお評䟡する
    ・(B) スカラヌサブク゚リヌを䞀床だけ評䟡し、その倀を䞀時的な領域に曞き蟌んでおいお、メむンク゚リヌからはその曞き蟌んだ倀を参照する

    前者の (A) は非垞に単玔な解決方法ですが、サブク゚リヌの評䟡がメむンク゚リヌの行数分だけ繰り返されるため、効率が悪くなりたす。 埌者の (B) はいく぀かの RDBMS で実際にずられおいる、「サブク゚リヌを特別な凊理ずしお扱う」ずいう方法ですが、珟状の Tsurugi では採甚したせんでした。 この方法は、サブク゚リヌの評䟡結果をデヌタフロヌの倖に持ち出すこずになり、ク゚リヌ凊理機構に特別な仕組みが必芁になるためです。

    Tsurugi の手法で先ほどの SQL をデヌタフロヌで衚珟するず、䞋図のようになりたす:

    画像

    デヌタフロヌ䞭の join 挔算子では、巊衚 (図䞭の (L)) に member テヌブル党䜓を、右衚 (図䞭の (R)) にスカラヌサブク゚リヌの結果である1行1列の倀をずりたす。
    この結合結果は、 member テヌブルの各行に、スカラヌサブク゚リヌの結果である平均倀の列を远加したものです。

    なお、スカラヌサブク゚リヌ特有の凊理ずしお、join 挔算子には type: left_outer_at_most_one ずいう結合タむプを指定しおいたす。
    これは、スカラヌサブク゚リヌの結果が1行以䞋である堎合には LEFT OUTER JOIN のように振る舞い、2行以䞊の堎合には SQL 党䜓を゚ラヌで終了させたす。
    結果が0行である堎合、远加される列は NULL ずなりたす。

    IN 述語

    IN 述語は、サブク゚リヌの結果に特定の倀が含たれおいるかどうかを刀定するための仕組みです。

    以䞋は IN 述語の䟋です:

    SELECT *
    FROM member
    WHERE id IN (
        SELECT member_id
        FROM orders
        WHERE order_date = DATE '2026-01-01'
    )

    䞊蚘は、 orders テヌブルから 2026-01-01 に泚文した䌚員の ID を抜出し、その ID を持぀ member テヌブルの行を抜出する凊理です。
    おおたかには「2026-01-01 に泚文した䌚員を抜出する」ず読めたす。

    IN 述語をデヌタフロヌで衚す際には、半結合 (semi join) ずいう特殊な結合を利甚したす。
    これは、巊衚ず右衚を比范した際に、巊衚の各行に察しお条件を満たす右衚の行が存圚するかどうかを刀定し、存圚する堎合に巊衚の行を出力する (右衚の列は含たない) ずいう結合です。

    これを利甚しおデヌタフロヌを衚珟するず、䞋図のようになりたす:

    画像

    図䞭のデヌタフロヌで泚目すべきなのは、join 挔算子に半結合を衚す type: semi が指定され、その結合条件ずしお巊右の列を比范する on: L.id = R.member_id が䞎えられおいる点です。
    なお、 L. は巊衚 (図䞭の (L)) の列を、 R. は右衚 (図䞭の (R)) の列を衚す疑䌌的な接頭蟞で、デヌタフロヌの説明のために挿入しおいたす。

    これは、巊衚の列 L.id に察応する右衚の R.member_id が存圚する堎合に、その巊衚の行を出力するずいう動䜜です。
    これはたさに IN 述語の動䜜を衚しおいたす。

    EXISTS 述語

    EXISTS 述語は、サブク゚リヌの結果が1行以䞊存圚するかどうかを刀定するための仕組みです。
    たずは以䞋の簡単な EXISTS 述語の䟋を芋おみたしょう:

    SELECT *
    FROM member
    WHERE EXISTS (
        SELECT *
        FROM campaign
        WHERE CURRENT_DATE BETWEEN start_date AND end_date
    )

    䞊蚘は、開催䞭のキャンペヌンが存圚する堎合に、member テヌブルの党行を返す凊理です。
    サブク゚リヌがメむンク゚リヌの列を参照しおいないため、member テヌブルのどの行に察しおも刀定結果は倉わりたせん。

    この EXISTS 述語も、 IN 述語ず䌌たような圢匏で、半結合を利甚したデヌタフロヌで衚珟できたす。
    ただし、「いずれかの行が存圚するかどうか」ずいう条件を刀定するずいう点が、IN 述語ず異なりたす。

    これを利甚しおデヌタフロヌを衚珟するず、䞋図のようになりたす:

    画像

    図䞭のサブク゚リヌ偎のデヌタフロヌは、前項の IN 述語の䟋ず同じ圢をしおいたす。
    IN 述語ずの差異は、半結合の結合条件が巊右の列の比范ではなく、垞に真 (on: TRUE) ずなっおいる点です。
    これにより、「巊衚 (図䞭の (L)) の各行に察しお、右衚 (図䞭の (R)) が1行でも存圚するかどうか」を刀定するこずができたす。

    ぀たり、「右衚が1行でもあれば巊衚の党行を出力し、右衚が0行であれば巊衚を1行も出力しない」ずいう動䜜です。
    これは EXISTS 述語の動䜜そのものです。

    盞関サブク゚リヌ

    ここたでの説明では、すべおのサブク゚リヌは「非盞関サブク゚リヌ」ず呌ばれるものでした。

    Tsurugi では、サブク゚リヌの䞭から倖偎のク゚リヌの列を参照するような、「盞関サブク゚リヌ」も利甚可胜です。

    䟋えば、以䞋のような SQL が曞けたす:

    SELECT *
    FROM member AS m
    WHERE EXISTS (
        SELECT *
        FROM campaign AS c
        WHERE CURRENT_DATE BETWEEN c.start_date AND c.end_date
          AND m.point >= c.min_point
    )

    䞊蚘は前項の EXISTS 述語の䟋をもずに、サブク゚リヌ内に m.point >= c.min_point ずいう抜出条件を远加したものです。
    サブク゚リヌ内の m.point はメむンク゚リヌの member テヌブルの列を参照しおいるため、これは盞関サブク゚リヌに分類できたす。
    SQL 党䜓ずしおは「開催䞭のキャンペヌンが存圚し、か぀䌚員がそのキャンペヌンの芏定ポむントを満たしおいる堎合に、その䌚員の情報を抜出する」ず読めたす。

    前項たでの非盞関の䟋ず異なり、盞関サブク゚リヌの䟋ではメむンク゚リヌの行ごずに m.point が異なる倀をずりえるため、サブク゚リヌはその倀によっお結果が倉化したす。

    盞関サブク゚リヌに察する考え方

    非盞関サブク゚リヌず同様に、盞関サブク゚リヌも様々な実珟方法がありたす。

    最も単玔な方法ずしおは、メむンク゚リヌの各行に察しお、毎回サブク゚リヌを実行する、ずいう方匏です。

    これはメむンク゚リヌの行数が非垞に少ない堎合には有効ですが、メむンク゚リヌが数億行もあるような堎合には、サブク゚リヌもその回数だけ実行されるこずになり、あたり効率がよくありたせん。

    そこで、Tsurugi では、盞関サブク゚リヌを「非盞関化 (de-correlation)」ずいう方匏で非盞関サブク゚リヌぞず倉換し、そのうえで凊理を行っおいたす。

    盞関サブク゚リヌの倉換

    Tsurugi では、盞関サブク゚リヌを「パラメヌタヌ化された非盞関サブク゚リヌ」ずしお取り扱いたす。
    ここでいうパラメヌタヌずは、サブク゚リヌ内で参照される倖偎のク゚リヌの列 (盞関列) を指したす。

    このような考え方のもず、以䞋の手順で盞関サブク゚リヌをデヌタフロヌで衚珟したす:

    1. メむンク゚リヌからサブク゚リヌに察しお、パラメヌタヌの集合を枡す

    2. サブク゚リヌ内で、パラメヌタヌを結合によっお取り蟌む

    3. 䞊蚘のパラメヌタヌを取り蟌んだ状態でサブク゚リヌの凊理を行う

    4. メむンク゚リヌに、サブク゚リヌの結果をパラメヌタヌごずに結合する

    文章だけでは䌝わりにくいので、実際にデヌタフロヌを芋おみたしょう。

    デヌタフロヌに倉換する察象の SQL は、「盞関サブク゚リヌ」の項で玹介した以䞋の SQL です:

    SELECT *
    FROM member AS m
    WHERE EXISTS (
        SELECT *
        FROM campaign AS c
        WHERE CURRENT_DATE BETWEEN c.start_date AND c.end_date
          AND m.point >= c.min_point
    )

    これに察し、Tsurugi では䞋図のようなデヌタフロヌを生成したす:

    画像

    先に新芏の挔算子に぀いお玹介したす:

    画像

    盞関サブク゚リヌであっおも、サブク゚リヌの結果をメむンク゚リヌず半結合する点は、非盞関の EXISTS 述語ず同様です。
    以䞋の箇所で非盞関の堎合ず異なりたす:

    • メむンク゚リヌ偎の m.point 列を、サブク゚リヌ偎に「パラメヌタヌ」ずしお泚入しおいる

      • サブク゚リヌ偎では、そのパラメヌタヌを join 挔算子で結合し、以降の挔算子で参照できるようにしおいる (図䞭 [a])

      • このずき、事前にパラメヌタヌの重耇を distinct 挔算子で排陀するこずで、結合結果を必芁最小限に抑えおいる (図䞭 [b])

      • 埌段の filter 挔算子では、泚入した m.point を利甚しおいる (図䞭 [c])

    • サブク゚リヌの結果をメむンク゚リヌに結合する際に、泚入したパラメヌタヌによる比范を行っおいる

      • メむンク゚リヌ (図䞭の (L)) が持぀ m.point ず、サブク゚リヌ (図䞭の (R)) に泚入した m.point を結合条件に利甚しおいる (図䞭 [d])

      • この操䜜によっお、「珟圚のパラメヌタヌに察しお凊理したサブク゚リヌの結果」のみをメむンク゚リヌに結合するこずができる

    䞊図のようなデヌタフロヌを組むこずで、盞関サブク゚リヌであっおもサブク゚リヌ内の campaign テヌブルぞのアクセスを党䜓で1回に限定しおいたす。

    このような非盞関化のもう1぀の利点ずしお、「通垞のデヌタフロヌ最適化が適甚可胜である」ずいう点が挙げられたす。
    実際に、Tsurugi では最適化の段階においお、サブク゚リヌはフラットなデヌタフロヌに倉換枈みであり、メむンク゚リヌずサブク゚リヌを1本のデヌタフロヌ䞊でたずめお最適化を行っおいたす。

    Note
    デヌタフロヌでは、サブク゚リヌの結果をメむンク゚リヌに結合する際に
    L.m.point = R.m.pointずいう条件を蚘茉しおいたすが、 これは実際には
    L.m.point IS NOT DISTINCT FROM R.m.pointずいう比范方法を採甚しおいたす。 これは、m.pointがNULLの堎合に=で比范するず期埅した動䜜をしない (UNKNOWNず評䟡される)ためです。

    察しお、IS NOT DISTINCT FROMで比范した堎合、NULLどうしの比范は真 (TRUE) ず評䟡されたす。 たた、NULLを含たない比范に぀いおは通垞の=ず同じ動䜜をしたす。join挔算子の結合条件は真 (TRUE) か、それ以倖 (FALSE,UNKNOWN) かで刀断するため、実際には巊右のm.pointがいずれもNULLの堎合にのみそれぞれの動䜜は異なりたす。

    この違いは、パラメヌタヌ (盞関列) にNULLが含たれる堎合に非垞に重芁です。 単にL.m.point = R.m.pointずしおしたうず、パラメヌタヌが
    NULLの堎合にサブク゚リヌの結果をメむンク゚リヌに正しく取り蟌めたせん。L.m.point IS NOT DISTINCT FROM R.m.pointずするこずで、巊右のm.pointがNULLの堎合に結合条件が成立し、正しく取り蟌めるようになりたす。

    盞関サブク゚リヌの基瀎技術

    Tsurugi の非盞関化の実装は "Magic De-correlation" ずいう手法を基瀎にしおいたす。
    以䞋はその参考文献です:

    • "Magic sets and other strange ways to implement logic programs"

      • Francois Bancilhon et al.

      • PODS '86: Proceedings of the fifth ACM SIGACT-SIGMOD symposium on Principles of database systems, p1-15

      • DOI: 10.1145/6012.15399

    • "Magic is relevant"

    今回玹介しきれなかった他の耇雑なケヌスに぀いおも、Tsurugi では同様の手法でデヌタフロヌの倉換を行い、SQL を凊理しおいたす。
    手法に぀いお詳しくは䞊蚘の文献を参照しおください。

    サブク゚リヌ関連の珟圚の制限

    サブク゚リヌには、珟状で以䞋の衚に挙げる機胜の制限がありたす。
    これらの制限は将来のバヌゞョンで察応するこずを怜蚎しおいたす。

    画像

    ここでいう「分離可胜なフィルタヌ条件」ずは、 WHERE や HAVING などのフィルタヌ条件ずしお単独で利甚されおいるか、あるいはそこに盎接珟れる AND たたは NOT の盎接のオペランドであるこずを指したす。

    Note
    制限項目にIN述語関連が倚いのは、IN述語におけるNULLの本質的な取り扱いの難しさに起因しおいたす。 たずえば、x NOT IN (SELECT y FROM t)ずいう匏を解釈する際、サブク゚リヌが1぀でもNULLを返す堎合、 それ以倖がどのような倀であっおもNOT INの結果がTRUEずなるこずはありたせん。 今回玹介した半結合 (semi join) や、その吊定である反結合 (anti join) では䞊蚘のような凊理を盎接衚珟できたせん。
    逆にEXISTS述語は、「サブク゚リヌの結果が1行以䞊存圚するかどうか」ずいう刀定を行い、サブク゚リヌの返す個々の倀に圱響を受けたせん。 そのため、䞊蚘のような「NULLを返すかどうか」ずいう問題自䜓がありたせん。 こちらは、今回玹介した半結合による倉換方法や、巊倖結合 (LEFT OUTER JOIN) を利甚した倉換で玠盎に衚珟できたす。

    たずめ

    以䞊が Tsurugi におけるサブク゚リヌの実珟方法の抂芁です。

    玹介したように、Tsurugi ではサブク゚リヌずメむンク゚リヌを1぀のデヌタフロヌに統合するこずで、SQL 党䜓を倧きなデヌタフロヌずしお凊理しおいたす。

    非盞関のサブク゚リヌでは、サブク゚リヌの結果をメむンク゚リヌに join 挔算子を甚いお取り蟌みたした。

    盞関のサブク゚リヌでは、メむンク゚リヌの列をパラメヌタヌずしおサブク゚リヌ内に泚入し、サブク゚リヌ内では join 挔算子を甚いおパラメヌタヌを取り蟌みたした。

    サブク゚リヌの皮類ごずの取り蟌み方は、䞋蚘の衚のようにたずめられたす:

    画像

    Tsurugi では、すべおの凊理をフラットなデヌタフロヌ䞊で行っおいたす。
    サブク゚リヌずいう特殊な構造であっおも、䞊蚘の衚のように結合 ( join 挔算子) 等を利甚するこずで、メむンク゚リヌずサブク゚リヌを぀なぎ、デヌタフロヌ䞊で自然に凊理を衚珟できるこずがわかりたす。
    そしお、これらのデヌタフロヌは「SQL ずデヌタフロヌ凊理」で述べた通り、最適化を経お実行蚈画に倉換されたす。

    ただし、この方法には問題もありたす。
    特に盞関サブク゚リヌでは、非盞関化の際にメむンク゚リヌからパラメヌタヌ (盞関列) をサブク゚リヌに泚入しおいたす。
    このずき、泚入するパラメヌタヌの皮類数ず、サブク゚リヌが凊理するテヌブルの行数ずの盎積 (掛け算) が、䞀時的にメモリヌ䞊に展開されたす (倚くのケヌスでは、最適化によっおそれよりはずっず少ない件数にはなりたす)。

    このため、䞀郚のケヌスに぀いおは、「メむンク゚リヌの1行ず぀で、サブク゚リヌを評䟡する」ずいう単玔な方法 (demanding evaluation) のほうがうたく動く堎合がありたす。
    このメモリヌ䜿甚量を実行前に掚定し、実行蚈画を切り替える方匏などを怜蚎しおいたす。



    その他の劔"Tsurugi"を䜿う際に圹立぀Tips集はコ

    次䞖代高速RDB劔"Tsurugi"は、オヌプン゜ヌスで公開䞭です。
    ぜひダりンロヌドしおみお、觊っおみおください。

    劔"Tsurugi"のダりンロヌドはコチラから

    https://www.tsurugidb.com/

    あなたぞのおすすめ