このリファレンスの見方

本書は、「SQL 92」と呼ばれるSQLの標準規格に基づいて記述しています。標準規格に基づいているのですから、本書に記載されているSQLならどんなデータベース(RDBMS;Relational DataBase Management System)でも通用しそうなものですが、実際のSQLの世界では残念ながら、標準規格といえども必ず通用するというわけではないのが現状です。つまり、データベース製品によって、それぞれ少しずつSQLの仕様が異なっているのです。

こうした細かい動作の違いは、データベースユーザの頭を悩ませる要因の1つです。そこで、データベースごとのSQLの違いのうち、本書に関係するものをHTMLにまとめました。

左手に主なデータベース製品の名前が書いてある「タブ」があります。タブをクリックすると、そのデータベースの画面に切り替わります。各画面では、SQLを利用する際の注意点などが、本書の章の順にまとめてあります。各データベース製品を利用して本書の内容を学習する際、そして実際にSQLを利用される際などに、ぜひお役立てください。

PostgreSQL を使う上での全般的な注意

列に別名をつける

カラム名「ColName」に「別名」という別名をつけるには、

  1. AS を使わない方法
    ColName 別名不可
    ColName "別名"不可
    ColName '別名'不可
  2. AS を使う方法
    ColName AS 別名
    ColName AS "別名"
    ColName AS '別名'不可

となります。

文字列の連結を行う

文字列の連結には、

||
+不可
CONCAT()関数不可

となります。

テーブルに別名をつける

テーブルの別名(これを相関名とも呼びます)を指定するには、

  1. AS を使う方法
    SELECT
      e.EmployeeID
    , e.EmployeeName
    FROM
      Employees AS e
    ;
    
  2. AS を使わない方法
    SELECT
      e.EmployeeID
    , e.EmployeeName
    FROM
      Employees e
    ;
    

の2通りがありますが、PostgreSQLではどちらの方法も使えます。

データの型変換に関するご注意

データの型変換は、第5章応用問題に多く出てくる日付型から文字列型への変換が典型的な例ですが各RDBMSによってその方法が異なる場合が多いのが実態です。

PostgreSQLでは日付型の項目を、「2007-12」と変換したければ、

TO_CHAR(日付型の項目,'yyyy-mm')

2007年12月」と変換したければ、

TO_CHAR(日付型の項目,'yyyy年mm月')

とします。

他にも、数値型を文字列型に変換、逆に文字列型を数値型に変換などいろいろな変換方法があります。 また、CAST() 関数によって型変換をすることもできます。

詳細はマニュアルなどで調べてみてください。

PostgreSQL日本語ドキュメント

https://www.postgresql.jp/document/

練習問題・応用問題

練習問題・応用問題について

全問にわたって、「全般的な注意」で記した

  1. カラム名に対する別名の与え方
  2. 文字列の連結の方法
  3. テーブルの別名(相関名)の指定の方法

を守ってください。

第2章 ひとつのテーブルを扱う

その14 クロス集計を行う

練習問題 第3問

PostgreSQL では、日付型の属性の一部を取り出すのに、日付型から文字列型に変換する TO_CHAR() 関数を利用します。

この練習問題では、

TO_CHAR( SaleDate, 'yyyy' )

で、日付型の SaleDate 項目の年部分を文字列型で取り出します。

TO_CHAR( SaleDate, 'mm' )

で、日付型の SaleDate 項目の月部分を文字列型で取り出します。

「全般的な注意」の「データの型変換に関するご注意」も参照してください。

SELECT
  CustomerID AS "顧客ID"
, SUM( CASE
         WHEN TO_CHAR( SaleDate, 'mm' ) = '09' THEN Quantity 
         ELSE 0
       END ) AS "9月" 
, SUM( CASE
         WHEN TO_CHAR( SaleDate, 'mm' ) = '10' THEN Quantity 
         ELSE 0
       END ) AS "10月" 
, SUM( CASE
         WHEN TO_CHAR( SaleDate, 'mm' ) = '11' THEN Quantity 
         ELSE 0
       END ) AS "11月" 
FROM 
  Sales 
WHERE 
  TO_CHAR( SaleDate, 'yyyy' ) = '2006' 
GROUP BY 
  CustomerID
;

第3章 複数のテーブルを扱う

その6 相関副問い合わせを使う

練習問題 第5問

標準SQLではGROUP BY句なしでもHAVING句を使うことができますが、 PostgreSQLではGROUP BY句を要求してきますので、次のとおりにしてください。

SELECT 
  ProductID 
, ProductName 
FROM 
  Products AS A
WHERE 
  ProductID IN
  (
   SELECT 
     ProductID 
   FROM
     Sales AS B
   WHERE
     A.ProductID = B.ProductID 
   GROUP BY
     ProductID
   HAVING
     AVG( Quantity ) <= MAX( Quantity )/10
  )
ORDER BY 
  ProductID 
;

第5章 応用問題

標準SQLのままではエラーになる問題と解決策の例

以下の問題ではPostgreSQLの場合、解答例の標準SQLのままではエラーになります。

それぞれの問題に対する解決策の例を記してみましたので参考にしてください。

その2 月別販売額一覧の作成

SELECT
  x.年月
, SUM( x.販売金額 ) AS "販売合計金額"
FROM
 (
  SELECT
   SUBSTR(CAST( SaleDate AS VARCHAR), 1,7 ) AS "年月"
  , s.Quantity * p.Price AS "販売金額"
  FROM
    Sales AS s
      JOIN
    Products AS p
      ON s.ProductID = p.ProductID
 ) AS x
GROUP BY
  x.年月
ORDER BY
  x.年月
;

その5 顧客別・商品別販売一覧の作成

SELECT
  c.CustomerID
, c.CustomerName
, p.ProductName
, SUM( s.Quantity * p.Price ) AS 販売合計金額
FROM
  Sales AS s
    JOIN
  Products AS p
    ON s.ProductID = p.ProductID
      JOIN
    Customers AS c
      ON s.CustomerID = c.CustomerID
GROUP BY
  c.CustomerID
, c.CustomerName
, p.ProductName
ORDER BY
  c.CustomerID
, c.CustomerName
, p.ProductName
;

その6 都道府県別・商品別販売一覧の作成

SELECT
  pr.PrefecturalID
, pr.PrefecturalName
, p.ProductName
, SUM( s.Quantity * p.Price ) AS 販売合計金額
FROM
  Sales AS s
    JOIN
  Products AS p
    ON s.ProductID = p.ProductID
      JOIN
   Customers AS c
      ON s.CustomerID = c.CustomerID
        JOIN
      Prefecturals AS pr
        ON c.PrefecturalID = pr.PrefecturalID
GROUP BY
  pr.PrefecturalID
, pr.PrefecturalName
, p.ProductName
ORDER BY
  pr.PrefecturalID
, pr.PrefecturalName
, p.ProductName
;

その9 商品別3ヶ月販売推移表の作成

SELECT
  ProductID
, ProductName
, "6月販売金額"
, "7月販売金額"
, CASE WHEN "6月販売金額" < "7月販売金額" THEN '↑'
       WHEN "6月販売金額" = "7月販売金額" THEN '→'
       ELSE '↓'
  END AS 対6月増減
, "8月販売金額"
, CASE WHEN "7月販売金額" < "8月販売金額" THEN '↑'
       WHEN "7月販売金額" = "8月販売金額" THEN '→'
       ELSE '↓'
  END AS 対7月増減
FROM
 (
  SELECT
    p.ProductID
  , p.ProductName
  , SUM(
        CASE WHEN s.SaleDate IS NULL THEN 0
             WHEN SUBSTR( CAST( s.SaleDate AS VARCHAR),1, 7) = '2007-06' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS "6月販売金額"
  , SUM(
        CASE WHEN s.Quantity IS NULL THEN 0
             WHEN SUBSTR( CAST( s.SaleDate AS VARCHAR),1, 7) = '2007-07' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS "7月販売金額"
  , SUM(
        CASE WHEN s.Quantity IS NULL THEN 0
             WHEN SUBSTR( CAST( s.SaleDate AS VARCHAR),1, 7) = '2007-08' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS "8月販売金額"
  FROM
    Products AS p
      LEFT OUTER JOIN
    Sales s
      ON p.ProductID = s.ProductID
  GROUP BY
    p.ProductID
  , p.ProductName
 ) AS x
ORDER BY
  ProductID
;

MySQL を使う上での全般的な注意

ANSIモードでの実行

 本書のサンプルをMySQLで実行する場合は「ANSIモード」で実行してください。

 具体的には、MySQLのデーモン mysqld--ansi または --sql-mode=ANSI オプションをつけて動作させるようにします。

 MySQLは、標準で実行している場合、SQLの標準規格「SQL 92」とは異なる動作をする演算子、引用符などがあります。たとえばMySQLの標準実行時、「||」は「OR(論理和)」を意味します。しかしANSIモードでMySQLを実行させると、「||」もANSI風に文字列連結として解釈されるようになります。

列に別名をつける

カラム名「ColName」に「別名」という別名をつけるには、

  1. AS を使わない方法
    ColName 別名不可*
    ColName "別名"
    ColName '別名'
  2. AS を使う方法
    ColName AS 別名不可*
    ColName AS "別名"
    ColName AS '別名'

となります注1 注2

たとえば、第2章その3の場合、

SELECT
  ProductName AS "商品名"
, Price AS "単価"
FROM
  Products
;

とします(上記表の * 印以外の方法なら問題ありません)。

注1)ただし、列の別名をテーブル名などで修飾する場合、シングルクォート'を使うと文法エラーになります。つまり、「TableName.'別名'」のような記述はできません。
注2)また、ANSIモードで起動していない場合は、ダブルクォート"もシングルクォートと同様に作用します。つまり「TableName."別名"」なども文法エラーです。通常、MYSQLでは、列名などの引用符にバッククォート`を使いますので、この場合は「TableName.`別名`」のようにバッククォートを使って記述します。

文字列の連結を行う

文字列の連結には、CONCAT()関数を使います。

たとえば、第2章その6の場合

SELECT
  CONCAT( CustomerName, '様' ) AS "お名前"
FROM
  Customers
;

とします。

テーブルに別名をつける

テーブルの別名(これを相関名とも呼びます)を指定するには、

  1. AS を使う方法
    SELECT
      e.EmployeeID
    , e.EmployeeName
    FROM
      Employees AS e
    ;
    
  2. AS を使わない方法
    SELECT
      e.EmployeeID
    , e.EmployeeName
    FROM
      Employees e
    ;
    

の2通りがありますが、MySQLではどちらの方法も使えます。

INTERSECTを使う

MySQLではINTERSECTが使えません。

そのため、代わりにEXISTSを使って問い合わせを行います。 たとえば第3章その9の問題では次のSQL文にすれば同じ結果が得られます。

SELECT DISTINCT
  x."氏名"
, x."日付"
FROM
 (
  SELECT
    e.EmployeeName AS "氏名"
  , s.SaleDate AS "日付"
  FROM
    Sales AS s
      JOIN
    Employees AS e
      ON s.EmployeeID = e.EmployeeID
 ) AS x
WHERE
  EXISTS
  (
   SELECT
     y."氏名"
   , y."日付"
   FROM
    (
     SELECT
       e.EmployeeName AS "氏名"
     , s.PayDate AS "日付"
     FROM
       Salary AS s
         JOIN
       Employees AS e
         ON s.EmployeeID = e.EmployeeID
    ) AS y
   WHERE
     x."氏名" = y."氏名"
     AND
     x."日付" = y."日付"
  )
;

EXCEPTを使う

MySQLではEXCEPTが使えません。

そのため、代わりにNOT EXISTSを使って問い合わせを行います。 たとえば、第3章その10の問題では次のSQL文にすれば同じ結果が得られます。

SELECT DISTINCT
  x.EmployeeName
FROM
  Employees AS x
WHERE
  NOT EXISTS
  (
   SELECT
     y.EmployeeName
   FROM
    (
     SELECT
       e.EmployeeName
     FROM
       Sales AS s
         JOIN
       Employees AS e
         ON s.EmployeeID = e.EmployeeID
    ) AS y
   WHERE
     x.EmployeeName = y.EmployeeName
  )
;

データの型変換に関するご注意

データの型変換は、第5章応用問題に多く出てくる日付型から文字列型への変換が典型的な例ですが各RDBMSによってその方法が異なる場合が多いのが実態です。

MySQLでは日付型の項目を、「2007-12」と変換したければ、

DATE_FORMAT(日付型の項目, '%Y-%m')

2007年12月」と変換したければ、

DATE_FORMAT(日付型の項目, '%Y年%m月')

とします。

他にも、数値型を文字列型に変換、逆に文字列型を数値型に変換などいろいろな変換方法があります。

また、CAST()関数によって型変換をすることもできます。

詳細はマニュアルなどで調べてみてください。

My SQL AB :: MySQL Documentation

https://dev.mysql.com/doc/

練習問題・応用問題

練習問題・応用問題について

全問にわたって、「全般的な注意」で記した

  1. カラム名に対する別名の与え方
  2. 文字列の連結の方法
  3. テーブルの別名(相関名)の指定の方法

を守ってください。

第3章 複数のテーブルを扱う

その9 INTERSECTを使う

MySQLではINTERSECTが使えませんので、EXISTS による相関副問い合わせを使います。

第1問解答例

SELECT DISTINCT
  CustomerID AS "ID"
, CustomerName AS "名前"
FROM
  Customers AS c
WHERE
  EXISTS
  (
   SELECT
     EmployeeID
   , EmployeeName
   FROM
     Employees AS e
   WHERE
     c.CustomerID = e.EmployeeID
     AND
     c.CustomerName = e.EmployeeName
  )
ORDER BY 
  ID 
;

第2問解答例

SELECT DISTINCT
  EmployeeID AS "ID"
, EmployeeName AS "名前"
FROM
  Employees AS e1
WHERE
  EXISTS
  (
   SELECT
     EmployeeID
   , EmployeeName
   FROM
     Employees AS e2
   WHERE
     e1.EmployeeID = e2.EmployeeID
     AND
     e1.EmployeeName = e2.EmployeeName
  )
ORDER BY
  ID
;

第3問解答例

SELECT DISTINCT
  ProductID 
FROM 
  Products AS p
WHERE
  EXISTS
  (
   SELECT
     ProductID
   FROM 
     Sales AS s
   WHERE
     p.ProductID = s.ProductID
  )
ORDER BY 
  ProductID 
;

第4問解答例

SELECT DISTINCT
  s1.CustomerID
, s1.ProductID
FROM
  Sales AS s1
WHERE
  SaleDate BETWEEN '2006-10-01' AND '2006-12-31'
  AND
  Quantity >= 10
  AND
  EXISTS
  (
   SELECT
     s0.CustomerID
   , s0.ProductID
   FROM  
    (
     SELECT
       s2.CustomerID
     , s2.ProductID
     FROM
       Sales s2
     WHERE
       SaleDate BETWEEN '2007-01-01' AND '2007-03-31'
       AND
       Quantity >= 10
       AND
       EXISTS
       (
        SELECT
          s3.CustomerID
        , s3.ProductID
        FROM
          Sales s3
        WHERE
          SaleDate BETWEEN '2007-04-01' AND '2007-06-30'
          AND
          Quantity >= 10
          AND
          s2.CustomerID = s3.CustomerID
          AND
          s2.ProductID = s3.ProductID
       )
    ) AS s0
   WHERE
     s1.CustomerID = s0.CustomerID
     AND
     s1.ProductID = s0.ProductID
  )
ORDER BY 
  CustomerID 
, ProductID 
;

第5問解答例

SELECT DISTINCT
  A.ProductID
FROM
  Sales AS A
    JOIN
  Customers AS B
    ON A.CustomerID = B.CustomerID
WHERE
  A.Quantity >= 10
  AND
  B.CustomerClassID = 2
  AND
  EXISTS
  (
   SELECT
     Z.ProductID
   FROM
    (
     SELECT
       A.ProductID
     FROM
       Sales AS A
         JOIN
       Customers AS B
         ON A.CustomerID = B.CustomerID
     WHERE
       A.Quantity >= 100
       AND
       B.CustomerClassID = 1
    ) AS Z
   WHERE
     A.ProductID = Z.ProductID
  )
ORDER BY
  ProductID
;

その10 EXCEPTを使う

MySQLではEXCEPTが使えませんので、NOT EXISTS による相関副問い合わせを使います。

第1問解答例

SELECT DISTINCT
  CustomerID AS "ID"
, CustomerName AS "名前"
FROM
  Customers AS c
WHERE
  NOT EXISTS
  (
   SELECT
     EmployeeID
   , EmployeeName
   FROM
     Employees AS e
   WHERE
     c.CustomerID = e.EmployeeID
     AND
     c.CustomerName = e.EmployeeName
  )    
ORDER BY 
  ID 
;

第2問解答例

SELECT DISTINCT
  EmployeeID AS "ID"
, EmployeeName AS "名前"
FROM
  Employees AS e1
WHERE
  NOT EXISTS
  (
   SELECT
     EmployeeID AS "ID"
   , EmployeeName AS "名前"
   FROM
     Employees AS e2
   WHERE
     e1.EmployeeID = e2.EmployeeID
     AND
     e1.EmployeeName = e2.EmployeeName
  )
ORDER BY
  ID
;

第3問解答例

SELECT DISTINCT
  ProductID 
FROM 
  Products AS p
WHERE
  NOT EXISTS
  (
   SELECT
     ProductID
   FROM 
     Sales AS s
   WHERE
     p.ProductID = s.ProductID
  )
ORDER BY 
  ProductID 
;

第4問解答例

SELECT DISTINCT
  A.ProductID
FROM
  Sales AS A
    JOIN
  Customers AS B
    ON A.CustomerID = B.CustomerID
WHERE
  A.Quantity >= 100
  AND
  B.CustomerClassID = 1
  AND
  NOT EXISTS
  (
   SELECT
     Z.ProductID
   FROM
    (
     SELECT
       A.ProductID
     FROM
       Sales AS A
         JOIN
       Customers AS B
         ON A.CustomerID = B.CustomerID
     WHERE
       A.Quantity >= 10
       AND
       B.CustomerClassID = 2
    ) AS Z
   WHERE
     A.ProductID = Z.ProductID
  )
ORDER BY
  ProductID
;

第5問解答例

SELECT DISTINCT
  A.ProductID
FROM
  Sales AS A
    JOIN
  Customers AS B
    ON A.CustomerID = B.CustomerID
WHERE
  A.Quantity >= 10
  AND
  B.CustomerClassID = 2
  AND
  NOT EXISTS
  (
   SELECT
     Z.ProductID
   FROM
    (
     SELECT
       A.ProductID
     FROM
       Sales AS A
         JOIN
       Customers AS B
         ON A.CustomerID = B.CustomerID
     WHERE
       A.Quantity >= 100
       AND
       B.CustomerClassID = 1
    ) AS Z
   WHERE
     A.ProductID = Z.ProductID
  )
ORDER BY
  ProductID
;

第4章 追加・更新・削除

その6 他のテーブルの値を使って更新する

第1問解答例

UPDATE
  Customers
SET
  Address
  =
  CONCAT(
   (
    SELECT
      PrefecturalName
    FROM
      Prefecturals
    WHERE
      Customers.PrefecturalID = Prefecturals.PrefecturalID
   )
  , Address )
WHERE
  EXISTS
  (
   SELECT
     'X'
   FROM
     Prefecturals
   WHERE
     Customers.PrefecturalID = Prefecturals.PrefecturalID
  )
;

第4問解答例

UPDATE 
  Products 
SET 
  ProductName 
  = 
  CONCAT(
    CONCAT( 
      CONCAT( ProductName, '(' ), 
       (
        SELECT 
          CategoryName 
        FROM 
          Categories 
        WHERE 
          Products.CategoryID = Categories.CategoryID 
      )
    )
  , ')' )
WHERE
  EXISTS
  (
   SELECT 
     'X' 
   FROM 
     Categories 
   WHERE 
     Products.CategoryID = Categories.CategoryID 
  ) 
;

第5問解答例

UPDATE 
  Products 
SET 
  ProductName 
  =
  CONCAT( 
    CONCAT(
     (
      SELECT 
        SUM( Quantity )
      FROM 
        Sales 
      WHERE 
        Products.ProductID = Sales.ProductID 
     )
    , '個も売れてるヒット商品!' )
  , ProductName ) 
WHERE 
 (
  SELECT 
    SUM( Quantity )
  FROM 
    Sales 
  WHERE 
    Products.ProductID = Sales.ProductID 
 ) >= 500
;

第5章 応用問題

標準SQLのままではエラーになる問題と解決策の例

以下の問題ではMySQLの場合、解答例の標準SQLのままではエラーになります。

それぞれの問題に対する解決策の例を記してみましたので参考にしてください。

その1 SQLをSELECT文で作成する

SELECT 
  CONCAT(
    CONCAT(
      CONCAT(
        CONCAT('INSERT INTO Pref_Back VALUES ('
        , PrefecturalID )
      , ',''' )
    , PrefecturalName )
  , ''');' ) AS "都道府県のINSERT文"
FROM 
  Prefecturals 
;

その2 月別販売額一覧の作成

SELECT
  A.YM 
, SUM( A.Amount ) "販売合計金額"
FROM
 (
  SELECT
    DATE_FORMAT( SaleDate, '%Y-%m' ) "YM"
  , s.Quantity * p.Price "Amount"
  FROM
    Sales AS s
      JOIN
    Products AS p
      ON s.ProductID = p.ProductID
 ) AS A
GROUP BY
  A.YM
ORDER BY
  A.YM
;

その3 社員別・月別販売額一覧の作成

SELECT
  e.EmployeeID
, e.EmployeeName
, x.YM
, SUM(
      CASE WHEN x.Amount IS NULL THEN 0
           ELSE x.Amount
      END
     ) AS "販売合計金額"
FROM
  Employees AS e
    LEFT OUTER JOIN
 (
  SELECT
    s.EmployeeID
  , DATE_FORMAT( SaleDate, '%Y-%m' ) AS "YM"
  , s.Quantity * p.Price AS "Amount"
  FROM
    Sales AS s
      JOIN
    Products AS p
      ON s.ProductID = p.ProductID
 ) AS x
    ON e.EmployeeID = x.EmployeeID
GROUP BY
  e.EmployeeID
, e.EmployeeName
, x.YM
ORDER BY
  e.EmployeeID
, e.EmployeeName
, x.YM
;

その4 商品別・月別販売額一覧の作成

SELECT
  p.ProductID
, p.ProductName
, s.YM
, SUM( s.Quantity * p.Price )
FROM
 (
  SELECT
    ProductID
  , DATE_FORMAT( SaleDate, '%Y-%m' ) AS "YM"
  , Quantity
  FROM
    Sales
 ) AS s
    JOIN
  Products AS p
    ON s.ProductID = p.ProductID
WHERE
  p.CategoryID IN (1, 3, 9)
GROUP BY
  p.ProductID
, p.ProductName
, s.YM
HAVING
  SUM( s.Quantity * p.Price ) > 5000
ORDER BY
  p.ProductID
, p.ProductName
, s.YM DESC
;

その7 部門別・月別平均給与一覧の作成

SELECT
  d.DepartmentID
, d.DepartmentName
, s.YM
, AVG( Amount ) AS "平均給与"
FROM
 (
  SELECT
    EmployeeID
  , DATE_FORMAT( PayDate, '%Y-%m' ) AS "YM"
  , Amount
  FROM
    Salary
  WHERE
    DATE_FORMAT( PayDate, '%Y' ) = '2007'
 ) AS s
    JOIN
  BelongTo AS b
    ON s.EmployeeID = b.EmployeeID
      JOIN
    Departments AS d
      ON b.DepartmentID = d.DepartmentID
GROUP BY
  d.DepartmentID
, d.DepartmentName
, s.YM
ORDER BY
  d.DepartmentID
, d.DepartmentName
, s.YM
;

その8 月別・カテゴリ別販売一覧の作成

SELECT
  s.YM
, SUM(
      CASE WHEN p.CategoryID = 1 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct1
, SUM(
      CASE WHEN p.CategoryID = 2 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct2
, SUM(
      CASE WHEN p.CategoryID = 3 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct3
, SUM(
      CASE WHEN p.CategoryID = 4 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct4
, SUM(
      CASE WHEN p.CategoryID = 5 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct5
, SUM(
      CASE WHEN p.CategoryID = 6 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct6
, SUM(
      CASE WHEN p.CategoryID = 7 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct7
, SUM(
      CASE WHEN p.CategoryID = 8 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct8
, SUM(
      CASE WHEN p.CategoryID = 9 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct9
, SUM(
      CASE WHEN p.CategoryID = 10 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct10
FROM
 (
  SELECT
    ProductID
  , DATE_FORMAT( SaleDate, '%Y-%m' ) AS "YM"
  , Quantity
  FROM
    Sales
 ) AS s
    JOIN
  Products AS p
    ON s.ProductID = p.ProductID
GROUP BY
  s.YM
;

その9 商品別3ヶ月販売推移表の作成

SELECT
  x.ProductID
, x.ProductName
, x.Amount06 AS "6月販売金額"
, x.Amount07 AS "7月販売金額"
, CASE WHEN x.Amount06 < x.Amount07 THEN '↑'
       WHEN x.Amount06 = x.Amount07 THEN '→'
       ELSE '↓'
  END AS "対6月増減"
, x.Amount08 AS "8月販売金額"
, CASE WHEN x.Amount07 < x.Amount08 THEN '↑'
       WHEN x.Amount07 = x.AMount08 THEN '→'
       ELSE '↓'
  END AS "対7月増減"
FROM
 (
  SELECT
    p.ProductID
  , p.ProductName
  , SUM(
        CASE WHEN s.SaleDate IS NULL THEN 0
             WHEN DATE_FORMAT( s.SaleDate, '%Y-%m' ) = '2007-06' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS "Amount06"
  , SUM(
        CASE WHEN s.Quantity IS NULL THEN 0
             WHEN DATE_FORMAT( s.SaleDate, '%Y-%m' ) = '2007-07' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS "Amount07"
  , SUM(
        CASE WHEN s.Quantity IS NULL THEN 0
             WHEN DATE_FORMAT( s.SaleDate, '%Y-%m' ) = '2007-08' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS "Amount08"
  FROM
    Products AS p
      LEFT OUTER JOIN
    Sales s
      ON p.ProductID = s.ProductID
  GROUP BY
    p.ProductID
  , p.ProductName
 ) AS x
ORDER BY
  x.ProductID
;

Oracleを使う上での全般的な注意

列に別名をつける

別名の指定方法

カラム名「ColName」に「別名」という別名をつけるには、

  1. AS を使わない方法
    ColName 別名
    ColName "別名"
    ColName '別名'不可
  2. AS を使う方法
    ColName AS 別名
    ColName AS "別名"
    ColName AS '別名'不可

となります。

文字列の連結を行う

文字列の連結には、

||
+不可
CONCAT()関数

となります。

たとえば、第2章その6の問題の場合

SELECT
  CONCAT( CustomerName, '様' ) AS "お名前"
FROM
  Customers
;

でも可となります。

CONCAT()関数は2つの文字列の連結を行います。3つ以上の文字列の連結は、CONCAT()関数を入れ子で使ってください。

 CONCAT( CONCAT( 文字列1, 文字列2 ), 文字列3 )

テーブルに別名をつける

テーブルの別名(これを相関名とも呼びます)を指定するには、

  1. AS を使う方法
    SELECT
      e.EmployeeID
    , e.EmployeeName
    FROM
      Employees AS e
    ;
    
  2. AS を使わない方法
    SELECT
      e.EmployeeID
    , e.EmployeeName
    FROM
      Employees e
    ;
    

の2通りがありますが、Oracleでは 1 は不可です。2 の方法で使ってください。

EXCEPTを使う

Oracleでは「EXCEPT」の代わりに「MINUS」を使います。

したがって第3章その10に出てくる例題の解答は、次のとおりとなります。

SELECT
  EmployeeName 
FROM
  Employees 
MINUS
SELECT
   e.EmployeeName 
FROM
  Sales s
    JOIN 
  Employees e
    ON s.EmployeeID = e.EmployeeID 
;

データの型変換に関するご注意

データの型変換は、第5章応用問題に多く出てくる日付型から文字列型への変換が典型的な例ですが各RDBMSによってその方法が異なる場合が多いのが実態です。

Oracleでは日付型の項目を、「2007-12」と変換したければ、

TO_CHAR(日付型の項目,'yyyy-mm')

2007年12月」と変換したければ、

TO_CHAR(日付型の項目,'yyyy')||'年'||TO_CHAR(日付型の項目,'mm')||'月'

とします。

他にも、数値型を文字列型に変換、逆に文字列型を数値型に変換などいろいろな変換方法があります。

また、CAST()関数によって型変換をすることもできます。

詳細はマニュアルなどで調べてみてください。

Oracle Database 12cドキュメント

http://www.oracle.com/technetwork/jp/database/enterprise-edition/documentation/

練習問題・応用問題

練習問題・応用問題について

全問にわたって、「全般的な注意」で記した

  1. カラム名に対する別名の与え方
  2. 文字列の連結の方法
  3. テーブルの別名(相関名)の指定の方法

を守ってください。

第2章 ひとつのテーブルを扱う

その14 クロス集計を行う

第3問

Oracleでは、日付型の属性の一部を取り出すのに、日付型から文字列型に変換するTO_CHAR()関数を利用します。

この練習問題では、TO_CHAR( SaleDate, 'yyyy' )で、日付型のSaleDate項目の年部分を文字列型で取り出します。

TO_CHAR( SaleDate, 'mm' )で、日付型のSaleDate項目の月部分を文字列型で取り出します。

「全般的な注意」の「データの型変換に関するご注意」も参照してください。

SELECT
  CustomerID AS 顧客ID 
, SUM( CASE
         WHEN TO_CHAR( SaleDate, 'mm' ) = '09' THEN Quantity 
         ELSE 0
       END ) AS "9月" 
, SUM( CASE
         WHEN TO_CHAR( SaleDate, 'mm' ) = '10' THEN Quantity 
         ELSE 0
       END ) AS "10月" 
, SUM( CASE
         WHEN TO_CHAR( SaleDate, 'mm' ) = '11' THEN Quantity 
         ELSE 0
       END ) AS "11月" 
FROM 
  Sales 
WHERE 
  TO_CHAR( SaleDate, 'yyyy' ) = '2006' 
GROUP BY 
  CustomerID
;

第3章 複数のテーブルを扱う

その10 EXCEPTを使う

第1問

Oracleでは「EXCEPT」の代わりに「MINUS」を使いますので、解答例としては 次のとおりとなります。以下、第5問まで同様です。

SELECT 
  CustomerID AS ID
, CustomerName AS 名前 
FROM 
  Customers 
MINUS 
SELECT 
  EmployeeID AS ID
, EmployeeName AS 名前 
FROM 
  Employees 
ORDER BY 
  ID 
;

第4章 追加・更新・削除

その6 ほかのテーブルの値を使って更新する

第1問

少し複雑な文字列の連結の場合は、「||」でなくCONCAT()関数を使うとうまくいきます。

UPDATE
  Customers
SET
  Address
  =
  CONCAT(
   (
    SELECT
      PrefecturalName
    FROM
      Prefecturals
    WHERE
      Customers.PrefecturalID = Prefecturals.PrefecturalID
   )
  , Address )
WHERE
  EXISTS
  (
   SELECT
     'X'
   FROM
     Prefecturals
   WHERE
     Customers.PrefecturalID = Prefecturals.PrefecturalID
  )
;

第5問

これも少し複雑な文字列の連結の例です。

SUM( Quantity )の結果は数値となるため文字列に変換しなければなりません。通常は暗黙の型変換といって自動的に数値を文字列に変換してくれるのですが、暗黙の型変換が行われない場合は、「||」でなくCONCAT()関数を使ってみてください。

UPDATE 
  Products 
SET 
  ProductName 
  =
  CONCAT(
   (
    SELECT 
      SUM( Quantity )
    FROM 
      Sales 
    WHERE 
      Products.ProductID = Sales.ProductID 
   )
  , '個も売れてるヒット商品!') || ProductName
WHERE 
 (
  SELECT 
    SUM( Quantity )
  FROM 
    Sales 
  WHERE 
    Products.ProductID = Sales.ProductID 
 ) >= 500
;

第5章 応用問題

標準SQLのままではエラーになる問題と解決策の例

以下の問題ではOracleの場合、解答例の標準SQLのままではエラーになります。

それぞれの問題に対する解決策の例を記してみましたので参考にしてください。

その2 月別販売額一覧の作成

SELECT
  年月
, SUM( 販売金額 ) AS 販売合計金額
FROM
 (
  SELECT
    TO_CHAR( SaleDate, 'yyyy-mm' ) AS 年月
  , s.Quantity * p.Price AS 販売金額
  FROM
    Sales s
      JOIN
    Products p
      ON s.ProductID = p.ProductID
 ) 
GROUP BY
  年月
ORDER BY
  年月
;

その3 社員別・月別販売額一覧の作成

SELECT
  e.EmployeeID
, e.EmployeeName
, 年月
, SUM(
      CASE WHEN 販売金額 IS NULL THEN 0
           ELSE 販売金額
      END
     ) AS 販売合計金額
FROM
  Employees e
    LEFT OUTER JOIN
 (
  SELECT
    s.EmployeeID
  , TO_CHAR( SaleDate, 'yyyy-mm' ) AS 年月
  , s.Quantity * p.Price AS 販売金額
  FROM
    Sales s
      JOIN
    Products p
      ON s.ProductID = p.ProductID
 ) x
    ON e.EmployeeID = x.EmployeeID
GROUP BY
  e.EmployeeID
, e.EmployeeName
, 年月
ORDER BY
  e.EmployeeID
, e.EmployeeName
, 年月
;

その4 商品別・月別販売額一覧の作成

SELECT
  p.ProductID
, p.ProductName
, 年月
, SUM( s.Quantity * p.Price )
FROM
 (
  SELECT
    ProductID
  , TO_CHAR( SaleDate, 'yyyy-mm' ) AS 年月
  , Quantity
  FROM
    Sales
 ) s
    JOIN
  Products p
    ON s.ProductID = p.ProductID
WHERE
  p.CategoryID IN (1, 3, 9)
GROUP BY
  p.ProductID
, p.ProductName
, 年月
HAVING
  SUM( s.Quantity * p.Price ) > 5000
ORDER BY
  p.ProductID
, p.ProductName
, 年月 DESC
;

その5 顧客別・商品別販売額一覧の作成

SELECT
  c.CustomerID
, c.CustomerName
, p.ProductName
, SUM( s.Quantity * p.Price ) 販売合計金額
FROM
  Sales s
    JOIN
  Products p
    ON s.ProductID = p.ProductID
      JOIN
    Customers c
      ON s.CustomerID = c.CustomerID
GROUP BY
  c.CustomerID
, c.CustomerName
, p.ProductName
ORDER BY
  c.CustomerID
, c.CustomerName
, p.ProductName
;

その6 都道府県別・商品別販売額一覧の作成

SELECT
  pr.PrefecturalID
, pr.PrefecturalName
, p.ProductName
, SUM( s.Quantity * p.Price ) 販売合計金額
FROM
  Sales s
    JOIN
  Products p
    ON s.ProductID = p.ProductID
      JOIN
   Customers c
      ON s.CustomerID = c.CustomerID
        JOIN
      Prefecturals pr
        ON c.PrefecturalID = pr.PrefecturalID
GROUP BY
  pr.PrefecturalID
, pr.PrefecturalName
, p.ProductName
ORDER BY
  pr.PrefecturalID
, pr.PrefecturalName
, p.ProductName
;

その7 部門別・月別平均給与一覧の作成

SELECT
  d.DepartmentID
, d.DepartmentName
, 年月
, AVG( Amount ) AS 平均給与
FROM
 (
  SELECT
    EmployeeID
  , TO_CHAR( PayDate, 'yyyy-mm' ) AS 年月
  , Amount
  FROM
    Salary
  WHERE
    TO_CHAR( PayDate, 'yyyy') = '2007'
 ) s
    JOIN
  BelongTo b
    ON s.EmployeeID = b.EmployeeID
      JOIN
    Departments d
      ON b.DepartmentID = d.DepartmentID
GROUP BY
  d.DepartmentID
, d.DepartmentName
, 年月
ORDER BY
  d.DepartmentID
, d.DepartmentName
, 年月
;

その8 月別・カテゴリ別販売一覧の作成

SELECT
  年月
, SUM(
      CASE WHEN p.CategoryID = 1 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct1
, SUM(
      CASE WHEN p.CategoryID = 2 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct2
, SUM(
      CASE WHEN p.CategoryID = 3 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct3
, SUM(
      CASE WHEN p.CategoryID = 4 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct4
, SUM(
      CASE WHEN p.CategoryID = 5 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct5
, SUM(
      CASE WHEN p.CategoryID = 6 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct6
, SUM(
      CASE WHEN p.CategoryID = 7 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct7
, SUM(
      CASE WHEN p.CategoryID = 8 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct8
, SUM(
      CASE WHEN p.CategoryID = 9 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct9
, SUM(
      CASE WHEN p.CategoryID = 10 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct10
FROM
 (
  SELECT
    ProductID
  , TO_CHAR( SaleDate, 'yyyy-mm' ) AS 年月
  , Quantity
  FROM
    Sales
 ) s
    JOIN
  Products p
    ON s.ProductID = p.ProductID
GROUP BY
  年月
;

その9 商品別3ヶ月販売推移表の作成

SELECT
  x.ProductID
, x.ProductName
, x."6月販売金額"
, x."7月販売金額"
, CASE WHEN x."6月販売金額" < x."7月販売金額" THEN '↑'
       WHEN x."6月販売金額" = x."7月販売金額" THEN '→'
       ELSE '↓'
  END AS 対6月増減
, x."8月販売金額"
, CASE WHEN x."7月販売金額" < x."8月販売金額" THEN '↑'
       WHEN x."7月販売金額" = x."8月販売金額" THEN '→'
       ELSE '↓'
  END AS 対7月増減
FROM
 (
  SELECT
    p.ProductID
  , p.ProductName
  , SUM(
        CASE WHEN s.SaleDate IS NULL THEN 0
             WHEN TO_CHAR( s.SaleDate, 'yyyy-mm' ) = '2007-06' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS "6月販売金額"
  , SUM(
        CASE WHEN s.Quantity IS NULL THEN 0
             WHEN TO_CHAR( s.SaleDate, 'yyyy-mm' ) = '2007-07' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS "7月販売金額"
  , SUM(
        CASE WHEN s.Quantity IS NULL THEN 0
             WHEN TO_CHAR( s.SaleDate, 'yyyy-mm' ) = '2007-08' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS "8月販売金額"
  FROM
    Products p
      LEFT OUTER JOIN
    Sales s
      ON p.ProductID = s.ProductID
  GROUP BY
    p.ProductID
  , p.ProductName
 ) x
ORDER BY
  x.ProductID
;

SQL Serverを使う上での全般的な注意

列に別名をつける

カラム名「ColName」に「別名」という別名をつけるには、

  1. AS を使わない方法
    ColName 別名
    ColName "別名"
    ColName '別名'
  2. AS を使う方法
    ColName AS 別名
    ColName AS "別名"
    ColName AS '別名'

となります。

文字列の連結を行う

文字列の連結には、

||不可
+
CONCAT() 関数不可

となります。

ですので、第2章その6の問題の場合

SELECT
  CustomerName + '様' AS "お名前"
FROM
  Customers
;

とします。

テーブルに別名をつける

テーブルの別名(これを相関名とも呼びます)を指定するには、

  1. AS を使う方法
    SELECT
      e.EmployeeID
    , e.EmployeeName
    FROM
      Employees AS e
    ;
    
  2. AS を使わない方法
    SELECT
      e.EmployeeID
    , e.EmployeeName
    FROM
      Employees e
    ;
    

の2通りがありますが、SQLServerではどちらの方法も使えます。

データの型変換に関するご注意

データの型変換は、第5章応用問題に多く出てくる日付型から文字列型への変換が典型的な例ですが各RDBMSによってその方法が異なる場合が多いのが実態です。

SQLServerでは日付型の項目を、「2007-12」と変換したければ、

CONVERT(VARCHAR, DATEPART(yyyy,日付型の項目))+'-'+CONVERT(VARCHAR, DATEPART(mm,日付型の項目))

2007年12月」と変換したければ、

CONVERT(VARCHAR, DATEPART(yyyy,日付型の項目))+'年'+CONVERT(VARCHAR, DATEPART(mm,日付型の項目))+'月'

とします。

他にも、数値型を文字列型に変換、逆に文字列型を数値型に変換などいろいろな変換方法があります。

また、CAST()関数によって型変換をすることもできます。

詳細はマニュアルなどで調べてみてください。

SQL Server 2016 ドキュメント

https://msdn.microsoft.com/ja-jp/library/dd692930.aspx

練習問題・応用問題

練習問題・応用問題について

全問にわたって、「全般的な注意」で記した

  1. カラム名に対する別名の与え方
  2. 文字列の連結の方法
  3. テーブルの別名(相関名)の指定の方法

を守ってください。

第3章 複数のテーブルを扱う

その6 相関副問い合わせを使う

第5問

標準SQLでは、GROUP BY句なしでもHAVING句を使うことができますが、SQLServerではGROUP BY句を要求してきますので、次のとおりにしてください。

SELECT 
  ProductID 
, ProductName 
FROM 
  Products AS A
WHERE 
  ProductID IN
  (
   SELECT 
     ProductID 
   FROM
     Sales AS B
   WHERE
     A.ProductID = B.ProductID 
   GROUP BY
     ProductID
   HAVING
     AVG( Quantity ) <= MAX( Quantity )/10
  )
ORDER BY 
  ProductID 
;

第4章 追加・更新・削除

その6 他のテーブルの値を使って更新する

第3問

SQLServerでは、「他のテーブルの値を使って更新する」場合に、相関副問い合わせを使うのでなく、通常の結合を使って更新することができます。次に示すのはこの例です。

UPDATE
  Products
SET
  Price = x.Amount
FROM
 (
  SELECT
    s.Quantity * p.Price As Amount
  FROM
    Products AS p
      JOIN
    Sales AS s
      ON  p.ProductID = s.ProductID
 ) AS x
;

第5問

SUM( Quantity )の結果は数値となるため文字列に変換しなければなりません。

通常は暗黙の型変換といって自動的に数値を文字列に変換してくれるのですが、暗黙の型変換が行われない場合は、CONVERT()関数を使ってみてください。

UPDATE 
  Products 
SET 
  ProductName 
  =
 (
  SELECT 
    CONVERT( VARCHAR, SUM( Quantity ))
  FROM 
    Sales 
  WHERE 
    Products.ProductID = Sales.ProductID 
 )
  + '個も売れてるヒット商品!' + ProductName
WHERE 
 (
  SELECT 
    SUM( Quantity )
  FROM 
    Sales 
  WHERE 
    Products.ProductID = Sales.ProductID 
 ) >= 500
;

第5章 応用問題

標準SQLのままではエラーになる問題と解決策の例

以下の問題ではSQLServerの場合、解答例の標準SQLのままではエラーになります。

それぞれの問題に対する解決策の例を記してみましたので参考にしてください。

その1 SQLをSELECT文で作成する

SELECT 
  'INSERT INTO Pref_Back VALUES ('
  + CAST( PrefecturalID AS VARCHAR )
  + ','''
  + PrefecturalName
  + ''');' AS 都道府県のINSERT文
FROM 
  Prefecturals 
;

その2 月別販売額一覧の作成

SELECT
  年月
, SUM( 販売金額 ) AS 販売合計金額
FROM
 (
  SELECT
    CONVERT( VARCHAR, DATEPART( yyyy, SaleDate ) ) + '-' 
  + CONVERT( VARCHAR, DATEPART( mm, SaleDate ) ) AS 年月
  , s.Quantity * p.Price AS 販売金額
  FROM
    Sales AS s
      JOIN
    Products AS p
      ON s.ProductID = p.ProductID
 ) AS x
GROUP BY
  年月
ORDER BY
  年月
;

その3 社員別・月別販売額一覧の作成

SELECT
  e.EmployeeID
, e.EmployeeName
, 年月
, SUM(
      CASE WHEN 販売金額 IS NULL THEN 0
           ELSE 販売金額
      END
     ) AS 販売合計金額
FROM
  Employees AS e
    LEFT OUTER JOIN
 (
  SELECT
    s.EmployeeID
  , CONVERT( VARCHAR, DATEPART( yyyy, SaleDate )) + '-'
  + CONVERT( VARCHAR, DATEPART( mm, SaleDate )) AS 年月
  , s.Quantity * p.Price AS 販売金額
  FROM
    Sales AS s
      JOIN
    Products AS p
      ON s.ProductID = p.ProductID
 ) AS x
    ON e.EmployeeID = x.EmployeeID
GROUP BY
  e.EmployeeID
, e.EmployeeName
, 年月
ORDER BY
  e.EmployeeID
, e.EmployeeName
, 年月
;

その4 商品別・月別販売額一覧の作成

SELECT
  p.ProductID
, p.ProductName
, 年月
, SUM( s.Quantity * p.Price )
FROM
 (
  SELECT
    ProductID
  , CONVERT(VARCHAR, DATEPART( yyyy, SaleDate )) + '-'
  + CONVERT(VARCHAR, DATEPART( mm, SaleDate )) AS 年月
  , Quantity
  FROM
    Sales
 ) AS s
    JOIN
  Products AS p
    ON s.ProductID = p.ProductID
WHERE
  p.CategoryID IN (1, 3, 9)
GROUP BY
  p.ProductID
, p.ProductName
, 年月
HAVING
  SUM( s.Quantity * p.Price ) > 5000
ORDER BY
  p.ProductID
, p.ProductName
, 年月 DESC
;

その7 部門別・月別平均給与一覧の作成

SELECT
  d.DepartmentID
, d.DepartmentName
, 年月
, AVG( Amount ) AS 平均給与
FROM
 (
  SELECT
    EmployeeID
  , CONVERT( VARCHAR, DATEPART( yyyy, PayDate )) + '-' 
  + CONVERT( VARCHAR, DATEPART( mm, PayDate )) AS 年月
  , Amount
  FROM
    Salary
  WHERE
    CONVERT( VARCHAR, DATEPART( yyyy, PayDate )) = '2007'
 ) AS s
    JOIN
  BelongTo AS b
    ON s.EmployeeID = b.EmployeeID
      JOIN
    Departments AS d
      ON b.DepartmentID = d.DepartmentID
GROUP BY
  d.DepartmentID
, d.DepartmentName
, 年月
ORDER BY
  d.DepartmentID
, d.DepartmentName
, 年月
;

その8 月別・カテゴリ別販売一覧の作成

SELECT
  年月
, SUM(
      CASE WHEN p.CategoryID = 1 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct1
, SUM(
      CASE WHEN p.CategoryID = 2 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct2
, SUM(
      CASE WHEN p.CategoryID = 3 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct3
, SUM(
      CASE WHEN p.CategoryID = 4 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct4
, SUM(
      CASE WHEN p.CategoryID = 5 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct5
, SUM(
      CASE WHEN p.CategoryID = 6 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct6
, SUM(
      CASE WHEN p.CategoryID = 7 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct7
, SUM(
      CASE WHEN p.CategoryID = 8 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct8
, SUM(
      CASE WHEN p.CategoryID = 9 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct9
, SUM(
      CASE WHEN p.CategoryID = 10 THEN s.Quantity * p.Price
           ELSE 0
      END
     ) AS Ct10
FROM
 (
  SELECT
    ProductID
  , CONVERT( VARCHAR, DATEPART(yyyy, SaleDate)) + '-'
  + CONVERT( VARCHAR, DATEPART(mm, SaleDate)) AS 年月
  , Quantity
  FROM
    Sales
 ) AS s
    JOIN
  Products AS p
    ON s.ProductID = p.ProductID
GROUP BY
  年月
;

その9 商品別3ヶ月販売推移表の作成

SELECT
  ProductID
, ProductName
, Amount06 AS "6月販売金額"
, Amount07 AS "7月販売金額"
, CASE WHEN Amount06 < Amount07 THEN '↑'
       WHEN Amount06 = Amount07 THEN '→'
       ELSE '↓'
  END AS "対6月増減"
, Amount08 AS "8月販売金額"
, CASE WHEN Amount07 < Amount08 THEN '↑'
       WHEN Amount07 = Amount08 THEN '→'
       ELSE '↓'
  END AS "対7月増減"
FROM
 (
  SELECT
    p.ProductID
  , p.ProductName
  , SUM(
        CASE WHEN s.SaleDate IS NULL THEN 0
             WHEN CONVERT( VARCHAR, DATEPART( yyyy, s.SaleDate )) + '-'
                + CONVERT( VARCHAR, DATEPART( mm, s.SaleDate )) = '2007-6' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS Amount06
  , SUM(
        CASE WHEN s.Quantity IS NULL THEN 0
             WHEN CONVERT( VARCHAR, DATEPART( yyyy, s.SaleDate )) + '-'
                + CONVERT( VARCHAR, DATEPART( mm, s.SaleDate )) = '2007-7' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS Amount07
  , SUM(
        CASE WHEN s.Quantity IS NULL THEN 0
             WHEN CONVERT( VARCHAR, DATEPART( yyyy, s.SaleDate )) + '-'
                + CONVERT( VARCHAR, DATEPART( mm, s.SaleDate )) = '2007-8' THEN s.Quantity * p.Price
             ELSE 0
        END
       ) AS Amount08
  FROM
    Products AS p
      LEFT OUTER JOIN
    Sales s
      ON p.ProductID = s.ProductID
  GROUP BY
    p.ProductID
  , p.ProductName
 ) AS x
ORDER BY
  ProductID
;