テーブルが結合されているにもかかわらず、クエリがそのうち 1 つのテーブルからのみ列を読み取る場合、AnalyticDB for MySQL はその結合が冗長であることを証明し、実行計画から削除できます。これにより、SQL を変更することなくレイテンシを低減できます。この機能を「結合削除」と呼び、宣言された主キー (PK) および外部キー (FK) 制約の関係に依存します。
AnalyticDB for MySQL の FK および PK 制約は情報提供専用であり、システムはデータ整合性を強制しません。すべての FK 値が有効な PK 値を参照していることを、ユーザー自身で保証する必要があります。宣言された制約に反するデータが存在する場合、結合削除後にクエリが誤った結果を返す可能性があります。
前提条件
開始する前に、以下の条件を満たしていることを確認してください。
AnalyticDB for MySQL クラスターがバージョン 3.1.10 以降で実行中であること
Data Lakehouse Edition クラスターのマイナーバージョンを確認するには、SELECT adb_version(); を実行します。マイナーバージョンをアップグレードするには、テクニカルサポートまでお問い合わせください。結合削除の仕組み
オプティマイザーは、FK-PK 制約情報を使用して、結合が結果セットを変更するかどうかを判断します。クエリがテーブル A(FK 側)とテーブル B(PK 側)を宣言済みの FK-PK 列ペアで結合し、かつクエリがテーブル B の列を選択しない場合、以下の条件が成立します。
テーブル A の各行は、テーブル B の各行と最大 1 件のみマッチします(B の列が PK であるため)。
結合はテーブル A の行をフィルター処理したり、複製したりしません。
したがって、この結合は冗長であり、単独でテーブル A をスキャンした場合と結果は同一です。
これらの条件が満たされる場合、オプティマイザーは結合を、FK テーブルに対する単一の TableScan に置き換えます。
クエリ単位でこの動作を有効化または無効化するには、/*+ PK_FK_DEPENDENCY_ENABLED*/ ヒントを使用します。
| ヒント | 効果 |
|---|---|
/*+ PK_FK_DEPENDENCY_ENABLED = true*/ | FK-PK に基づく結合削除を有効化 |
/*+ PK_FK_DEPENDENCY_ENABLED = false*/ | FK-PK に基づく結合削除を無効化 |
外部キーの宣言
テーブル作成時(CREATE TABLE)に外部キーを宣言するか、後から追加または削除します(ALTER TABLE)。CREATE TABLE の構文詳細については、「CREATE TABLE」をご参照ください。また、ALTER TABLE の構文詳細については、「ALTER TABLE」をご参照ください。
制限事項
| 制限事項 | 詳細 |
|---|---|
| 単一列外部キーのみ | 外部キーは 1 列のみを参照できます。FOREIGN KEY (sr_item_sk, sr_ticket_number) REFERENCES store_sales(ss_item_sk, d_date_sk) のような複数列外部キーはサポートされていません。 |
| データ制約の検証なし | AnalyticDB for MySQL は FK-PK 間のデータ整合性を検証しません。結合削除を利用する前に、データが宣言された制約を満たしていることを確認してください。 |
| 外部テーブル | 外部テーブルには外部キー制約を追加できません。 |
| DROP TABLE 動作 | DROP TABLE 文で外部キーを削除する場合、AnalyticDB for MySQL はこの操作を検証せず、エラーも報告しません。 |
| 1 回の ALTER TABLE 文で 1 つの外部キーのみ | ALTER TABLE で複数の外部キーを追加する場合は、各外部キーごとに個別の文を実行してください。 |
CREATE TABLE による外部キーの宣言
同一データベース内の外部キー
データベースおよびプライマリテーブルを作成します。
CREATE DATABASE db; USE db; CREATE TABLE item ( i_item_sk bigint NOT NULL, i_current_price bigint, PRIMARY KEY(i_item_sk) ) DISTRIBUTED BY HASH(i_item_sk);外部キーとして
itemを参照するテーブルを作成します。symbolを指定しない場合、パーサーは自動的に制約名を<column_name>_fkとします。本例では、制約名はsr_item_sk_fkとなります。CREATE TABLE store_returns ( sr_sale_id bigint, sr_store_sk bigint, sr_item_sk bigint NOT NULL, FOREIGN KEY (sr_item_sk) REFERENCES item (i_item_sk) );
クロスデータベース外部キー
他のデータベース内のテーブルを参照するには、テーブル名の前にデータベース名を付与します。
CREATE DATABASE db2;
USE db2;
CREATE TABLE store_sales
(
ss_sale_id bigint,
ss_store_sk bigint,
ss_item_sk bigint not null,
CONSTRAINT fk_constraint FOREIGN KEY (ss_item_sk) REFERENCES db.item (i_item_sk)
);単一テーブルへの複数外部キーの宣言
1 つの CREATE TABLE 文に複数の FOREIGN KEY 句を含めます。
参照先テーブルを作成します。
USE db; CREATE TABLE customer ( i_customer_sk bigint NOT NULL, i_current_price bigint, PRIMARY KEY(i_customer_sk) ) DISTRIBUTED BY HASH(i_customer_sk); CREATE TABLE vendor ( id bigint primary key, name varchar(5) not null );2 つの外部キーを持つテーブルを作成します。
CREATE TABLE store_product ( sr_sale_id bigint, sr_store_sk bigint, sr_customer_sk bigint NOT NULL, FOREIGN KEY (sr_sale_id) REFERENCES vendor (id), FOREIGN KEY (sr_customer_sk) REFERENCES customer (i_customer_sk) );
ALTER TABLE による外部キーの宣言
既存のテーブルに対して外部キーを追加または削除するには、ALTER TABLE を使用します。1 回の文で 1 つの外部キーのみを処理します。複数の外部キーを管理する場合は、複数回の文を実行してください。
外部キーの追加:
USE db;
ALTER TABLE store_returns ADD CONSTRAINT sr_item_fk FOREIGN KEY (sr_item_sk) REFERENCES item (i_item_sk);外部キーの削除:
USE db;
ALTER TABLE store_returns DROP FOREIGN KEY sr_item_sk_fk;他のデータベースを参照する外部キーの追加:
-- ステップ 1:db2 に参照先テーブルを作成
USE db2;
CREATE TABLE store
(
id bigint primary key,
name varchar(5) not null
);
-- ステップ 2:db.store_returns から db2.store への外部キーを追加
ALTER TABLE db.store_returns ADD FOREIGN KEY (sr_store_sk) REFERENCES store(id);宣言済み外部キーの確認
テーブル上の外部キー定義を確認するには、SHOW CREATE TABLE を使用します。
USE db;
SHOW CREATE TABLE store_returns;サンプル出力(その他の文は省略):
-- CONSTRAINT `sr_item_sk_fk` FOREIGN KEY (`sr_item_sk`) REFERENCES `db`.`item`(`i_item_sk`)結合削除の適用
外部キーが宣言された後、クエリに /*+ PK_FK_DEPENDENCY_ENABLED = true*/ ヒントを追加します。オプティマイザーは、以下の条件を満たす結合を削除します。
結合条件が宣言済みの FK-PK 関係と一致すること
クエリが結合された PK テーブルの列を選択しないこと
生成された実行計画には、FK テーブルに対する TableScan のみが含まれ、InnerJoin オペレーターは表示されません。
同一データベース内の 2 つのテーブル
store_returns.sr_item_sk は item.i_item_sk を参照する外部キーを持ちます。クエリは store_returns のみの列を選択しています。i_item_sk は PK であるため、各 sr_item_sk 値は item の行と最大 1 件のみマッチし、結合によって結果セットが変更されることはありません。そのため、オプティマイザーはこの結合を削除します。
/*+ PK_FK_DEPENDENCY_ENABLED = true*/
EXPLAIN
SELECT
s.sr_sale_id,
s.sr_store_sk,
s.sr_item_sk
FROM
store_returns s,
item
WHERE
sr_item_sk = i_item_sk;実行計画には TableScan のみが表示され、InnerJoin オペレーターは存在しません。
+---------------+
| 実行計画の概要 |
+---------------+
1- Output[ Query plan ] {Est rowCount: 1.0}
2 -> Exchange[GATHER] {Est rowCount: 1.0}
3 - TableScan {table: store_returns, Est rowCount: 1.0}クロスデータベースの 2 つのテーブル
クロスデータベースの外部キー関係にも同様のロジックが適用されます。db2.store_sales.ss_item_sk は db.item.i_item_sk を参照します。クエリは store_sales のみの列を選択しているため、db.item との結合は冗長です。
USE db2;
/*+ PK_FK_DEPENDENCY_ENABLED = true*/
EXPLAIN
SELECT
s.ss_sale_id,
s.ss_item_sk
FROM
store_sales s,
db.item
WHERE
ss_item_sk = i_item_sk;サンプル出力:
+---------------+
| 実行計画の概要 |
+---------------+
1- Output[ Query plan ] {Est rowCount: 1.0}
2 -> Exchange[GATHER] {Est rowCount: 1.0}
3 - TableScan {table: store_sales, Est rowCount: 1.0}複数テーブルの結合
クエリ内のすべての結合が冗長である場合、オプティマイザーはそれらを 1 回のパスで削除します。本例では、store_returns は item および db2.store の両方を参照する外部キーを持ち、クエリは store_returns のみの列を読み取ります。
USE db;
/*+ PK_FK_DEPENDENCY_ENABLED = true*/
EXPLAIN
SELECT
s.sr_sale_id,
s.sr_store_sk,
s.sr_item_sk
FROM
store_returns s,
item,
db2.store
WHERE
sr_item_sk = i_item_sk
AND sr_store_sk = id;サンプル出力:
+---------------+
| 実行計画の概要 |
+---------------+
1- Output[ Query plan ] {Est rowCount: 1.0}
2 -> Exchange[GATHER] {Est rowCount: 1.0}
3 - TableScan {table: store_returns, Est rowCount: 1.0}ビュー内の結合
複数のテーブルを基に定義されたビューに対しても、結合削除は機能します。store_returns および item を結合するビューを作成します。
CREATE VIEW sr_item_v AS
SELECT
s.sr_store_sk AS store_name,
s.sr_sale_id AS sale_id,
s.sr_item_sk AS sr_item_id,
item.i_current_price AS item_price,
item.i_item_sk as item_id
FROM
store_returns s,
item
WHERE
sr_item_sk = i_item_sk;ビューに対するクエリで、item_price(または item のみから取得可能な他の任意の列)を参照しない場合、結合削除がトリガーされます。オプティマイザーは item.i_item_sk への参照を store_returns.sr_item_sk に置き換え、store_returns のみをスキャンします。
/*+ PK_FK_DEPENDENCY_ENABLED = true*/
EXPLAIN
SELECT store_name, sr_item_id, sale_id
FROM sr_item_v;サンプル出力:
+---------------+
| 実行計画の概要 |
+---------------+
1- Output[ Query plan ] {Est rowCount: 1.0}
2 -> Exchange[GATHER] {Est rowCount: 1.0}
3 - TableScan {table: store_returns, Est rowCount: 1.0}結合削除が適用されない場合:item のみから解決可能な列(例:item_price)をクエリが選択する場合、結合は必須であり、オプティマイザーはこれを保持します。
/*+ PK_FK_DEPENDENCY_ENABLED = true*/
EXPLAIN
SELECT store_name, sr_item_id, sale_id, item_price
FROM sr_item_v;サンプル出力 — 実行計画に InnerJoin[Hash Join] が表示されます。
+---------------+
| 実行計画の概要 |
+---------------+
1- Output[ Query plan ] {Est rowCount: 1.0}
2 -> Exchange[GATHER] {Est rowCount: 1.0}
3 -> InnerJoin[Hash Join] {Est rowCount: 1.0}
4 -> Project {Est rowCount: 1.0}
5 -> Exchange[REPARTITION] {Est rowCount: 1.0}
6 - TableScan {table: store_returns, Est rowCount: 1.0}
7 -> LocalExchange[HASH] {Est rowCount: 1.0}
8 -> ScanProject {table: item, Est rowCount: 1.0}
9 - TableScan {table: item, Est rowCount: 1.0}