例6: カバー索引を使用したデータのグループ化

すべてのユーザーについて、州(state)および市(city)でグループ化して、州(state)、市(city)および収入(income)合計をフェッチします。
SELECT u.address.state, u.address.city, sum(income) 
AS income FROM Users u GROUP BY u.address.state, u.address.city
問合せ実行計画:
{
   "iterator kind" : "SELECT",
   "FROM" :
   {
      "iterator kind" : "RECEIVE",
      "distribution kind" : "ALL_SHARDS",
      "order by fields at positions" : [ 0, 1 ],
      "input iterator" :
      {
         "iterator kind" : "SELECT",
         "FROM" :
         {
             "iterator kind" : "TABLE",
             "target table" : "Users",
             "row variable" : "$$u",
             "index used" : "idx_state_city_income",
             "covering index" : true,
             "index row variable" : "$$u_idx",
             "index scans" : [
                {
                    "equality conditions" : {},
                    "range conditions" : {}
                }
            ]
          },
          "FROM variable" : "$$u_idx",
          "GROUP BY" : "Grouping by the first 2 expressions in the SELECT list",
          "SELECT expressions" : [
             {
                "field name" : "state",
                "field expression" :
                {
                   "iterator kind" : "FIELD_STEP",
                   "field name" : "address.state",
                   "input iterator" :
                   {
                      "iterator kind" : "VAR_REF",
                      "variable" : "$$u_idx"
                   }
               } 
             },
             {
                "field name" : "city",
                "field expression" :
                {
                   "iterator kind" : "FIELD_STEP",
                   "field name" : "address.city",
                   "input iterator" :
                   {
                      "iterator kind" : "VAR_REF",
                      "variable" : "$$u_idx"
                   }
                }
             },
             {
                "field name" : "income",
                "field expression" :
                {
                   "iterator kind" : "FUNC_SUM",
                   "input iterator" :
                   {
                      "iterator kind" : "FIELD_STEP",
                      "field name" : "income",
                      "input iterator" :
                      {
                         "iterator kind" : "VAR_REF",
                         "variable" : "$$u_idx"
                      }
                   }
                }
            }
       ]
    }
 },
 "FROM variable" : "$from-1",
 "GROUP BY" : "Grouping by the first 2 expressions in the SELECT list",
 "SELECT expressions" : [
    {
       "field name" : "state",
       "field expression" :
       {
          "iterator kind" : "FIELD_STEP",
          "field name" : "state",
          "input iterator" :
          {
             "iterator kind" : "VAR_REF",
             "variable" : "$from-1"
          }
       }
    },
    {
       "field name" : "city",
       "field expression" :
       {
          "iterator kind" : "FIELD_STEP",
          "field name" : "city",
          "input iterator" :
          {
             "iterator kind" : "VAR_REF",
             "variable" : "$from-1"
          }
       }
    },
    {
       "field name" : "income",
       "field expression" :
       {
          "iterator kind" : "FUNC_SUM",
          "input iterator" :
          {
             "iterator kind" : "FIELD_STEP",
             "field name" : "income",
             "input iterator" :
             {
                "iterator kind" : "VAR_REF",
                "variable" : "$from-1"
             }
           }
        }
     }
  ]
}
問合せ実行計画の説明:
  • この例では、stateおよびcityに基づいてユーザーをグループ化してから、グループ化したユーザーのincomesumを算出します。
  • グループ化は索引ベースであるため、GROUP BYのフィールドは、使用される索引の一部でもあります。これは、GROUPイテレータがないことからもわかります。かわりに、グループ化はSELECTイテレータで実行されます。
  • 2つのSELECTイテレータがあり、内側のイテレータには、どのSELECT句式がグループ化式でもあるかを指定するGROUP BYプロパティがあります。ここで、GROUP BYのフィールドは、SELECTのリスト内の最初の2つの式(u.address.state,u.address.city)です。
    "GROUP BY" : "Grouping by the first 2 expressions in the SELECT list"
  • ここでは索引idx_state_city_incomeが使用されています。また、この例では、索引のエントリのみを使用して問合せを評価可能であるため、これはカバー索引です。
  • index row variableは$$u_idxであり、これは、TABLEイテレータによって生成されるすべての索引エントリを対象とする、変数の名前です。索引スキャンによって新しい索引エントリが生成されるたびに、$$u_idx変数がそのエントリにバインドされます。
  • このindex row variable ($$u_idx)は、SELECT式の他の句を実装するイテレータによって参照されます。
  • このSELECT式では、3つのフィールド(statecityおよびsum(income))がフェッチされます。これらは、SELECT式の句にある3つのフィールド名およびフィールド式に対応しています。
  • 内側のSELECTイテレータによって様々なRN (レプリケーション・ノード)から返される結果は、部分的なグループです。これは、州(state)と市(city)が同一の行が、複数のRNに存在する場合があるためです。そのため、再グループ化および再集計をドライバで実行する必要があります。これは、外側のSELECTイテレータ(RECEIVEイテレータの上)によって実行されます。
  • この結果は、stateおよびcityを基準にソートされます。order by fields at positionsプロパティにより、ソートに使用するフィールドを指定します。この配列の値は、SELECT式でのソートするフィールドの位置によって異なります。この例では、SELECT式において、stateが1番目のフィールドで、cityが2番目のフィールドです。したがって、order by fields at positionsの値は0,1になります。
    "order by fields at positions" : [ 0, 1 ]
  • 外側のSELECT式では、3つのフィールド(statecityおよびsum(income))がフェッチされます。FROM variableである$from-1は、外側のSELECT式の他の句を実装するイテレータによって参照されます。これは、外側のSELECT式の句にある3つのフィールド名およびフィールド式に対応しています。この例では、フィールド式のうちの2つでそれらのフィールドのみがフェッチされ、1つのフィールド式で関数(sum(income))が評価されます。
  • このSELECT式で直接フェッチされる2つのフィールドについては、フィールド式がFIELD_STEPイテレータによって計算されます。FIELD_STEPイテレータでは、その入力イテレータで生成されたレコードからフィールド(以下のstate)の値が抽出され返されます。cityフィールドについても同様です。
    {
       "field name" : "state",
       "field expression" :
       {
          "iterator kind" : "FIELD_STEP",
          "field name" : "state",
          "input iterator" :
          {
             "iterator kind" : "VAR_REF",
             "variable" : "$from-1"
          }
       }
    }
    SELECT式内の3番目のフィールドは、incomeの合計を算出する関数です。これには、FUNC_SUMイテレータが使用されます。incomeフィールドの値を反復処理し、その入力イテレータの結果からすべてのincomeの合計を算出します。
    {
       "field name" : "income",
       "field expression" :
       {
          "iterator kind" : "FUNC_SUM",
          "input iterator" :
          {
             "iterator kind" : "FIELD_STEP",
             "field name" : "income",
             "input iterator" :
             {
                "iterator kind" : "VAR_REF",
                "variable" : "$from-1"
             }
          }
       }
    }