私はFEデータベースを克服したい(2/3)

前回の続きです。
前回(第1回):
https://machi11038004.hatenablog.com/entry/2020/10/19/182604
次回(第3回):
https://machi11038004.hatenablog.com/entry/2020/10/19/182908

内容は以下です。
第一回---------------------------------
1.準備運動
2.とりあえず問題を見てみる
3.クエリの基本的な文
4.アスタリスク(*)
第二回(本記事)---------------------------------
5.関数、式、述語
 5.1 SELECT句の補足
 5.2 関数
  5.2.1 COUNT関数
  5.2.2 SUM関数
  5.2.3 AVG関数
  5.2.4 MAX関数
  5.2.5 MIN関数
 5.3 CASE式
 5.4 述語
  5.4.1 LIKE述語
  5.4.2 IN述語
  5.4.3 EXISTS述語
6.集合演算、結合
 6.1 UNION
 6.2 INNER JOIN
 6.3 OUTER JOIN
7.その他キーワード
 7.1 AS
 7.2 DISTINCT
第三回---------------------------------
8.問題に挑む
9.おわりに

5.関数、CASE式、述語

先に紹介した句だけでなく、SELECT文には他にもいろんなものが含まれます。
その内に、「関数」、「CASE式」、「述語」があり、それぞれ役割が存在しますが、
いずれも戻り値を返すという働きがあります。
ですから、これらが出てきても単なる値だ、という見方をすると、
SELECT文を読むときの脳の負担が減ると思います。

なお、関数とCASE式は値を返し、述語は真理値を返します。

これらはたくさんの種類がありますが、書籍や問題でよく見かけるものを抜粋して示します。

ここでも、3節で使ったbooksテーブルを使います。

・booksテーブル

 book_id |         title          | category | price |  release
---------+------------------------+----------+-------+------------
       1 | ドラゴソボール         | 漫画     |   480 | 2000-01-01
       2 | 名探偵ドイル           | 漫画     |   600 | 2000-01-01
       3 | ジュラえもん           | 漫画     |   480 | 2000-03-01
       4 | スタジオギブリアート集 | 芸術     |  3800 | 2000-05-01
       5 | 風景水彩画集           | 芸術     |  2980 | 2000-05-01
       6 | 静物油絵集             | 芸術     |  4800 | 2000-06-01
       7 | 国内絶景写真集         | 写真集   |  5600 | 2000-07-01
       8 | ヨーロッパ写真集       | 写真集   |  3200 | 2000-08-01
       9 | 週刊少年チャンプ       | 雑誌     |   280 | 2000-09-01
      10 | ファッションkankam     | 雑誌     |   980 | 2000-10-01
      11 | 科学雑誌アイザック     | 雑誌     |  1280 | 2000-11-01
      12 | 孤独に学ぶRuby         | 技術書   |  3200 | 2000-12-01
      13 | 孤独に学ぶPython       | 技術書   |  3000 | 2000-12-01
      14 | 孤独に学ぶPHP          | 技術書   |  3500 | 2001-02-01
(14 行)


その前に、SELECT句について補足をします。

5.1 SELECT句の補足

3.1節でSELECT句の次には列名を続けると書きましたが、
実はSELECT句は単なる「値」も受け付けます。
値とは数値や文字などです。
コマンド

SELECT 1;

結果

 ?column?
----------
        1
(1 行)


コマンド

SELECT 'aaa';

結果

 ?column?
----------
 aaa
(1 行)


これらの場合、SELECTはこの値のデータ1行を返しています。
 

加えてSELECT句は、これから紹介する「関数」も受け付けます。
SELECT句は値を受け付けるわけですから、戻り値を返す関数を受け付けるのも、
すっとんきょうな話ではないでしょう。

SELECT句に関数を入れると、列名にはその関数が割り当てられ、
データ1行を返します。

例)全ての本の値段の合計を返す
※SUM(price)の部分が合計を出す「関数」です。
コマンド

SELECT SUM(price) FROM books;

結果

  sum
-------
 34180
(1 行)


SELECTに値や関数を入れると一行のデータが返ってくることがわかりましたが、
複数行返ってくることもあります。

例えばGROUP BYを使って「カテゴリごと」に算出すると、
その「カテゴリごと」のレコードの数だけデータを返します。

例)カテゴリごとの本の値段の合計を出す
コマンド

SELECT category, SUM(price) FROM books GROUP BY category;

結果

 category |  sum
----------+-------
 漫画     |  1560
 芸術     | 11580
 写真集   |  8800
 技術書   |  9700
 雑誌     |  2540
(5 行)

5.2 関数

関数は「キーワード()」の形になっています。
カッコには引数として列名を入れます。

5.2.1 COUNT関数

指定した列のレコードの数を数えます。
・全ての書籍の行数を数える
コマンド

SELECT COUNT(*) FROM books;

結果

 count
-------
    14
(1 行)

 「*(アスタリスク)」は4節で触れたとおり、全ての列を意味します。 そしてCOUNT関数はレコードの数を数える関数ですから、
booksテーブル全体の行数を数えたわけですね。
書籍は全部で14個ありましたから、その値がでています。

なお、*(アスタリスク)を引数に使える関数は、COUNT関数だけです。

・値段が1000円未満の本の行数を数える。
コマンド

SELECT COUNT(*) FROM books WHERE price < 1000;

結果

 count
-------
     5
(1 行)

WHEREの段階で1000円以上のものは行に残りません。
結果、1000円以下の本の行数を数えます。漫画3種類と雑誌2種類あわせて5です。

5.2.2 SUM関数

指定した列の全ての行の合計値を算出します。
・全ての本の値段の合計を返す。
コマンド

SELECT SUM(price) FROM books;

結果

  sum
-------
 34180
(1 行)


・カテゴリごとの本の値段の合計を表示する。
コマンド

SELECT category, SUM(price) FROM books GROUP BY category;

結果

 category |  sum
----------+-------
 漫画     |  1560
 芸術     | 11580
 写真集   |  8800
 技術書   |  9700
 雑誌     |  2540
(5 行)

5.2.3 AVG関数

指定した列の全ての行の平均を算出します。

・全ての本の値段の平均を返す。
コマンド

SELECT AVG(price) FROM books;

結果

          avg
-----------------------
 2441.4285714285714286
(1 行)


・カテゴリごとの本の値段の平均を表示する。
コマンド

SELECT category, AVG(price) FROM books GROUP BY category;

結果

 category |          avg
----------+-----------------------
 漫画     |  520.0000000000000000
 芸術     | 3860.0000000000000000
 写真集   | 4400.0000000000000000
 技術書   | 3233.3333333333333333
 雑誌     |  846.6666666666666667
(5 行)


5.2.4 MAX関数

指定した列の全ての行から最大値を返します。

・書籍の値段が一番高い値を返す
コマンド

SELECT MAX(price) FROM books;

結果

 max
------
 5600
(1 行)


・書籍のカテゴリごとに値段が一番高い値を表示する
コマンド

SELECT category, MAX(price) FROM books GROUP BY category;

結果

 category | max
----------+------
 漫画     |  600
 芸術     | 4800
 写真集   | 5600
 技術書   | 3500
 雑誌     | 1280
(5 行)


DATE型(日付)にも使えます。最も新しい日付を返します。

・書籍のカテゴリごとに発売日が新しいものを表示する コマンド

SELECT category, MAX(release) FROM books GROUP BY category;

結果

 category |    max
----------+------------
 漫画     | 2000-03-01
 芸術     | 2000-06-01
 写真集   | 2000-08-01
 技術書   | 2001-02-01
 雑誌     | 2000-11-01
(5 行)

 

5.2.5 MIN関数

指定した列の全ての行から最小値を返します。

・書籍の値段が一番安い値を返す コマンド

SELECT MIN(price) FROM books;

結果

 min
-----
 280
(1 行)


・書籍のカテゴリごとに値段が一番安い値を表示する
コマンド

SELECT category, MIN(price) FROM books GROUP BY category;

結果

 category | min
----------+------
 漫画     |  480
 芸術     | 2980
 写真集   | 3200
 技術書   | 3000
 雑誌     |  280
(5 行)


DATE型(日付)にも使えます。最も古い日付を返します。

・書籍のカテゴリごとに発売日が古いものを表示する コマンド

SELECT category, MIN(release) FROM books GROUP BY category;

結果

 category |    min
----------+------------
 漫画     | 2000-01-01
 芸術     | 2000-05-01
 写真集   | 2000-07-01
 技術書   | 2000-12-01
 雑誌     | 2000-09-01
(5 行)

5.3 CASE式

指定した列の全ての行に対し条件分岐を行い、指定した処理結果の値を返します。

CASE式は、列を指定するような箇所に記述します。
私が調べた中では、一番よく見るのがSELECT句の後です。
他には、関数の引数に使うパターンもあります。

CASE式の構文は以下です。
CASE 列名 WHEN 列名に関する条件式 THEN 条件式が該当した時の処理
     WHEN 列名に関する条件式 THEN 条件式が該当した時の処理
     WHEN 列名に関する条件式 THEN 条件式が該当した時の処理
     ...      ELSE 該当しない場合の処理
END

とりあえずサンプルいきしょう。
例として、booksテーブルのカテゴリを英語にしてみます。
コマンド

SELECT title, CASE category
                   WHEN '漫画'   THEN 'comic'  
                   WHEN '芸術'   THEN 'art'
                   WHEN '写真集' THEN 'photo' 
                   WHEN '雑誌'   THEN 'magazine' 
                   WHEN '技術書' THEN 'Technical' 
                   ELSE 'other' 
END FROM books;

結果

         title          |   case
------------------------+-----------
 ドラゴソボール         | comic
 名探偵ドイル           | comic
 ジュラえもん           | comic
 スタジオギブリアート集 | art
 風景水彩画集           | art
 静物油絵集             | art
 国内絶景写真集         | photo
 ヨーロッパ写真集       | photo
 週刊少年チャンプ       | magazine
 ファッションkankam     | magazine
 科学雑誌アイザック     | magazine
 孤独に学ぶRuby         | Technical
 孤独に学ぶPython       | Technical
 孤独に学ぶPHP          | Technical
(14 行)

 

5.4 述語

5節の冒頭で申し上げた通り、述語は真理値を返します。
私が調べた中では、WHERE句の条件式で使われます。
真理値を返すという性質を考えれば自然です。

 これまたいくつか種類がありますが、その中の一部を示します。

5.4.1 LIKE述語

いわゆる曖昧検索です。

列名に続けてLIKE '検索したい文字'と書きます。
この時、\%や_を使うことで曖昧検索ができます。
\%は「任意の0文字以上」、_は「任意の1文字」です。

・末尾に"集"がつく書籍を表示する
コマンド

 SELECT title FROM books WHERE title LIKE '%集';

結果

         title
------------------------
 スタジオギブリアート集
 風景水彩画集
 静物油絵集
 国内絶景写真集
 ヨーロッパ写真集
(5 行)

5.4.2 IN述語

IN述語で指定した値が含まれているか判断します。

列名に続けてIN(値、値、...)と記述します。

・値段が480円か、1280円か、3200円の書籍のタイトルと値段を表示する
コマンド

SELECT title, price FROM books WHERE price IN(480, 1280, 3200);

結果

       title        | price
--------------------+-------
 ドラゴソボール     |   480
 ジュラえもん       |   480
 ヨーロッパ写真集   |  3200
 科学雑誌アイザック |  1280
 孤独に学ぶRuby     |  3200
(5 行)


NOT IN述語という、IN述語の否定形もあります。
・値段が480円か、1280円か、3200円以外の書籍のタイトルと値段を表示する
コマンド

SELECT title, price FROM books WHERE price NOT IN(480, 1280, 3200);

結果

         title          | price
------------------------+-------
 名探偵ドイル           |   600
 スタジオギブリアート集 |  3800
 風景水彩画集           |  2980
 静物油絵集             |  4800
 国内絶景写真集         |  5600
 週刊少年チャンプ       |   280
 ファッションkankam     |   980
 孤独に学ぶPython       |  3000
 孤独に学ぶPHP          |  3500
(9 行)

5.4.3 EXISTS述語

EXISTS述語(イグジスツ述語)は今までの述語とは毛色が違います。
EXISTS述語も括弧を使いますが、引数に渡すのは別のSELECT文です。

さらに言うと、この述語は今調べているテーブルと関係のある別のテーブルから、
関連性を指定して、一致する行があるかどうかを返します。

言葉だけだとアレなのでサンプルを...と言いたいところですが、
今のところサンプル用のテーブルが一つしかないので、新しく作ります。

書籍の出荷情報を管理しているテーブルです。
・書籍の出荷情報を管理するshippingsテーブル

 shipping_id | book_id | number | shipping_date
-------------+---------+--------+---------------
           1 |       1 |     50 | 2005-01-01
           2 |       4 |    150 | 2005-02-10
           3 |       2 |     50 | 2005-02-10
           4 |       6 |    150 | 2005-03-15
           5 |       1 |    100 | 2005-04-01
           6 |       7 |     50 | 2005-05-15
           7 |       6 |    350 | 2005-08-30
           8 |       3 |    550 | 2005-09-05
           9 |       1 |    100 | 2005-09-10

book_id列が実際に出荷した書籍のidです。
numberは出荷数、shipping_dateは出荷日としています。

必要なものが揃ったので、サンプルを見てみます。

・出荷済みの本のタイトルを表示する コマンド

SELECT title FROM books WHERE EXISTS(SELECT * FROM shippings
                         WHERE books.book_id = shippings.book_id);

結果

         title
------------------------
 ドラゴソボール
 名探偵ドイル
 ジュラえもん
 スタジオギブリアート集
 静物油絵集
 国内絶景写真集
(6 行)

SELECT文の中に記述されるSELECT文を、「サブクエリ」といいます。
(ちょっと語弊があるのですが、試験対策程度ならこの理解で問題ないと思います。)
サブクエリで、booksテーブルのbook_idと
shippingsテーブルのbook_idが一致する行を抽出しています。

ここで、新しい表現が出てきました。
「books.book_id」と「shipping.book_id」です。

「.(ドット)」を使用すると、何のテーブルの列かを表せます。
「表名.列名」です。「の」と読むと理解しやすいです。
books.book_idは「booksテーブル"の"book_id」です。

表名は列名の重複がないなら省略して良いので今まで書かなかったのですが、
ここでbook_idが別のテーブルで同じ列名になっているので表名を付けました。

また、サブクエリのSELECTには*が続いていますが、
EXISTS述語で使うサブクエリの場合は、ここは何でも構いません。
例えば、先ほどの「出荷済みの本のタイトルを表示する」コマンドは、

SELECT title FROM books WHERE EXISTS(SELECT book_id FROM shippings
                        WHERE books.book_id = shippings.book_id);

でも、

SELECT title FROM books WHERE EXISTS(SELECT 3.1415 FROM shippings
                        WHERE books.book_id = shippings.book_id);

でも同じ結果になります。
 
これはEXISTS内では最終的に何を表示するかはどうでもよいからです。
実際、サンプルを見ればわかる通りそもそも表示されません。
EXISTSの使命はテーブルに指定した条件が一致した行があるかどうかを見ることだけです。

なにやら申し上げましたが
大事なのは、EXISTS述語で使うサブクエリのSELECT文の直後に何が来ようと、
振り回される必要はないという事です。

もう一発くらいサンプルをつくってみます。
「AND」は「かつ」という意味です。

・一度の出荷で出荷数が500を超えた書籍のタイトルを表示
コマンド

SELECT title FROM books WHERE EXISTS(SELECT * FROM shippings
                        WHERE books.book_id = shippings.book_id 
                              AND shippings.number > 500);

結果

    title
--------------
 ジュラえもん
(1 行)

6.集合演算、結合

集合演算はテーブル同士を行で足し引きすることで、
結合はテーブル同士を列で操作することです。

6.1 UNION

UNIONは集合演算のひとつで、行を足します。

booksと同じ列を持つbooks_2ndというテーブルがあったとします。
・books_2nd

 book_id |          title           | category | price |  release
---------+--------------------------+----------+-------+------------
       1 | ドラゴソボール           | 漫画     |   480 | 2000-01-01
       2 | 名探偵ドイル             | 漫画     |   600 | 2000-01-01
       3 | ジュラえもん             | 漫画     |   480 | 2000-03-01
      15 | 人間合格                 | 小説     |  3800 | 2000-05-01
      16 | チーズはいずこへ消えた? | ビジネス |  2980 | 2000-05-01
(5 行)

UNIONを使うと、booksテーブルとbooks_2ndテーブルを足したテーブルを生成できます。
SELECT文同士の間にUNIONと書きます。
この時、二つのテーブルの列の数と列の型は一致している必要があります。

・booksテーブルとbooks_2ndテーブルを足す
コマンド

SELECT * FROM books
UNION
SELECT * FROM books_2nd;

結果

 book_id |          title           | category | price |  release
---------+--------------------------+----------+-------+------------
       9 | 週刊少年チャンプ         | 雑誌     |   280 | 2000-09-01
       5 | 風景水彩画集             | 芸術     |  2980 | 2000-05-01
      13 | 孤独に学ぶPython         | 技術書   |  3000 | 2000-12-01
      12 | 孤独に学ぶRuby           | 技術書   |  3200 | 2000-12-01
      14 | 孤独に学ぶPHP            | 技術書   |  3500 | 2001-02-01
       8 | ヨーロッパ写真集         | 写真集   |  3200 | 2000-08-01
      15 | 人間合格                 | 小説     |  3800 | 2000-05-01
       4 | スタジオギブリアート集   | 芸術     |  3800 | 2000-05-01
      10 | ファッションkankam       | 雑誌     |   980 | 2000-10-01
      16 | チーズはいずこへ消えた? | ビジネス |  2980 | 2000-05-01
       6 | 静物油絵集               | 芸術     |  4800 | 2000-06-01
       1 | ドラゴソボール           | 漫画     |   480 | 2000-01-01
       3 | ジュラえもん             | 漫画     |   480 | 2000-03-01
       2 | 名探偵ドイル             | 漫画     |   600 | 2000-01-01
       7 | 国内絶景写真集           | 写真集   |  5600 | 2000-07-01
      11 | 科学雑誌アイザック       | 雑誌     |  1280 | 2000-11-01
(16 行)


ところで、よく見るとbooks_2ndにはbooksと全く同じ行が存在します。
漫画3つです。
そういった場合、UNIONは重複した行を排除します。
実際、上のテーブルには重複行はありません。

重複した行をそのまま出力したければ、UNIONの後ろにALLと書きます。
コマンド

SELECT * FROM books
UNION ALL
SELECT * FROM books_2nd;

結果

 book_id |          title           | category | price |  release
---------+--------------------------+----------+-------+------------
       1 | ドラゴソボール           | 漫画     |   480 | 2000-01-01
       2 | 名探偵ドイル             | 漫画     |   600 | 2000-01-01
       3 | ジュラえもん             | 漫画     |   480 | 2000-03-01
       4 | スタジオギブリアート集   | 芸術     |  3800 | 2000-05-01
       5 | 風景水彩画集             | 芸術     |  2980 | 2000-05-01
       6 | 静物油絵集               | 芸術     |  4800 | 2000-06-01
       7 | 国内絶景写真集           | 写真集   |  5600 | 2000-07-01
       8 | ヨーロッパ写真集         | 写真集   |  3200 | 2000-08-01
       9 | 週刊少年チャンプ         | 雑誌     |   280 | 2000-09-01
      10 | ファッションkankam       | 雑誌     |   980 | 2000-10-01
      11 | 科学雑誌アイザック       | 雑誌     |  1280 | 2000-11-01
      12 | 孤独に学ぶRuby           | 技術書   |  3200 | 2000-12-01
      13 | 孤独に学ぶPython         | 技術書   |  3000 | 2000-12-01
      14 | 孤独に学ぶPHP            | 技術書   |  3500 | 2001-02-01
       1 | ドラゴソボール           | 漫画     |   480 | 2000-01-01
       2 | 名探偵ドイル             | 漫画     |   600 | 2000-01-01
       3 | ジュラえもん             | 漫画     |   480 | 2000-03-01
      15 | 人間合格                 | 小説     |  3800 | 2000-05-01
      16 | チーズはいずこへ消えた? | ビジネス |  2980 | 2000-05-01
(19 行)

重複した漫画3つがそのまま足されています。

6.2 INNER JOIN

INNER JOINは「内部結合」を行います。列で結合します。

構文は以下です。
SELECT 列名、列名... FROM テーブル1 INNER JOIN テーブル2 ON テーブル1のキー = テーブル2のキー;

キーというのはテーブル同士を紐づけることができる列です。
5.4.3で登場したshippingsテーブルをここでも使います。

・booksとshippingsを列で連結し表示する。 コマンド

SELECT * FROM books INNER JOIN shippings ON books.book_id = shippings.book_id;

結果 f:id:machi11038004:20201018205147p:plain booksとshippingsの全ての列が含まれたテーブルが作成されます。

6.3 OUTER JOIN

OUTER JOINは「外部結合」といいます。列で連結します。

INNER JOIN(内部結合)との違いは、片方のテーブルに情報があれば、
もう片側にそれに紐づく行がなくても空欄で出します。
ちなみに、データベースでは空欄のことをNULLと言います。

今「片方」と書きましたが、その片方がどっちのテーブルかは指定する必要があります。
構文内で先に書いた方なら「LEFT」、後に書いた方なら「RIGHT」です。

構文は以下です。
SELECT * FROM テーブル1 LEFTかRIHGT OUTER JOIN テーブル2 ON テーブル1のキー = テーブル2のキー;

・出荷されていようがいまいが書籍情報(books)と出荷情報(shippings)を連結して出力する

SELECT * FROM books LEFT OUTER JOIN shippings ON books.book_id = shippings.book_id;

結果 f:id:machi11038004:20201018211031p:plain
ちなみに、今左外部結合しましたが、右外部結合するとどうなるかというと、
コマンド

SELECT * FROM books RIGHT OUTER JOIN shippings ON books.book_id = shippings.book_id;

結果 f:id:machi11038004:20201018211506p:plain 6.2で示した内部結合と同じ結果になりました。
これはshippiingテーブル(右)に有ってbooksテーブル(左)に無い行が存在しないためです。

7.その他キーワード

7.1 AS

別名を与えます。
対象 AS 別名 と書くことで別名を設定できます。

一番よく見かけるのは列名に別名を与えることです。

・列名を変えてタイトルと値段を表示する。
コマンド

SELECT title AS 題名, price AS お値段 FROM books;

結果

          題名          | お値段
------------------------+--------
 ドラゴソボール         |    480
 名探偵ドイル           |    600
 ジュラえもん           |    480
 スタジオギブリアート集 |   3800
 風景水彩画集           |   2980
 静物油絵集             |   4800
 国内絶景写真集         |   5600
 ヨーロッパ写真集       |   3200
 週刊少年チャンプ       |    280
 ファッションkankam     |    980
 科学雑誌アイザック     |   1280
 孤独に学ぶRuby         |   3200
 孤独に学ぶPython       |   3000
 孤独に学ぶPHP          |   3500
(14 行)


また、テーブルに別名を与えることも可能です。
一度別名を与えれば、続く記述は別名を使用することができます。
特にテーブルが複数出てくる場面で使用されます。

サブクエリが出てくるEXISTS述語や
集合演算、結合、などはテーブルが複数出てくるので遭遇する場面が増えるでしょう。

・出荷済みの書籍のタイトルを表示する(ASを絡めたパターン)
コマンド

SELECT title FROM books AS 書籍情報
                      WHERE EXISTS(SELECT book_id FROM shippings AS 出荷情報
                      WHERE 書籍情報.book_id = 出荷情報.book_id);

結果

         title
------------------------
 ドラゴソボール
 名探偵ドイル
 ジュラえもん
 スタジオギブリアート集
 静物油絵集
 国内絶景写真集
(6 行)


・booksとshippingsを列で連結し表示する(ASを絡めたパターン)

SELECT * FROM books AS 書籍情報 
INNER JOIN shippings AS 出荷情報 ON 書籍情報.book_id = 出荷情報.book_id;

結果
f:id:machi11038004:20201019180128p:plain

7.2 DISTINCT

重複を除いて出力します。

UNIONのくだりでも重複を除くと書いたのでごっちゃになるかも知れませんが、
あれはレコードの足し算の話で、今回はSELECT文の話です。

今までのサンプルではそういう出力をしていないので気付かないのは当然なのですが、
実はSELECT句では、GROUP BYを使うといった工夫をしないとたとえ結果が重複してもそのまま出力されます。

・カテゴリを表示する コマンド

SELECT category FROM books;

結果

 category
----------
 漫画
 漫画
 漫画
 芸術
 芸術
 芸術
 写真集
 写真集
 雑誌
 雑誌
 雑誌
 技術書
 技術書
 技術書
(14 行)


DISTINCTを使うと重複をなくせます。
使い方は、SELECT句で列名の前にDISTINCTと書きます。

・カテゴリを重複なしで表示する
コマンド

SELECT DISTINCT category FROM books;

結果

 category
----------
 漫画
 芸術
 写真集
 技術書
 雑誌
(5 行)

クエリの私なりの理解は以上です。

次回はいよいよ問題を解いていきます。
次回(第3回):
https://machi11038004.hatenablog.com/entry/2020/10/19/182908


参考文献:
キタミ式イラストIT塾 基本情報技術者 令和02年 (情報処理技術者試験)
SQL 第2版 ゼロからはじめるデータベース操作