解答

CHAPTER 6

016

SQL文(クエリ)

      WITH master AS
      (SELECT product_id
      , SUM(quantity) AS sum_qty
      , SUM(revenue) AS sum_revenue
      , SUM(revenue) / SUM(quantity) AS avg_unit_price
      FROM impress_sweets.sales
      GROUP BY product_id
      )
      
      SELECT *
      , (SELECT AVG(avg_unit_price) FROM master)
      AS avg_unit_price_by_product
      FROM master
      WHERE avg_unit_price >
      (SELECT AVG(avg_unit_price) FROM master)
      ORDER BY 4 DESC
    

結果テーブル

結果テーブル

解説

[sales]テーブルを[product_id]でグループ化し、販売個数合計、販売金額合計、それらを割って求めた平均単価のテーブルを作ったものを、WITH句で「master」という名前の仮想テーブルとして利用しています。[master]テーブルの作成時に、商品ID別のグループ化と集計を行っているため、メインのSQLでの集計は不要です。商品ID別の平均単価の平均について、1つの値を戻り値とするサブクエリで取り出して、5番目のフィールドに加えると同時に、WHERE句の絞り込みに利用しています。

017

SQL文(クエリ)

      WITH master AS
      (SELECT *
      FROM impress_sweets.customers
      WHERE prefecture IN
      (SELECT prefecture
      FROM impress_sweets.customers
      GROUP BY prefecture
      HAVING SUM(IF (is_premium IS TRUE, 1, 0)) >= 4
      ))
      
      SELECT * FROM master
      WHERE birthday = (SELECT MIN(birthday) FROM master)
    

結果テーブル

結果テーブル

解説

WITH句では[customers]テーブルから「プレミアム顧客が4名以上いる都道府県」に合致するレコードを取得しています。WHERE句にて、IN演算子で比較する対象にサブクエリを記述し、そのサブクエリでは、条件に該当する都道府県のリストを返しています。表形式の戻り値を「master」として仮想テーブルで利用します。

「もっとも高齢の顧客を取得する」ということは、誕生日(birthday)が[master]テーブル全体の最小値と一致する、ということなので、「WHERE birthday = (SELECT MIN(birthday) FROM master)」で個別の顧客の誕生日(birthday)と[master]テーブル全体の最小の[birthday]を比較して、同じ顧客だけに絞り込んでいます。

なお、メインのクエリは以下でもよいでしょう。

      SELECT * FROM master
      WHERE birthday IS NOT NULL
      ORDER BY birthday
      LIMIT 1
    

018

SQL文(クエリ)

      SELECT COUNT(*) AS session
      , SUM(pageviews) AS sum_pageviews
      FROM (
      SELECT SUM(
      IF(event_name = "page_view", 1, 0)) AS pageviews
      FROM impress_sweets.web_log
      GROUP BY user_pseudo_id, ga_session_number
      )
    

結果テーブル

結果テーブル

解説

「手ごわいクエリを記述する必要があるのでは?」と心配した人もいるかもしれませんが、実際は上記の通り数行で取得できます。まずは4行目のサブクエリから見ていきましょう。セッション数は「ユニークな[user_pseudo_id]と[ga_session_number]の組み合わせ」と定義されたので、それらでグループ化します。

SECTION 4-1で学んだ通り、グループ化によりフィールドの構成要素(この場合は[user_pseudo_id]と[ga_session_number])が1レコードになるからです。ページビュー数は[event_name]が"page_view"に等しいレコード数と定義されているので、IF文を使い条件が合致した場合に1 を記録しています。

1~2行目の本体のクエリでは、セッションの総数を求めるためにレコード数をCOUNT(*)で集計し、ページビュー数の合計を求めるためにSUM関数で[pageviews]を集計しています。なお、テーブル全体を集計しているのでグループ化は不要です。そのため、GROUP BY句は記述していません。

019

SQL文(クエリ)

      SELECT
      sum_proper_revenue - sum_non_proper_revenue
      AS revenue_diff
      FROM (
      SELECT
      (SELECT SUM(revenue) FROM impress_sweets.sales
      WHERE is_proper IS TRUE)
      AS sum_proper_revenue
      , (SELECT SUM(revenue) FROM impress_sweets.sales
      WHERE is_proper IS FALSE)
      AS sum_non_proper_revenue
      )
    

結果テーブル

結果テーブル

解説

[sales]テーブルから1つだけの値を返すサブクエリとして、まずは定価で販売された場合の販売金額の合計を求めます。この問題のポイントは、4~5行目にある以下のサブクエリです。

      SELECT SUM(revenue) FROM impress_sweets.sales
      WHERE is_proper IS TRUE
    

これが記述できれば、WHERE句の条件を変えて「割引価格で販売された場合の販売金額の合計」も求めることができます。

4行目から10行目のサブクエリでフィールド[sum_proper_revenue]として定価で販売された場合の販売金額合計を求め、フィールド[sum_non_proper_revenue]として割引価格で販売された販売金額合計を求めています。外側のフィールドでは、この2つのフィールドの値を引き算して、解答を求めています。

以下が別解となります。サブクエリで、いわゆる縦持ちの形のまま[is_proper]の値ごとに[revenue]を合計で集計しています。2~3行目の本クエリでは、サブクエリで作成された仮想テーブルが[is_prroper]フィールドが、[true]のレコードと[false]のレコードに分かれていて、そのままでは引き算できないので、集計関数MAXを利用して1行にまとめ、前者から後者を差し引く形で解答を求めています。MAXでなくとも値を1つにまとめる、MIN、AVGでも同じ結果となります。

      SELECT
      MAX(IF(is_proper IS TRUE, revenue,NULL)) -
      MAX(IF(is_proper IS FALSE, revenue,NULL)) AS revenue_diff
      FROM (
      SELECT is_proper, SUM(revenue) AS revenue
      FROM impress_sweets.sales
      GROUP BY is_proper
      )
    

020

SQL文(クエリ)

      SELECT SUM(lost_revenue) AS sum_lost_revenue
      FROM (
      SELECT *
      , quantity * (proper_unit_price - discount_unit_price)
      AS lost_revenue
      FROM (
      SELECT *, revenue / quantity AS discount_unit_price
      , (
      SELECT
      SUM(revenue) / SUM(quantity)
      FROM impress_sweets.sales
      WHERE product_id = 1 AND is_proper IS TRUE
      ) AS proper_unit_price
      FROM impress_sweets.sales
      WHERE product_id = 1 AND is_proper IS FALSE
      )
      )
    

結果テーブル

結果テーブル

解説

[product_id]が「1」の商品について、数量(quantity)、定価の単価(proper_unit_price)、割引価格の単価(discount_unit_price)を1つのテーブルにまとめて、「数量×(定価の単価-割引価格の単価)」を計算した結果を合計するのが、基本的な考え方です。

9~13行目のクエリでは「product_id = 1 AND is_proper IS TRUE」という条件で、商品IDが「1」の定価での販売にレコードを絞り込んでいます。そのうえで[revenue]の合計額と[quantity]の合計数を割り算して求めた「1つの値」を[proper_unit_price]として取得しています。この値が商品ID(product_id)が「1」の定価の単価です。

次に、その外側のクエリ(7~8行目、14~15行目)では、「product_id = 1 AND is_proper IS FALSE」で、割引販売された商品IDが「1」のレコードに絞り込んだうえで、もともと存在していたすべてのフィールドを「*」で取得し、かつ[revenue]を[quantity]で割った割引販売の単価(discount_unit_price)のフィールドと、9~13行目のクエリで取得した[proper_unit_price]を追加しています。

結果的に、7~15行目では、もともとの全フィールド、割引販売の単価(discount_unit_price)、定価での単価(proper_unit_price)が1つのテーブルにそろったことになります。あとは「数量×(定価の単価-割引価格の単価)」で、定価で販売していたら得られた売上を計算しています。

サポートページに戻る