

 Amazon Redshift は、パッチ 198 以降、新しい Python UDF の作成をサポートしなくなります。既存の Python UDF は、2026 年 6 月 30 日まで引き続き機能します。詳細については、[ブログ記事](https://aws.amazon.com/blogs/big-data/amazon-redshift-python-user-defined-functions-will-reach-end-of-support-after-june-30-2026/)を参照してください。

# クエリ最適化のためのデータのディストリビューション
<a name="t_Distributing_data"></a>

テーブルにデータをロードすると、そのテーブルの分散スタイルに従って、Amazon Redshift がテーブルの行を各コンピューティングノードに分散します。クエリを実行すると、必要に応じて結合と集計を実行するために、クエリオプティマイザによって行がコンピューティングノードに再分散されます。テーブル分散スタイルの選択は、クエリを実行する前にデータを必要な場所に配置しておくことによって、再分散ステップの影響を最小限に抑えるために行われます。

**注記**  
このセクションでは、Amazon Redshift データベースにおけるデータディストリビューションの原則について説明します。`DISTSTYLE AUTO` を使用してテーブルを作成することをお勧めします。その場合、Amazon Redshift は自動テーブル最適化を使用してデータディストリビューションスタイルを選択します。詳細については、「[自動テーブル最適化](t_Creating_tables.md)」を参照してください。このセクションの残りの部分では、ディストリビューションスタイルについて詳しく説明します。

**Topics**
+ [データ分散の概念](#t_data_distribution_concepts)
+ [分散スタイル](c_choosing_dist_sort.md)
+ [分散スタイルの表示](viewing-distribution-styles.md)
+ [クエリパターンの評価](t_evaluating_query_patterns.md)
+ [分散スタイルの指定](t_designating_distribution_styles.md)
+ [クエリプランの評価](c_data_redistribution.md)
+ [クエリプランの例](t_explain_plan_example.md)
+ [分散の例](c_Distribution_examples.md)

## データ分散の概念
<a name="t_data_distribution_concepts"></a>

以下に、Amazon Redshift のデータディストリビューションの概念をいくつか示します。

 **ノードとスライス** 

 Amazon Redshift クラスターは一連のノードです。クラスター内の各ノードには、独自のオペレーティングシステム、専用メモリ、および専用のディスクストレージが備わっています。ノードの 1 つは*リーダーノード*であり、コンピューティングノードへのデータの分散およびクエリ処理タスクを管理します。*コンピューティングノード*は、これらのタスクを実行するためのリソースを提供します。

 コンピューティングノードのディスクストレージは複数のスライスに*分割*されています。ノードあたりのスライスの数は、クラスターのノードサイズによって決まります。ノードはいずれも並列クエリの実行に関与し、スライス全体でできるだけ均等に分散されたデータを処理します。各ノードサイズに含まれるスライスの数の詳細については、「*Amazon Redshift 管理ガイド*」の「[クラスターおよびノードについて](https://docs.aws.amazon.com/redshift/latest/mgmt/working-with-clusters.html#rs-about-clusters-and-nodes)」を参照してください。

 **データの再分散** 

 テーブルにデータをロードすると、そのテーブルの分散スタイルに従って、Amazon Redshift がテーブルの行を各ノードスライスに分散します。クエリプランの一環として、クエリオプティマイザは、クエリの実行を最適化するために必要なデータブロックの配置場所を決定します。クエリの実行中にデータが物理的に移動または再配信されます。再分散では、結合する特定の行を送信するか、またはテーブル全体をすべてのノードにブロードキャストすることが必要となる場合があります。

 データの再分散は、クエリプランコストの相当な部分を占める可能性があり、また、再分散によって生成されるネットワークトラフィックは、他のデータベース処理に影響を及ぼし、システム全体のパフォーマンスを低速化する可能性があります。データを最初にどこに配置すれば最も効果的かを予測すると、データ再分散の影響を最小限に抑えることができます。

 **データ分散の目標** 

 テーブルにデータをロードすると、そのテーブルを作成したときに選択した分散スタイルに従って、Amazon Redshift がテーブルの行をコンピューティングノードとスライスに分散します。データ分散では、次の 2 つの主要目標が設定されます。
+ クラスター内のノード間でワークロードを均等に分散させること。不均等な分散が行われると (データ分散スキュー)、一部のノードの作業量が他のノードよりも多くなり、クエリのパフォーマンスが低下します。
+ クエリの実行中にデータ移動を最小限に抑えるため。結合または集計に関与する行が、他のテーブルの結合列とともにノード上ですでにコロケーションされている場合、クエリ実行中に再分散する必要のあるデータの量はそれほど多くなりません。

データベースにどの分散戦略を選択するかは、クエリのパフォーマンス、ストレージ要件、データロード、およびメンテナンスに重大な影響を及ぼします。各テーブルに最良の分散スタイルを選択することにより、データを均等に分散し、システム全体のパフォーマンスを大幅に向上させることができます。

# 分散スタイル
<a name="c_choosing_dist_sort"></a>

テーブルを作成する場合は、以下の AUTO、EVEN、KEY、または ALL という分散スタイルのいずれかを指定します。

分散スタイルを指定しない場合、Amazon Redshift は AUTO 分散を使用します。

 **AUTO 分散** 

AUTO 分散では、Amazon Redshift はテーブルデータのサイズに基づいて最適な分散スタイルを割り当てます。例えば、AUTO 分散スタイルが指定された場合、Amazon Redshift ではまず、ALL 分散スタイルを小さなテーブルに割り当てます。テーブルが大きくなると、Amazon Redshift は分散スタイルを KEY に変更し、プライマリキー (または複合プライマリキーの列) を分散キーとして選択する場合があります。テーブルが大きくなり、分散キーに適した列がない場合、Amazon Redshift は分散スタイルを EVEN に変更します。ディストリビューションスタイルの変更は、ユーザークエリへの影響を最小限に抑え、バックグラウンドで発生します。

テーブルのディストリビューションキーを変更するために Amazon Redshift が自動的に実行したアクションを表示するには、[SVL\$1AUTO\$1WORKER\$1ACTION](r_SVL_AUTO_WORKER_ACTION.md) を参照してください。テーブルのディストリビューションキーの変更に関する現在の推奨事項を表示するには、[SVV\$1ALTER\$1TABLE\$1RECOMMENDATIONS](r_SVV_ALTER_TABLE_RECOMMENDATIONS.md) を参照してください。

テーブルに適用された分散スタイルを表示するには、PG\$1CLASS\$1INFO システムカタログビューに対してクエリを実行します。詳細については、「[分散スタイルの表示](viewing-distribution-styles.md)」を参照してください。CREATE TABLE ステートメントで分散スタイルを指定しない場合、Amazon Redshift は AUTO 分散を適用します。

 **EVEN 分散** 

 リーダーノードは、特定の列の値に含まれている値にかかわらず、ラウンドロビン方式によって複数のスライス間で行を分散させます。テーブルが結合に参加しない場合は、EVEN ディストリビューションが適切です。KEY ディストリビューションと ALL ディストリビューションのどちらかを明確に選択できない場合にも適しています。

 **キー分散** 

 行の分散は、特定の列に含まれている値に従って行われます。リーダーノードは、複数の一致する値を同じノードスライスに配置します。結合キーに基づいてテーブルのペアを分散する場合、リーダーノードは、結合列に含まれている値に従って行をスライスにコロケーションします。このようにして、共通の列から一致する値が物理的に一緒に保存されます。

 **ALL 分散** 

 テーブル全体のコピーがすべてのノードに分散されます。EVEN 分散または KEY 分散によってテーブルの一部の行のみが各ノードに配置されている場合、ALL 分散を行うと、テーブルが関与しているあらゆる結合ですべての行が確実にコロケーションされるようになります。

 ALL 分散では、クラスタ内のノードの数だけ必要なストレージが増えるため、データをロードまたは更新したり、複数のテーブルに挿入したりするのに時間がかかります。ALL 分散は、比較的移動の少ないテーブル、つまり、更新頻度が低く、更新範囲が広くないテーブルに適しています。クエリ中に小さなテーブルを再分散するコストは低いため、小さいディメンションテーブルを DISTSTYLE ALL として定義しても大きな利点はありません。

**注記**  
 列のディストリビューションスタイルを指定した後、Amazon Redshift はクラスターレベルでデータ配信を処理します。Amazon Redshift は、データベースオブジェクト内でのデータをパーティション化するという概念を必要とせず、サポートしていません。テーブルスペースを作成したり、テーブルのパーティション化方式を定義したりする必要はありません。

シナリオによっては、テーブルの作成後にそのテーブルの分散スタイルを変更することができます。詳細については、「[ALTER TABLE](r_ALTER_TABLE.md)」を参照してください。テーブルの作成後にテーブルの分散スタイルを変更できない場合は、テーブルを再作成して新しいテーブルにディープコピーを作成できます。詳細については、[ディープコピーを実行する](performing-a-deep-copy.md) を参照してください。

# 分散スタイルの表示
<a name="viewing-distribution-styles"></a>

テーブルの分散スタイルを表示するには、PG\$1CLASS\$1INFO ビューまたは SVV\$1TABLE\$1INFO ビューに対してクエリを実行します。

テーブルの現在の分散スタイルは、PG\$1CLASS\$1INFO の RELEFFECTIVEDISTSTYLE 列に示されます。テーブルが自動分散を使用する場合、RELEFFECTIVEDISTSTYLE は 10、11、または 12 です。これは、効率的な分散スタイルが AUTO (ALL)、AUTO (EVEN)、または AUTO (KEY) のどれであるかを示します。テーブルが自動分散を使用する場合、分散スタイルには当初 AUTO (ALL) が表示され、その後テーブルが大きくなると AUTO (EVEN) または AUTO (KEY) に変更される場合があります。

次の表は、RELEFFECTIVEDISTSTYLE 列に含まれる各値の分散スタイルを示しています。


| RELEFFECTIVEDISTSTYLE | 現在の分散スタイル | 
| --- | --- | 
| 0 | EVEN | 
| 1 | KEY | 
| 8 | ALL | 
| 10 | AUTO (ALL) | 
| 11 | AUTO (EVEN) | 
| 12 | AUTO (KEY) | 

テーブルの現在の分散スタイルは、SVV\$1TABLE\$1INFO の DISTSTYLE 列に示されます。テーブルが分散スタイルを使用する場合、DISTSTYLE は AUTO (ALL)、AUTO (EVEN)、または AUTO (KEY) になります。

次の例では、3 つの分散スタイルと自動分散を使用して 4 つのテーブルを作成した後で、分散スタイルを表示するために SVV\$1TABLE\$1INFO に対するクエリを実行します。

```
create table public.dist_key (col1 int)
diststyle key distkey (col1);

insert into public.dist_key values (1);

create table public.dist_even (col1 int)
diststyle even;

insert into public.dist_even values (1);

create table public.dist_all (col1 int)
diststyle all;

insert into public.dist_all values (1);

create table public.dist_auto (col1 int);

insert into public.dist_auto values (1);

select "schema", "table", diststyle from SVV_TABLE_INFO
where "table" like 'dist%';

        schema   |    table        | diststyle
     ------------+-----------------+------------
      public     | dist_key        | KEY(col1)
      public     | dist_even       | EVEN
      public     | dist_all        | ALL
      public     | dist_auto       | AUTO(ALL)
```

# クエリパターンの評価
<a name="t_evaluating_query_patterns"></a>

 分散スタイルの選択は、データベース設計の 1 つの側面にすぎません。分散スタイルをシステム全体のコンテキストの中で検討し、クラスターサイズ、圧縮エンコード方法、ソートキー、テーブル制約など、他の重要な要因と分散の間でバランスを取ることが必要です。

 できる限り実際のデータに近いデータを使用してシステムをテストします。

分散スタイルについて適切な選択を行うには、Amazon Redshift アプリケーションのクエリパターンを理解しておく必要があります。システム内で最もコストの大きいクエリを特定し、それらのクエリの要求に基づいてデータベースの初期設計を行います。クエリの総コストを決定する要因には、クエリの実行に要する時間、およびクエリによって使用されるコンピューティングリソースの量などがあります。クエリのコストを決定するその他の要因には、クエリの実行頻度、他のクエリやデータベースのオペレーションに及ぼす影響の度合いなどがあります。

 最もコストの大きいクエリによって使用されるテーブルを特定し、クエリランタイムにおけるそれらのテーブルの役割を評価します。テーブルの結合および集計方法を検討します。

 このセクションに示すガイドラインに従って、各テーブルの分散スタイルを選択します。それが完了したら、テーブルを作成し、実際のデータにできるだけ近いデータをテーブルにロードします。次に、使用する予定のクエリの種類についてテーブルのテストを行います。クエリの説明プランを評価して、調整の余地を特定できます。ロード時間、ストレージスペース、およびクエリランタイムを比較して、システムの全体的な要件のバランスを取ります。

# 分散スタイルの指定
<a name="t_designating_distribution_styles"></a>

 このセクションでは、分散スタイルの指定に関する検討事項と推奨事項にスタースキーマを例として使用しています。データベース設計は、スタースキーマに基づいている場合もあれば、スタースキーマの変形または完全に異なるスキーマに基づいている場合もあります。Amazon Redshift は、選択したスキーマデザインで効果的に機能するように設計されています。このセクションで示す原則は、どの設計スキーマにも適用できます。

1.  **すべてのテーブルのプライマリキーと外部キーを指定します。**

   プライマリキーおよび外部キーの制約は、Amazon Redshift によって強制されることはありませんが、クエリプランの生成時にクエリオプティマイザによって使用されます。プライマリキーと外部キーを設定する場合は、アプリケーションがキーの有効性を維持する必要があります。

1.  **ファクトテーブルとその最大のディメンションテーブルを共通の列に分散します。**

   テーブルのサイズだけでなく、最も一般的な結合に関与しているデータセットのサイズにも基づいて、最大のディメンションを選択します。WHERE 句を使用したテーブルのフィルタリングが一般的に行われている場合は、そのテーブルの行の一部しか結合に関与しません。このようなテーブルが再分散に及ぼす影響は、より多くのデータを提供するサイズの小さなテーブルよりも小さくなります。ディメンションテーブルのプライマリキーとそれに対応するファクトテーブルの外部キーをいずれも DISTKEY として指定します。複数のテーブルが同じディストリビューションキーを使用している場合、これらのテーブルはファクトテーブルともコロケーションされます。ファクトテーブルに指定できる分散キーは 1 つだけです。別のキーを使用して結合しているテーブルは、ファクトテーブルとコロケーションされません。

1.  **他のディメンションテーブルの分散キーを指定します。**

   他のテーブルとの結合に最も一般的に使用される方法に応じて、プライマリキーまたは外部キーを使用してテーブルを分散させます。

1.  **一部のディメンションテーブルで ALL 分散を使用するよう変更すべきかどうかを評価します。**

   ファクトテーブルやその他の重要な結合テーブルとディメンションテーブルをコロケーションできない場合は、テーブル全体を全ノードに分散させることによってパフォーマンスを大幅に向上させることができます。ALL 分散を使用すると、ストレージ領域要件の増大、ロード時間の長期化、メンテナンス操作の増加を招くため、ALL 分散を選択する前にすべての要因を比較検討しておく必要があります。次のセクションでは、EXPLAIN プランを評価することによって ALL 分散の適用候補を特定する方法について説明します。

1.  **残りのテーブルに AUTO 分散を使用します。**

   テーブルがほとんど非正規化され、結合に関与していない場合や、別のディストリビューションスタイルとして何を選択すべきかが明らかでない場合は、AUTO 分散を使用します。

Amazon Redshift が適切な分散スタイルを選択できるようにするには、分散スタイルを明示的に指定しないでください。

# クエリプランの評価
<a name="c_data_redistribution"></a>

クエリプランを使用すると、分散スタイル最適化の候補を特定できます。

初期設計の決定を行った後で、テーブルを作成し、テーブルにデータをロードして、テーブルをテストします。実際のデータにできるだけ近いテストデータセットを使用します。比較のベースラインとして使用するロード時間を測定します。

実行する予定のクエリのうち、典型的な高コストクエリ、つまり結合と集計を使用するクエリを評価します。さまざまな設計オプションの実行時間を比較します。実行時間を比較する際は、最初のランタイムにはコンパイル時間が含まれるため、最初の時間はカウントしないでください。

**DS\$1DIST\$1NONE**  
対応するスライスはコンピューティングノードにコロケーションされているため、再分散は必要となりません。通常は、DS\$1DIST\$1NONE ステップ (ファクトテーブルと単一のディメンションテーブル間の結合) が 1 つあるだけです。

**DS\$1DIST\$1ALL\$1NONE**  
内部結合テーブルで DISTSTYLE ALL が使用されているため、再分散は必要となりません。テーブル全体が各ノードに配置されます。

**DS\$1DIST\$1INNER**  
内部テーブルが再分散されます。

**DS\$1DIST\$1OUTER**  
外部テーブルが再分散されます。

**DS\$1BCAST\$1INNER**  
内部テーブル全体のコピーがすべてのコンピューティングノードにブロードキャストされます。

**DS\$1DIST\$1ALL\$1INNER**  
外部テーブルで DISTSTYLE ALL が使用されるため、内部テーブル全体が単一スライスに再分散されます。

**DS\$1DIST\$1BOTH**  
両方のテーブルが再分散されます。

**DS\$1DIST\$1ERR**  
テーブルにディストリビューションスタイルが選択されていない場合。

DS\$1DIST\$1NONE と DS\$1DIST\$1ALL\$1NONE が好適です。この 2 つは、すべての結合がコロケーションされているため、そのステップで分散が必要とならなかったことを示します。

DS\$1DIST\$1INNER は、内部テーブルがノードに再分散されているため、ステップのコストが比較的高くなる可能性があることを意味します。DS\$1DIST\$1INNER は、外部テーブルが結合キーを使用してすでに正しく分散されていることを示します。これを DS\$1DIST\$1NONE に変換するには、内部テーブルの分散キーを結合キーに設定します。場合によっては、外部テーブルが結合キーを使用して配信されていないため、結合キーを使用して内部テーブルを配信できないことがあります。この場合、内部テーブルに ALL ディストリビューションを使用するかどうかを評価します。テーブルの更新頻度が低いか、更新範囲が広くない場合で、高い再ディストリビューションコストに対応できるだけの十分なテーブルサイズの場合は、ディストリビューションスタイルを ALL に変更してから、再びテストします。ALL 分散はロード時間の長期化を招くため、再テストする場合は、評価要因にロード時間を含めてください。

DS\$1DIST\$1ALL\$1INNER は好適ではありません。これは、外部テーブルで DISTSTYLE ALL が使用されているため、外部テーブル全体のコピーが各ノードに配置されるよう、内部テーブル全体が単一スライスに再分散されることを意味します。その結果、全ノードを使用した並列ランタイムのメリットを生かせず、単一ノードで結合が直列ランタイムになるという非効率が生じます。DISTSTYLE ALL は、内部結合テーブルでのみ使用するよう設計されています。代わりに、外部テーブルの分散キーを指定するか、EVEN 分散を使用してください。

DS\$1BCAST\$1INNER と DS\$1DIST\$1BOTH は好適ではありません。通常、これらの再分散は、再分散キーを使用してテーブルが結合されないために行われます。ファクトテーブルにまだ分散キーが指定されていない場合は、両方のテーブルの分散キーとして結合列を指定します。ファクトテーブルの別の列にディストリビューションキーがすでに指定されている場合は、この結合をコロケーションするためにディストリビューションキーを変更することが全般的なパフォーマンスの向上につながるかどうかを評価します。外部テーブルのディストリビューションキーを変更することが最適な選択肢でない場合は、内部テーブルに DISTSTYLE ALL を指定することによってコロケーションを実現できます。

 次の例は、DS\$1BCAST\$1INNER ラベルと DS\$1DIST\$1NONE ラベルを持つクエリプランの一部を示しています。

```
->  XN Hash Join DS_BCAST_INNER  (cost=112.50..3272334142.59 rows=170771 width=84)
        Hash Cond: ("outer".venueid = "inner".venueid)
        ->  XN Hash Join DS_BCAST_INNER  (cost=109.98..3167290276.71 rows=172456 width=47)
              Hash Cond: ("outer".eventid = "inner".eventid)
              ->  XN Merge Join DS_DIST_NONE  (cost=0.00..6286.47 rows=172456 width=30)
                    Merge Cond: ("outer".listid = "inner".listid)
                    ->  XN Seq Scan on listing  (cost=0.00..1924.97 rows=192497 width=14)
                    ->  XN Seq Scan on sales  (cost=0.00..1724.56 rows=172456 width=24)
```

DISTSTYLE ALL を使用するようディメンションテーブルに変更を加えると、同じクエリのクエリプランに DS\$1BCAST\$1INNER ではなく DS\$1DIST\$1ALL\$1NONE が表示されるようになります。また、結合ステップの相対的なコストも大幅に変化します。合計コストは前のクエリの `3272334142.59` と比べて `14142.59` です。

```
->  XN Hash Join DS_DIST_ALL_NONE  (cost=112.50..14142.59 rows=170771 width=84)
        Hash Cond: ("outer".venueid = "inner".venueid)
        ->  XN Hash Join DS_DIST_ALL_NONE  (cost=109.98..10276.71 rows=172456 width=47)
              Hash Cond: ("outer".eventid = "inner".eventid)
              ->  XN Merge Join DS_DIST_NONE  (cost=0.00..6286.47 rows=172456 width=30)
                    Merge Cond: ("outer".listid = "inner".listid)
                    ->  XN Seq Scan on listing  (cost=0.00..1924.97 rows=192497 width=14)
                    ->  XN Seq Scan on sales  (cost=0.00..1724.56 rows=172456 width=24)
```

# クエリプランの例
<a name="t_explain_plan_example"></a>

この例では、クエリプランを評価して分散最適化の機会を特定する方法を示します。

EXPLAIN コマンドを使用して次のクエリを実行し、クエリプランを作成します。

```
explain
select lastname, catname, venuename, venuecity, venuestate, eventname, 
month, sum(pricepaid) as buyercost, max(totalprice) as maxtotalprice
from category join event on category.catid = event.catid
join venue on venue.venueid = event.venueid
join sales on sales.eventid = event.eventid
join listing on sales.listid = listing.listid
join date on sales.dateid = date.dateid
join users on users.userid = sales.buyerid
group by lastname, catname, venuename, venuecity, venuestate, eventname, month
having sum(pricepaid)>9999
order by catname, buyercost desc;
```

TICKIT データベースでは、SALES がファクトテーブルであり、LISTING がその最大のディメンションです。テーブルをコロケーションするために、SALES は LISTING の外部キーである LISTID を使用して分散され、LISTING はそのプライマリキーである LISTID を使用して分散されます。次の例は、SALES および LISTING の CREATE TABLE コマンドを示しています。

```
create table sales(
	salesid integer not null,
	listid integer not null distkey,
	sellerid integer not null,
	buyerid integer not null,
	eventid integer not null encode mostly16,
	dateid smallint not null,
	qtysold smallint not null encode mostly8,
	pricepaid decimal(8,2) encode delta32k,
	commission decimal(8,2) encode delta32k,
	saletime timestamp,
	primary key(salesid),
	foreign key(listid) references listing(listid),
	foreign key(sellerid) references users(userid),
	foreign key(buyerid) references users(userid),
	foreign key(dateid) references date(dateid))
        sortkey(listid,sellerid);

create table listing(
	listid integer not null distkey sortkey,
	sellerid integer not null,
	eventid integer not null encode mostly16,
	dateid smallint not null,
	numtickets smallint not null encode mostly8,
	priceperticket decimal(8,2) encode bytedict,
	totalprice decimal(8,2) encode mostly32,
	listtime timestamp,
	primary key(listid),
	foreign key(sellerid) references users(userid),
	foreign key(eventid) references event(eventid),
	foreign key(dateid) references date(dateid));
```

次のクエリプランでは、SALES および LISTING を使用した結合のマージ結合ステップに、そのステップで再分散が必要とならないことを意味する DS\$1DIST\$1NONE が示されています。ただし、クエリプランを上に移動するとわかるとおり、他の内部結合には、クエリ実行の一環として内部テーブルがブロードキャストされることを意味する DS\$1BCAST\$1INNER が示されています。キー分散を使用してコロケーションできるテーブルは 1 ペアのみであるため、5 つのテーブルを再ブロードキャストする必要があります。

```
QUERY PLAN
XN Merge  (cost=1015345167117.54..1015345167544.46 rows=1000 width=103)
  Merge Key: category.catname, sum(sales.pricepaid)
  ->  XN Network  (cost=1015345167117.54..1015345167544.46 rows=170771 width=103)
        Send to leader
        ->  XN Sort  (cost=1015345167117.54..1015345167544.46 rows=170771 width=103)
              Sort Key: category.catname, sum(sales.pricepaid)
              ->  XN HashAggregate  (cost=15345150568.37..15345152276.08 rows=170771 width=103)
                    Filter: (sum(pricepaid) > 9999.00)
	                    ->  XN Hash Join DS_BCAST_INNER  (cost=742.08..15345146299.10 rows=170771 width=103)
	                          Hash Cond: ("outer".catid = "inner".catid)
	                          ->  XN Hash Join DS_BCAST_INNER  (cost=741.94..15342942456.61 rows=170771 width=97)
	                                Hash Cond: ("outer".dateid = "inner".dateid)
	                                ->  XN Hash Join DS_BCAST_INNER  (cost=737.38..15269938609.81 rows=170766 width=90)
	                                      Hash Cond: ("outer".buyerid = "inner".userid)
	                                      ->  XN Hash Join DS_BCAST_INNER  (cost=112.50..3272334142.59 rows=170771 width=84)
	                                            Hash Cond: ("outer".venueid = "inner".venueid)
	                                            ->  XN Hash Join DS_BCAST_INNER  (cost=109.98..3167290276.71 rows=172456 width=47)
	                                                  Hash Cond: ("outer".eventid = "inner".eventid)
	                                                  ->  XN Merge Join DS_DIST_NONE  (cost=0.00..6286.47 rows=172456 width=30)
	                                                        Merge Cond: ("outer".listid = "inner".listid)
	                                                        ->  XN Seq Scan on listing  (cost=0.00..1924.97 rows=192497 width=14)
	                                                        ->  XN Seq Scan on sales  (cost=0.00..1724.56 rows=172456 width=24)
	                                                  ->  XN Hash  (cost=87.98..87.98 rows=8798 width=25)
	                                                        ->  XN Seq Scan on event  (cost=0.00..87.98 rows=8798 width=25)
	                                            ->  XN Hash  (cost=2.02..2.02 rows=202 width=41)
	                                                  ->  XN Seq Scan on venue  (cost=0.00..2.02 rows=202 width=41)
	                                      ->  XN Hash  (cost=499.90..499.90 rows=49990 width=14)
	                                            ->  XN Seq Scan on users  (cost=0.00..499.90 rows=49990 width=14)
	                                ->  XN Hash  (cost=3.65..3.65 rows=365 width=11)
	                                      ->  XN Seq Scan on date  (cost=0.00..3.65 rows=365 width=11)
	                          ->  XN Hash  (cost=0.11..0.11 rows=11 width=10)
	                                ->  XN Seq Scan on category  (cost=0.00..0.11 rows=11 width=10)
```

DISTSTYLE ALL を使用してテーブルを変更することは 1 つの解決策です。

```
ALTER TABLE users ALTER DISTSTYLE ALL;
ALTER TABLE venue ALTER DISTSTYLE ALL;
ALTER TABLE category ALTER DISTSTYLE ALL;
ALTER TABLE date ALTER DISTSTYLE ALL;
ALTER TABLE event ALTER DISTSTYLE ALL;
```

EXPLAIN を使用して同じクエリを再実行し、新しいクエリプランを検証します。DISTSTYLE ALL を使用して全ノードにデータが分散されたため再分散が必要とならないことを意味する DS\$1DIST\$1ALL\$1NONE が、結合に表示されるようになります。

```
QUERY PLAN
XN Merge  (cost=1000000047117.54..1000000047544.46 rows=1000 width=103)
  Merge Key: category.catname, sum(sales.pricepaid)
  ->  XN Network  (cost=1000000047117.54..1000000047544.46 rows=170771 width=103)
        Send to leader
        ->  XN Sort  (cost=1000000047117.54..1000000047544.46 rows=170771 width=103)
              Sort Key: category.catname, sum(sales.pricepaid)
              ->  XN HashAggregate  (cost=30568.37..32276.08 rows=170771 width=103)
                    Filter: (sum(pricepaid) > 9999.00)
                    ->  XN Hash Join DS_DIST_ALL_NONE  (cost=742.08..26299.10 rows=170771 width=103)
                          Hash Cond: ("outer".buyerid = "inner".userid)
                          ->  XN Hash Join DS_DIST_ALL_NONE  (cost=117.20..21831.99 rows=170766 width=97)
                                Hash Cond: ("outer".dateid = "inner".dateid)
                                ->  XN Hash Join DS_DIST_ALL_NONE  (cost=112.64..17985.08 rows=170771 width=90)
                                      Hash Cond: ("outer".catid = "inner".catid)
                                      ->  XN Hash Join DS_DIST_ALL_NONE  (cost=112.50..14142.59 rows=170771 width=84)
                                            Hash Cond: ("outer".venueid = "inner".venueid)
                                            ->  XN Hash Join DS_DIST_ALL_NONE  (cost=109.98..10276.71 rows=172456 width=47)
                                                  Hash Cond: ("outer".eventid = "inner".eventid)
                                                  ->  XN Merge Join DS_DIST_NONE  (cost=0.00..6286.47 rows=172456 width=30)
                                                        Merge Cond: ("outer".listid = "inner".listid)
                                                        ->  XN Seq Scan on listing  (cost=0.00..1924.97 rows=192497 width=14)
                                                        ->  XN Seq Scan on sales  (cost=0.00..1724.56 rows=172456 width=24)
                                                  ->  XN Hash  (cost=87.98..87.98 rows=8798 width=25)
                                                        ->  XN Seq Scan on event  (cost=0.00..87.98 rows=8798 width=25)
                                            ->  XN Hash  (cost=2.02..2.02 rows=202 width=41)
                                                  ->  XN Seq Scan on venue  (cost=0.00..2.02 rows=202 width=41)
                                      ->  XN Hash  (cost=0.11..0.11 rows=11 width=10)
                                            ->  XN Seq Scan on category  (cost=0.00..0.11 rows=11 width=10)
                                ->  XN Hash  (cost=3.65..3.65 rows=365 width=11)
                                      ->  XN Seq Scan on date  (cost=0.00..3.65 rows=365 width=11)
                          ->  XN Hash  (cost=499.90..499.90 rows=49990 width=14)
                                ->  XN Seq Scan on users  (cost=0.00..499.90 rows=49990 width=14)
```

# 分散の例
<a name="c_Distribution_examples"></a>

次の例は、CREATE TABLE ステートメントで定義したオプションに応じてデータがどのように分散されるかを示しています。

## DISTKEY の例
<a name="c_Distribution_examples-distkey-examples"></a>

TICKIT データベースの USERS テーブルのスキーマを確認します。USERID が SORTKEY 列および DISTKEY 列として定義されています。

```
select "column", type, encoding, distkey, sortkey 
from pg_table_def where tablename = 'users';
    
    column     |          type          | encoding | distkey | sortkey
---------------+------------------------+----------+---------+---------
 userid        | integer                | none     | t       |       1
 username      | character(8)           | none     | f       |       0
 firstname     | character varying(30)  | text32k  | f       |       0

...
```

このテーブルの分散列として USERID を選択するのは適切です。SVV\$1DISKUSAGE システムビューに対してクエリを実行すると、テーブルが非常に均等に分散されていることがわかります。列数はゼロベースであるため、USERID は列 0 です。

```
select slice, col, num_values as rows, minvalue, maxvalue
from svv_diskusage
where name='users' and col=0 and rows>0
order by slice, col;

slice| col | rows  | minvalue | maxvalue
-----+-----+-------+----------+----------
0    | 0   | 12496 | 4        | 49987
1    | 0   | 12498 | 1        | 49988
2    | 0   | 12497 | 2        | 49989
3    | 0   | 12499 | 3        | 49990
(4 rows)
```

テーブルには 49,990 行が含まれます。行 (num\$1values) 列は、各スライスにほぼ同じ数の行が含まれることを示します。minvalue 列と maxvalue 列は、各スライスの値の範囲を示します。各スライスには、値のほぼ全範囲が含まれるため、各スライスは、ユーザー ID の範囲をフィルターするクエリの実行に参加する可能性が高くなります。

この例は、小規模なテストシステムでの分散を示しています。通常、スライスの合計数はこれよりかなり多くなります。

STATE 列を使用してよく参加または結合する場合は、STATE 列で分散する選択を行うことができます。次の例では、USERS テーブルと同じデータを含む新しいテーブルを作成し、DISTKEY を STATE 列に設定した場合を示しています。この場合、ディストリビューションは均等ではありません。スライス 0 (13,587 行) には、スライス 3 (10,150 行) よりも約 30% 多く行が含まれます。これよりかなり大きなテーブルでは、この量の分散スキューがあるとクエリ処理に悪影響を及ぼす可能性があります。

```
create table userskey distkey(state) as select * from users;

select slice, col, num_values as rows, minvalue, maxvalue from svv_diskusage
where name = 'userskey' and col=0 and rows>0
order by slice, col;

slice | col | rows  | minvalue | maxvalue
------+-----+-------+----------+----------
    0 |   0 | 13587 |        5 |    49989
    1 |   0 | 11245 |        2 |    49990
    2 |   0 | 15008 |        1 |    49976
    3 |   0 | 10150 |        4 |    49986
(4 rows)
```

## DISTSTYLE EVEN の例
<a name="c_Distribution_examples-diststyle-even-example"></a>

USERS テーブルと同じデータを含む新しいテーブルを作成し、DISTSTYLE を EVEN に設定した場合、行はスライス間で常に均等に分散されます。

```
create table userseven diststyle even as 
select * from users;

select slice, col, num_values as rows, minvalue, maxvalue from svv_diskusage
where name = 'userseven' and col=0 and rows>0
order by slice, col;

slice | col | rows  | minvalue | maxvalue
------+-----+-------+----------+----------
    0 |   0 | 12497 |        4 |    49990
    1 |   0 | 12498 |        8 |    49984
    2 |   0 | 12498 |        2 |    49988
    3 |   0 | 12497 |        1 |    49989  
(4 rows)
```

ただし、分散が特定の列に基づいて行われないので、特にテーブルが他のテーブルに結合される場合に、クエリ処理の質が低下する可能性があります。結合列で分散が行われないと、多くの場合、効率的に実行できるタイプの結合操作に影響を及ぼします。結合、集計、およびグループ化操作は、両方のテーブルがそれぞれの結合列で分散およびソートされる場合に最適化されます。

## DISTSTYLE ALL の例
<a name="c_Distribution_examples-diststyle-all-example"></a>

USERS テーブルと同じデータを使用して新しいテーブルを作成して、DISTSTYLE を ALL に設定すると、すべての行が各ノードの最初のスライスに分散されます。

```
select slice, col, num_values as rows, minvalue, maxvalue from svv_diskusage
where name = 'usersall' and col=0 and rows > 0
order by slice, col;

slice | col | rows  | minvalue | maxvalue
------+-----+-------+----------+----------
    0 |   0 | 49990 |        4 |    49990
    2 |   0 | 49990 |        2 |    49990

(4 rows)
```