UNNEST句を使用したネスト解除問合せの最適化
ノート:
UNNEST句によって適用される制限の詳細は、UNNEST句での式の使用の制限を参照してください。ここで、ネスト解除問合せを使用するための表の作成およびデータのロード用のSQL文を使用できます。例1: 米国で放送された様々な番組と、それらを視聴する人の数をフェッチします
- 問合せは国(米国)および特定の番組に固有です。そのため、
stream_acct
表のcountry
およびshowid
フィールドに索引を作成します。CREATE INDEX idx_country_showid ON stream_acct( acct_data.country AS string, acct_data.contentStreamed[].showId AS integer) WITH UNIQUE KEYS PER ROW
ノート:
索引と同じ配列/マップをネスト解除する問合せで索引を使用できるようにするには、行プロパティごとに一意のキーを使用して索引を作成する必要があります。 - UNNEST句を使用して、米国で放送された様々な番組と、それを視聴する人の数をフェッチする問合せ。
出力:SELECT $show.showId, count(*) as cnt FROM stream_acct $s, unnest($s.acct_data.contentStreamed[] as $show) WHERE $s.acct_data.country = "USA" GROUP BY $show.showId ORDER BY count(*) desc
前述の問合せは{"showId":15,"cnt":2} {"showId":16,"cnt":2}
idx_country_showid
索引を使用します。国の条件は索引にプッシュされ、グループ化は索引ベースになり、索引はこの問合せのカバー索引です。問合せを解決するために必要なすべての情報を含む索引は、カバー索引と呼ばれ、問合せを完全にカバーします。カバー索引にはすべての列が含まれ、問合せはSELECT句、JOIN句およびWHERE句で参照します。UNNEST句を使用しない場合、索引は考慮されません。
例2: アプリケーションで放送されたすべての番組についての、すべてのユーザーの合計視聴時間:
SELECT $show.showId, sum($show.seriesInfo.episodes.minWatched)
AS total_time FROM stream_acct $s,
unnest($s.acct_data.contentStreamed[] AS $show)
GROUP BY $show.showId
ORDER BY sum($show.seriesInfo.episodes.minWatched)
showId
に基づいてデータがグループ化されているため、acct_data.contentStreamed[].showId
に索引を作成するという効果を検討します。
UNNEST句の使用に関係なく、この問合せではidx_showid
索引を使用できません。これは、sum()
関数の引数によるものです。idx_showid
索引には、showId
(および主キー)のみが含まれます。そのため、式$show.seriesInfo.episodes.minWatched
は索引から評価できません。この問合せを最適化するには、2つの方法があります。
オプション1: 追加索引の作成:
showId
フィールドとminWatched
フィールドの両方を問合せで使用するため、それらに追加のコンポジット索引を作成します。CREATE INDEX idx_showid_minWatched ON
stream_acct(acct_data.contentStreamed[].showId AS integer,
acct_data.contentStreamed[].seriesInfo[].episodes[].minWatched AS integer,
acct_data.contentStreamed[].seriesInfo[].episodes[].episodeID as integer)
WITH UNIQUE KEYS PER ROW
ノート:
行ごとに一意のキーの制約を満たすには、この索引でepisodeID
を最後の索引パスとして追加する必要があります。この索引は、問合せによってカバー索引として使用されます。
オプション2: 追加索引のコストの回避:
idx_showid
索引を使用するように問合せをリライトできます。次のリライトされた問合せではidx_showid
索引が使用されますが、索引はカバーしていません。SELECT $show.showId, sum($s.acct_data.contentStreamed[$element.showId = $show.showId].
seriesInfo.episodes.minWatched) AS total_time
FROM stream_acct $s, unnest($s.acct_data.contentStreamed[] AS $show) GROUP BY $show.showId
ORDER BY sum($s.acct_data.contentStreamed[$element.showId = $show.showId].seriesInfo.episodes.minWatched)
出力:
{"showId":26,"total_time":225}
{"showId":16,"total_time":440}
{"showId":15,"total_time":642}
例3: 番組およびシーズンごとのユーザーの合計視聴時間
SELECT $show.showId, $seriesInfo.seasonNum,
sum($seriesInfo.episodes.minWatched) AS length
FROM stream_acct n,
unnest(n.acct_data.contentStreamed[] AS $show, $show.seriesInfo[] as $seriesInfo)
GROUP BY $show.showId, $seriesInfo.seasonNum
ORDER BY sum($seriesInfo.episodes.minWatched)
出力:
{"showId":26,"seasonNum":2,"length":80}
{"showId":26,"seasonNum":1,"length":145}
{"showId":16,"seasonNum":2,"length":190}
{"showId":16,"seasonNum":1,"length":250}
{"showId":15,"seasonNum":2,"length":295}
{"showId":15,"seasonNum":1,"length":347}
idx_showid_seasonNum_minWatched
は複数キー索引です。行ごとに一意のキーの制約を満たすには、この索引でepisodeID
を最後の索引パスとして追加する必要があります。
CREATE INDEX idx_showid_seasonNum_minWatched ON
stream_acct(acct_data.contentStreamed[].showId as integer,
acct_data.contentStreamed[].seriesInfo[].seasonNum as integer,
acct_data.contentStreamed[].seriesInfo[].episodes[].minWatched as integer,
acct_data.contentStreamed[].seriesInfo[].episodes[].episodeID as integer)
WITH UNIQUE KEYS PER ROW
ノート:
索引は、表のデータ行ごとに索引に複数のエントリが作成される場合、複数キー索引と呼ばれます。複数キー索引には、.keys()、.values()または[]ステップを使用する索引パスが少なくとも1つあります。このような索引パスはすべて、複数キー索引パスと呼ばれます。問合せで、ネスト解除している配列/マップの索引を使用する場合、UNNEST句の各パス式は、索引内の索引パスのmultikey_path_prefixと一致する必要があります。CREATE INDEX文の構文に示すように、multikey_path_prefixは、最後の複数キー・ステップまで(そのステップを含む)の索引パスの一部です。たとえば、最初の索引パスのmultikey_path_prefixは、UNNEST句の最初のパス式に一致する必要があります。そうでない場合、索引は使用されません。
前述の問合せで、式n.value.contentStreamed[]
はidx_showid_seasonNum_minWatched
の最初の索引パスのmultikey_path_prefixと一致し、式$show.seriesInfo[]
は、$show
変数がドメイン式に置き換えられた後の2番目の索引パスのmultikey_path_prefixと一致します。そのため、この索引は問合せで使用されます。