SQLの基礎をざっと振り返り 〜備忘録〜

# はじめに

Webエンジニアとして現場へ入る前にSQLの基礎部分はざっと復習したため、業務に入ってからも振り返りすることを目的に、記事を作成します。

SQLの学習にあたっては [SQL Volt](https://sqlbolt.com/)というサイトを使って、学習しました。

自分で環境を作る必要もなく、簡単にSQLを実行できるところが非常におすすめです。
無料で使えてSQLの基礎は網羅されていると思います。

# SQLの基礎を復習

SQL Voltでは各Lessonごとに問題があり、SQLを修正し期待通りの結果が得られたら
次の問題へ進むことができます。質問文は全て英語なので、翻訳して記載します。

自分の回答内容も載せていますが、正確な答えは SQL Volt の各設問にある 
Solution というリンクをクリックすると表示されます。

各Lessonの質問と自分の答え、Lessonによっては追加で抑えておくべきポイントは記載しています。
テーブル構造はSQL Voltへアクセスしないと見えないので、試してみたくなったら、[SQL Volt](https://sqlbolt.com/)へどうぞ！!!
（すぐに復習できるように各レッスンへの直リンクを載せています）

## [Lesson1](https://sqlbolt.com/lesson/select_queries_introduction)

BasicなSELECT文の練習です。SQLの基礎の内容で以下のように実行します。

:::note info
SELECT カラム FROM テーブル名;
:::

<details><summary>Find the title of each film<br>各映画の「タイトル（Title）」を検索する。</summary>


```sql
SELECT title FROM movies;
```
</details>


<details><summary>Find the director of each film<br>各映画の「監督（Director）」を検索</summary>


```sql
SELECT director FROM movies;
```
</details>


<details><summary>Find the title and director of each film<br>各映画の「タイトル（Title）」と「監督（Director）」を検索</summary>

```sql
SELECT title, director FROM movies;
```
</details>


<details><summary>Find the title and year of each film<br>各映画の「タイトル（Title）と年（Year）」を検索</summary>


```sql
SELECT title, year FROM movies;
```
</details>

<details><summary>Find all the information about each film<br>各映画の「全ての情報」を検索</summary>

```sql
SELECT * FROM movies;
```
</details>

## [Lesson2](https://sqlbolt.com/lesson/select_queries_with_constraints)

ここでは条件つきのSELECT処理の学習を行います。WHERE AND/OR の使い方と絞り込み条件を習得します。

:::note info
SELECT カラム FROM テーブル名 WHERE 条件;
:::

| 演算子 | 条件 | サンプル |
|:-:|:-:|:-:|
| =, !=, <, <=, >, >=  |  数学と同様の条件 | id != 1 |
| BETWEEN … AND …  | ２つの数字に含まれる場合(その数値は含む) | BETWEEN 10 AND 20 |
| NOT BETWEEN … AND …  |  ２つの数字に含まれない場合(その数値は含む) | NOT BETWEEN 10 AND 20  |
| IN (…) | リストに含まれる数字がある場合 | id IN (1,3,5,7)  |
| NOT IN (…) | リストに含まれる数字がない場合 | id NOT IN (1,3,5,7)  |


<details><summary>Find the movie with a row id of 6<br>idが6の行の映画を検索</summary>


```sql
SELECT * FROM movies WHERE id = 6;
```
</details>


<details><summary>Find the movies released in the years between 2000 and 2010<br>2000年から2010年の間にリリースされた映画を検索</summary>


```sql
SELECT * FROM movies WHERE year BETWEEN 2000 AND 2010;
```
</details>


<details><summary>Find the movies not released in the years between 2000 and 2010<br>2000年から2010年の間にリリースされなかった映画を検索</summary>

```sql
SELECT * FROM movies WHERE year NOT BETWEEN 2000 AND 2010;
```
</details>


<details><summary>Find the first 5 Pixar movies and their release year<br>ピクサー映画を公開順に5つ検索</summary>

```sql
SELECT * FROM movies WHERE id <= 5;
SELECT * FROM movies WHERE id BETWEEN 1 AND 5;
```
</details>

## [Lesson3](https://sqlbolt.com/lesson/select_queries_with_constraints_pt_2)

ここでも条件つきのSELECT処理の学習を行います。

:::note warn
WHERE の条件として指定する必要があることに注意！
:::

| 演算子 | 条件 | サンプル |
|:-:|:-:|:-:|
| = | 文字列の比較(大文字/小文字は区別する) | name="Taro" |
| != or <>  | 正確な文字列の不等式比較(大文字/小文字は区別する) | name!="Taro" |
| LIKE  | LIKEに続く文字列を含むかを判定  | name LIKE "Taro" |
| NOT LIKE  | LIKEに続く文字列を含まないかを判定 | name NOT LIKE "Taro"  |
|  % | 任意の文字列の任意の場所で一致するかを判定 | name LIKE "%ar%"  |
|  _ |  任意の１文字に一致するかを判定 | name LIKE "Tar_"  |
|  IN(...) | 文字列がリスト内に存在するかを判定 | name IN("Taro", "Ichiro","Jiro")  |
|  NOT IN(...) | 文字列のリスト内に存在しないかを判定 | name NOT IN("Taro", "Ichiro","Jiro")  |

<details><summary>Find all the Toy Story movies<br>トイストーリーを全て検索</summary>

```sql
SELECT * FROM movies WHERE title LIKE "Toy Story%";
```
</details>


<details><summary>Find all the movies directed by John Lasseter<br>監督が”John Lasseter”となっている映画を全て検索</summary>

```sql
SELECT * FROM movies WHERE director = "John Lasseter";
```
</details>


<details><summary>Find all the movies (and director) not directed by John Lasseter<br>監督が”John Lasseter”ではない映画を全て検索</summary>

```sql
SELECT * FROM movies WHERE director != "John Lasseter";
```
</details>


<details><summary>Find all the WALL-* movies<br>タイトルが”WALL～”となっている映画を全て検索</summary>

```sql
SELECT * FROM movies WHERE title LIKE "WALL-%";
```
</details>

## [Lesson4](https://sqlbolt.com/lesson/filtering_sorting_query_results)

SQLのクエリ結果をフィルターしたり、ソートする方法を学習します。
ORDER BYやLIMIT、DISTINCTといったキーワードを使うことになります。

:::note warn
DISTINCT は取得するデータに対して指定する必要があります。
その他はWHERE以降に続けて指定しましょう。
:::


| 句 | 意味 | サンプル |
|:-:|:-:|:-:|
| DISTINCT | 重複の削除 |  SELECT DISTINCT title FROM 〜 |
| ORDER BY | データの並べ替え | 〜 FROM テーブル名 ORDER BY year ASC; |
| ASC | 昇順を指定  |  〜 FROM テーブル名 ORDER BY year ASC; |
| DESC | 降順を指定  |  〜 FROM テーブル名 ORDER BY year DESC; |
| LIMIT | 取得するデータ件数を指定 | FROM テーブル名 LIMIT 2; |
| OFFSET  |  取得するデータの開始位置を指定 | FROM テーブル名 LIMIT 3 OFFSET 6; |

<details><summary>List all directors of Pixar movies (alphabetically), without duplicates<br>映画監督を重複させずアルファベット順に列挙</summary>

```sql
SELECT DISTINCT director FROM movies ORDER BY director ASC;
```
</details>


<details><summary>List the last four Pixar movies released (ordered from most recent to least)<br>最新のものから順に最近公開された4本の映画を列挙</summary>


```sql
SELECT * FROM movies ORDER BY year DESC LIMIT 4;
```
</details>


<details><summary>List the first five Pixar movies sorted alphabetically<br>ピクサーの映画をアルファベット順に並べ、最初の5本を列挙</summary>


```sql
SELECT * FROM movies ORDER BY title ASC LIMIT 5;
```
</details>


<details><summary>List the next five Pixar movies sorted alphabetically<br>ピクサーの映画をアルファベット順に並べ、6本目～列挙</summary>

```sql
SELECT * FROM movies ORDER BY title ASC LIMIT 5 OFFSET 5;
```
</details>

## [Lesson5](https://sqlbolt.com/lesson/select_queries_review)

より複雑な条件指定をしてSELECT文を実行します。Lesson4までの内容の組み合わせです。

:::note info
複雑なSELECT処理を試してみましょう！
:::


<details><summary>List all the Canadian cities and their populations<br>カナダのすべての都市とその人口を列挙</summary>

```sql
SELECT city,population FROM north_american_cities WHERE country = "Canada";
```
</details>


<details><summary>Order all the cities in the United States by their latitude from north to south<br>アメリカのすべての都市を緯度を北から南に列挙</summary>

```sql
SELECT city FROM north_american_cities WHERE country = "United States" ORDER BY latitude DESC;
```
</details>


<details><summary>List all the cities west of Chicago, ordered from west to east<br>シカゴから西に位置する全ての都市を西から東に列挙</summary>


```sql
SELECT city, longitude FROM north_american_cities WHERE longitude < -87.629798 ORDER BY longitude ASC;
```
</details>


<details><summary>List the two largest cities in Mexico (by population)<br>メキシコの最も大きな都市を人口で比較し2つ列挙</summary>

```sql
SELECT city FROM north_american_cities WHERE country = "Mexico" ORDER BY population DESC Limit 2;
```
</details>

<details><summary>List the third and fourth largest cities (by population) in the United States and their population<br>3番目と4番目に大きい都市を人口で比較し、その人口を列挙</summary>

```sql
SELECT city FROM north_american_cities WHERE country = "United States" ORDER BY population DESC Limit 2 OFFSET 2;
```
</details>

## [Lesson6](https://sqlbolt.com/lesson/select_queries_with_joins)

複数のテーブルをJOINしてクエリを実行する方法を学習します。
ここで学習した内容はデータベースの正規化とJOINを利用したマルチテーブルのクエリ実行です。

:::note warn
JOINの種類については以下を確認しましょう。
:::

JOINはテーブルとテーブルを結合し、複数のテーブルからデータを取得することができます。利用するコマンドです。

| 構文 | 意味 |
|:-:|:-:|
| INNER JOIN | 結合する2つのテーブルの共通する列の値が一致する場合に、そのレコードを取得します。このJOINは、共通の値が存在しない場合には何も返しません。 |  
| OUTER JOIN | INNER JOINと異なり、共通の値が存在しない場合でも、結合する2つのテーブルから全てのレコードを取得します。このJOINには、LEFT OUTER JOINとRIGHT OUTER JOINの2つの種類があります。  |  
| LEFT JOIN | 左側のテーブルに存在する全てのレコードと、右側のテーブルと共通する値があるレコードを取得します。共通する値がない場合には、右側のテーブルのレコードはNULL値として表示されます。  |   
| RIGTH JOIN | LEFT JOINの逆で、右側のテーブルに存在する全てのレコードと、左側のテーブルと共通する値があるレコードを取得します。共通する値がない場合には、左側のテーブルのレコードはNULL値として表示されます。 |   

それぞれのサンプルのコードは以下です。
Customers テーブル と Ordersテーブルを結合し、両方のテーブルで id 列の値が一致するレコードを取得します。

```
# INNER JOIN
SELECT * FROM Customers INNER JOIN Orders
ON customers.id = orders.id;

# LEFT OUTER JOIN
SELECT * FROM customers LEFT OUTER JOIN orders
ON customers.id = orders.id;

# RIGHT OUTER JOIN
SELECT * FROM customers RIGHT OUTER JOIN orders
ON customers.id = orders.id;
```

日本語では内部結合（InnerJoin）、外部結合（OuterJoin）と呼び、使いわけられるようになる必要があります。

<details><summary>Find the domestic and international sales for each movie<br>各映画の国内売上（Domestic_sales）と海外売上（International_sales）を検索</summary>

```sql
SELECT title, domestic_sales, international_sales FROM movies JOIN Boxoffice ON movies.id = Boxoffice.movie_id;
```
</details>


<details><summary>Show the sales numbers for each movie that did better internationally rather than domestically<br>国内より海外の方が売上が良かった映画を表示</summary>

```sql
SELECT title, domestic_sales, international_sales FROM movies JOIN Boxoffice ON movies.id = Boxoffice.movie_id WHERE international_sales > domestic_sales;
```
</details>


<details><summary>List all the movies by their ratings in descending order<br>全ての映画を評価（Rating）で降順にソート</summary>

```sql
SELECT title, rating FROM movies JOIN Boxoffice ON movies.id = Boxoffice.movie_id ORDER BY rating DESC;
```
</details>

## [Lesson7](https://sqlbolt.com/lesson/select_queries_with_outer_joins)

引き続き、テーブルの結合について学習しました。

SQL voltのブラウザ上ではLEFT JOINのみがサポートされているようなので、問題自体はLEFT JOINで解いています。

<details><summary>Find the list of all buildings that have employees<br>従業員のいる建物を全て検索</summary>

```sql
SELECT DISTINCT building FROM employees;
```
</details>


<details><summary>Find the list of all buildings and their capacity<br>全ての建物と収容力を検索</summary>

```sql
SELECT DISTINCT building_name, capacity FROM buildings LEFT JOIN employees ON buildings.building_name = employees.building;
```
</details>


<details><summary>List all buildings and the distinct employee roles in each building (including empty buildings)<br>全ての建物と、各建物（空の建物を含む）の従業員の個別の役割を列挙</summary>

```sql
SELECT DISTINCT building_name, capacity ,role FROM buildings LEFT JOIN employees ON buildings.building_name = employees.building;
```
</details>


## [Lesson8](https://sqlbolt.com/lesson/select_queries_with_nulls)

データベースにおけるNULLの取り扱いについて学習しました。
データ が NULL の箇所は WHERE カラム名 IS/IS NOT NULL　で条件を指定します。

テーブルにデータが入っていない時には、NULLとして扱われるため、必要に応じて利用することが重要です。

<details><summary>Find the name and role of all employees who have not been assigned to a building<br>建物に割り当てられていないすべての従業員の名前と役割を検索</summary>

```sql
SELECT name, role FROM employees WHERE building IS NULL;
```
</details>


<details><summary>Find the names of the buildings that hold no employees<br>従業員がいない建物の名前を検索</summary>

```sql
SELECT building_name FROM buildings LEFT JOIN employees ON buildings.building_name = employees.building WHERE role IS NULL;
```
</details>


## [Lesson9](https://sqlbolt.com/lesson/select_queries_with_expressions)

ここでは、式を使用したクエリを学習します。

<details><summary>List all movies and their combined sales in millions of dollars<br>全ての映画と合計売上を100万ドル単位で表記した値を列挙</summary>

```sql
SELECT title, (domestic_sales + international_sales) / 10000 AS gross_sales_millions FROM movies JOIN boxoffice ON movies.id = boxoffice.movie_id;
```
</details>


<details><summary>List all movies and their ratings in percent<br>全ての映画と評価をパーセント表記した値を列挙</summary>

```sql
SELECT title, (rating * 10) AS rating_percent FROM movies 
JOIN boxoffice ON movies.id = boxoffice.movie_id;
```
</details>


<details><summary>List all movies that were released on even number years<br>偶数年に公開された全ての映画を列挙</summary>

```sql
SELECT title, year FROM movies WHERE year % 2 = 0;
```
</details>


## [Lesson10](https://sqlbolt.com/lesson/select_queries_with_aggregates)

集計関数を使用したクエリの学習を行います。

| 関数 | 意味 | サンプル |
|:-:|:-:|:-:|
| COUNT(*), COUNT(column) | 行数をカウントします。 | SELECT COUNT(name) 〜 |
| MIN(column) | 指定された列で最大の値を見つける。  | SELECT MAX(score) 〜 |
| MAX(column) | 指定された列で最小の値を見つける。  | SELECT MIN(score) 〜 |
| AVG(column) | 指定された列の平均値を見つける。 | SELECT AVG(score) 〜 |
| SUM(column)  | 指定された列で合計値を見つける。 | SELECT SUM(score) 〜 |

<details><summary>Find the longest time that an employee has been at the studio<br>スタジオへの勤続年数が最も長い従業員を検索</summary>

```sql
SELECT name, MAX(years_employed) FROM employees;
```
</details>


<details><summary>For each role, find the average number of years employed by employees in that role<br>各役割について役割ごとの平均勤続年数を計算</summary>

```sql
SELECT role, AVG(years_employed) as Average_years_employed 
FROM employees GROUP BY role;
```
</details>


<details><summary>Find the total number of employee years worked in each building<br>各建物の従業員の合計勤続年数を計算</summary>

```sql
SELECT building, SUM(years_employed) as sum_years_employed 
FROM employees GROUP BY building;
```
</details>

## [Lesson11](https://sqlbolt.com/lesson/select_queries_with_aggregates_pt_2)

引き続き、集計関数を使用したクエリの学習を行います。GROUP BY と HAVINGを使いこなして集計処理を行います。

| 構文 | 意味 | サンプル |
|:-:|:-:|:-:|
| GROUP BY |  指定したカラムの値で結果をグループ化 | GROUP BY カラム名 |
| HAVING |  グループ化された結果に対して条件を指定 | GROUP BY カラム名 HAVING 条件|

WHERE と HAVING は非常に似ていますが、条件を指定する対象が異なります。
WHERE は テーブルに対して条件を指定します。HAVING　はブルーぷ化された結果に対して条件を指定します。

<details><summary>Find the number of Artists in the studio (without a HAVING clause)
<br>HAVING句なしでスタジオのアーティスト数を確認</summary>

```sql
SELECT role, COUNT(*) as Number_of_artists FROM employees WHERE role = "Artist";
```
</details>


<details><summary>Find the number of Employees of each role in the studio<br>スタジオで役割毎の従業員数を検索</summary>

```sql
SELECT role, COUNT(*) FROM employees GROUP BY role;
```
</details>


<details><summary>Find the total number of years employed by all Engineers<br>エンジニアの合計勤続年数を検索</summary>

```sql
SELECT role, SUM(years_employed) AS total_employed_year FROM employees
GROUP BY role HAVING role = "Engineer";
```
</details>

## [Lesson12](https://sqlbolt.com/lesson/select_queries_order_of_execution)

クエリの実行順序について学習します。

<details><summary>Find the number of movies each director has directed<br>監督毎に担当した映画の数を検索</summary>

```sql
SELECT director, COUNT(id) as Number_of_movies_directed FROM movies 
GROUP BY director;
```
</details>


<details><summary>Find the total domestic and international sales that can be attributed to each director<br>監督毎の国内売上と海外売上の合計を検索</summary>

```sql
SELECT director, SUM(domestic_sales + international_sales) as Cumulative_sales_from_all_movies FROM movies
JOIN boxoffice ON movies.id = boxoffice.movie_id
GROUP BY director;
```
</details>

## [Lesson13](https://sqlbolt.com/lesson/inserting_rows)

いよいよINSERT文を用いてテーブルへデータを追加します。

`INSERT INTO テーブル名 VALUES(追加するデータ)` という構文で追加します。

<details><summary>Add the studio's new production, Toy Story 4 to the list of movies (you can use any director)<br>映画リストにスタジオの新作「Toy Story 4」を追加</summary>

```sql
INSERT INTO movies VALUES (4, "Toy Story 4", "director", 2023, 100);
```
</details>


<details><summary>Toy Story 4 has been released to critical acclaim! It had a rating of 8.7, and made 340 million domestically and 270 million internationally. Add the record to the BoxOffice table<br>トイストーリー4が公開され絶賛され、評価は8.7、売上は国内で3億4000万、海外では2億7000万を達成。このレコードをBoxOfficeテーブルに追加</summary>

```sql
INSERT INTO boxoffice VALUES (4, 8.7, 340000000, 270000000);
```
</details>

## [Lesson14](https://sqlbolt.com/lesson/updating_rows)

ここではUPDATE処理を行います。

`UPDATE テーブル名 SET カラム名 = 更新後のデータ WHERE 条件` という構文で更新処理行います。
WHERE以降はなしでも更新処理自体はできますが、テーブル内のデータが全て更新されちゃうので要注意です。

<details><summary>The director for A Bug's Life is incorrect, it was actually directed by John Lasseter<br>バグズ・ライフの監督が間違っているため、”John Lasseter”に更新</summary>

```sql
UPDATE movies SET director = "John Lasseter" WHERE id = 2;
```
</details>


<details><summary>The year that Toy Story 2 was released is incorrect, it was actually released in 1999<br>トイストーリー2の公開年が間違っているため、1999年に更新</summary>

```sql
UPDATE movies SET year = 1999 WHERE title = "Toy Story 2";
```
</details>


<details><summary>Both the title and director for Toy Story 8 is incorrect! The title should be "Toy Story 3" and it was directed by Lee Unkrich<br>トイストーリー8のタイトルと監督が間違っているため、タイトルを”トイストーリー3″、監督を”Lee Unkrich”に更新</summary>

```sql
UPDATE movies SET title="Toy Story 3" , director = "Lee Unkrich" WHERE title = "Toy Story 8";
```
</details>

## [Lesson15](https://sqlbolt.com/lesson/deleting_rows)

データの削除処理を行います。
`DELETE FROM テーブル名 WHERE 条件式`でデータを削除します。
WHERE がないとテーブルから全てのデータを削除することになるので注意しましょう。

<details><summary>This database is getting too big, lets remove all movies that were released before 2005.<br>2005年より前に公開されたすべての映画を削除</summary>

```sql
DELETE FROM movies WHERE year <= 2005;
```
</details>


<details><summary>Andrew Stanton has also left the studio, so please remove all movies directed by him.<br>Andrew Stantonがスタジオを去ったため、彼が監督した映画をすべて削除</summary>

```sql
DELETE FROM movies WHERE director = "Andrew Stanton";
```
</details>

## [Lesson16](https://sqlbolt.com/lesson/creating_tables)

データのINSERT〜DELETEまでを学んだところで、テーブルの作成方法を学習します。

`CREATE TABLE テーブル名(カラム名 データ型);` と記載することでテーブルを作成できます。

なお、既に存在するテーブル名を指定した場合、エラーになるため`IF NOT EXISTS`という句を追加することで
同じ名前のデータベースが存在する場合は、CREATE処理をスキップすることができます。

<details><summary>Create a new table named Database with the following columns:
– Name A string (text) describing the name of the database
– Version A number (floating point) of the latest version of this database
– Download_count An integer count of the number of times this database was downloaded
This table has no constraints.<br>次のような列を持つテーブル「Database」を作成する。
–name データベースの名前を説明する文字列（テキスト）
–version このデータベースの最新バージョンの番号（浮動小数点）
– Download_count このデータベースがダウンロードされた回数を示す整数
このテーブルには制約はありません。</summary>

```sql
CREATE TABLE Database (
    Name TEXT,
    Version FLOAT,
    Download_count INTEGER
);
```
</details>

テーブル定義の際に使用できるデータ型にはDBMSにより異なりますが、以下は基本的なものなので押さえておくと良いでしょう。

| データ型 | 意味 |
|:-:|:-:|
| INTEGER | 整数データ型  |
| BOOLEAN  | true / false または 真(1) / 偽(0) |
| FLOAT | 浮動小数点データ型(小数点第7位まで) |
| DOUBLE | 浮動小数点データ型(小数点第15位まで) |
| CHARACTER  | 固定長文字列（0から255） |
| VARCHAR  | 可変長文字列（0から65535） |
| TEXT | 文字列データを扱うデータ型 |
| DATE | 日付を扱うデータ型  |
| DATETIME |  日付と日時を扱うデータ型 |

さらに、テーブルに制約を追加することができます。
一般的にはConstraintと呼び、Primary Keyなどよく利用するものもあります。

| 制約 | 意味 |
|:-:|:-:|
| PRIMARY KEY | 主キー : データに重複がなく必ず一意になります。 |
| FOREIGN KEY | 外部キー : 親テーブルと子テーブルの２つのテーブル間のでデータの整合性を保つために設定されます。  |
| AUTOINCREMENT | 値を自動採番できるようにするものです。 |
| UNIQUE | 指定されたカラムには重複した値を格納できなくします。 |
| NOT NULL |  指定されたカラムを NULL にできなくなります。 |

テーブルの作成時にはこれらのデータ型と制約をカラムごとに指定して、テーブルを定義します。

## [Lesson17](https://sqlbolt.com/lesson/altering_tables)

ここではテーブルの変更方法を学習します。テーブルの変更時にはALTER構文を利用します。

テーブルにカラムを追加する
`ALTER TABLE テーブル名 ADD カラム名 データ型 (Option）制約 DEFAULT 値;`

テーブルからカラムを削除する。
`ALTER TABLE テーブル名 DROP カラム名 データ型 (Option）制約 DEFAULT 値;`

テーブルの名称を変更する。
`ALTER TABLE テーブル名 RENAME TO 新しいテーブル名;`のように使用します。

<details><summary>Add a column named Aspect_ratio with a FLOAT data type to store the aspect-ratio each movie was released in.<br>各映画が公開されたときのアスペクト比を格納するために、データ型がFLOATの”Aspect_ratio”という名前の列を追加</summary>

```sql
ALTER TABLE Movies ADD COLUMN Aspect_ratio FLOAT DEFAULT 2.39;
```
</details>


<details><summary>Add another column named Language with a TEXT data type to store the language that the movie was released in. Ensure that the default for this language is English.<br>各映画が公開されたときの言語を格納するために、データ型がTEXTの”Language”という名前の別の列とを追加。このときデフォルト値はEnglish</summary>

```sql
ALTER TABLE Movies ADD COLUMN Aspect_ratio FLOAT DEFAULT 2.39;
```
</details>


## [Lesson18](https://sqlbolt.com/lesson/dropping_tables)

最後にテーブルの削除を行う処理を学びます。

`DROP TABLE IF EXISTS  テーブル名;`という形で指定します。

<details><summary>We've sadly reached the end of our lessons, lets clean up by removing the Movies table<br>Moviesテーブルを削除</summary>

```sql
DROP TABLE IF EXISTS movies;
DROP TABLE movies;
```
</details>

<details><summary>And drop the BoxOffice table as well<br>BoxOfficeテーブルも削除</summary>

```sql
DROP TABLE IF EXISTS boxoffice;
DROP TABLE boxoffice;
```
</details>

# データベース設計のための基礎知識

データベースとは、大量の情報を保存できて、効率良くアクセスできる仕組みを備えたデータの集まりのこと。

データベース設計とは、データベースに保持するデータに関する設計のことで、システムの拡張性や運用のしやすさなどが大きく変わる。

DBの種類(RDB / NoSQL / DocumentDBなど)によっても適切ねデータベース設計は異なることに注意する必要がある。

## 3層スキーマ

データベースは3層のスキーマ(枠組み)からなります。

1. 外部スキーマ 
   ユーザーから見たデータベースのビュー

2. 概念スキーマ
   開発者から見たデータベースのテーブル

3. 内部スキーマ
   DBMSから見たデータの物理的配置（データファイル）

```markdown
図を追加予定
```

なお、外部スキーマと内部スキーマは直接的に関係を持ちません。
概念スキーマの存在が、人間の理解しやすい形(テーブル)に変換してくれています。

概念スキーマは、データの独立性を担保するために重要な役割を果たしています。
なぜなら、データファイルの物理的な配置は人間が非常に理解し難いものだからです。

なお、ここでの独立性とは、「外部スキーマを変更しても、内部スキーマに影響しない」と「内部スキーマを変更しても、外部スキーマに影響しない」の２つの状態が成立していることを指します。

データベースの設計という文脈では「論理設計」と「物理設計」の２つの観点で行われることが一般的です。

論理設計とは、概念スキーマ、つまり、データベースのテーブルの設計ということができます。
物理設計とは、内部スキーマ、つまり、DDLによる実装やストレージの構成などの設計を指します。

概念スキーマを変更すると外部スキーマにも内部スキーマにも影響が出るため、論理設計が重要になります。

## 論理設計のステップ

論理設計は以下の４ステップで行われます。

①エンティティの抽出
どんなデータを管理するエンティティ(テーブル)が必要かを明確にする

②エンティティの定義
エンティティ（テーブル）ごとにどんな属性(カラム)がいるかを明確にする

③正規化
テーブルを分割することでデータの冗長性(ムダ・重複)をなくす

④ER図の作成
エンティティ（テーブル間）の関係を視覚的に分かりやすく作図する

### ①②エンティティ関連のステップについて

エンティティとは、システムにおいて管理する必要があるデータのことを指します。
RDBにおいては「テーブル」がエンティティと捉えてみます。

エンティティの抽出では、実現したいシステムでどんなエンティティを管理する必要があるのか？を洗い出すプロセスになります。
開発プロセスにおける要件定義と被る領域です。

エンティティの定義では、各エンティティがどのような属性(データ)を保持するかを決めるプロセスになります。
RDBにおいては、各テーブルがどのような「列」を守るのかを決定することと定義できます。
中でも、どの列にkeyを設定するのか？は非常に重要です。

テーブルの構成要素は以下の３つ

①行と列
行(レコード)と列(カラム)

②キー
ある特定のデータを引き出すための鍵となる列

主キー(Primary Key)
その値を指定すれば必ず1行のレコードを特定できるような列。テーブルには必ず１つ存在する必要がある。
複数列を指定して、主キーとすることもできる。

外部キー(Foreign Key)
２つのテーブル間の列同士の関連性を設定するもの。
外部キーを登録することにより、存在しないデータを登録させないよう制限できる。

③制約
テーブルに対して制約をつけることができる。

NOT NULL制約
NULLを禁止する制約

参照整合性制約
あるテーブルのカラムに存在する値が、他のテーブルのカラムに存在する値に依存する場合に用いられます。

### ③正規化のステップについて

正規化とは、システムの利用がスムーズに行えるようにエンティティを整理するプロセスのことです。
RDBにおいては、テーブルを正規形に整えることを指します。

正規形とは、データベースにおいて保持するデータの重複が排除されたデータ形式のことです。
データの重複があると無駄なデータ領域を保持する＋無駄な更新作業が必要なことが問題になります。

#### 第1正規形

１つのフィールドには１つの値しか含まないようにすること。
複数の値が入ると、主キーが各列の値を一意に定められなくなり、主キーの原則に反します。

以下のようなテーブルは１つのフィールドに複数の値を含んでおり、主キーの原則に反する

(仮名) 購入テーブル
| 顧客名 | 購入商品 |
|:-:|:-:|
| 田中 | テレビ, DVD, スピーカー |
| 山田 | 冷蔵庫, 洗濯機 |

第１正規形として、行と列に分ける方法もありますが、、、、
行を追加すると、主キーが一意に値を定められず、列を追加すると大量のNULLのフィールドが発生する可能性があります。

よって、第１正規形を実現するためには、テーブルを追加して分割するが一般的な手順になります。

#### 第2正規形・第3正規形

第２正規形 : 部分関数従属が解消され、完全関数従属のみのテーブルにすることで、主キーの対象となる列が複数ある場合にのみ発生する。

そもそも「関数従属」とは、y=f(x) のように xの値を１つに決めれば、y の値が１つに決まる関係性を持つことを言う
`{x}={y}` のように示し、RDBにおいて正規化とはテーブルのすべての列が関数重属性を満たし、
`{主キー}={カラム}`が全てにおいて成立する状態を指します。

以下は第2正規形を満たしていないテーブルの例です。

注文情報テーブル

| 注文ID | 顧客名 | 商品名 | 単価 | 数量 |			
|:-:|:-:|:-:|:-:|:-:|
| 1 | 山田 太郎 | テレビ	| 50000	|1|
| 1	| 山田 太郎 | DVDプレーヤー	| 20000	|2|
| 2	| 田中 次郎 | 冷蔵庫 | 80000 | 1|
| 3	| 鈴木 三郎 | 洗濯機 | 60000 | 1|

このテーブルは、注文情報を格納するためのものですが、顧客名と商品名が重複しているため、
顧客名と商品名の組み合わせに対して、異なる単価と数量が格納される可能性があり、第2正規形を満たしていません。

第3正規形 : 推移的関数従属が解消されているテーブルにすること

以下は第3正規形を満たしていないテーブルの例です。

学生情報テーブル

| 学籍番号 | 学生名 | 性別 | 学科名 | 教員名 | 教員所属 |
|:-:|:-:|:-:|:-:|:-:|:-:|
| 101 | 山田 太郎 | 男性 | 数学科目 | 鈴木 一郎 | 数学科 |
| 101 | 山田 太郎 | 男性 | 物理学科目 |	田中 次郎 | 物理学科 |
| 102 | 田中 次郎 | 男性 | 数学科目 | 鈴木 一郎 | 数学科 |
| 102 | 田中 次郎 | 男性 | 化学科目 | 佐藤 三郎 | 化学科 |

このテーブルは、学生の学籍番号、学生名、性別、学科名、教員名、教員所属を格納するためのものです。
しかし、教員名と教員所属は、学科名に関係がある情報であり、学科名に依存しているため、第3正規形を満たしていません。

### ④ER図の書き方について

ER図とは、複数のテーブルの関係性(リレーション)を可視化する際に使用します。

リレーションとは、異なるテーブルが同じ意味のカラムを持つ場合に発生します。

リレーションは2種類あり、1:N のリレーション(Nは0以上を意味する)と1:1のリレーションがあります。
なお、正常にリレーションされていれば、ほとんどのテーブルは1:Nになります。

1:1のリレーションは、そもそも１つのテーブルとして表現できるので、
適切な論理設計を行なっていれば、1:1のリレーションは発生することはありません。

なお、ER図を書くときは、schema spyなどER図自動作成ツールを使うことが多いですが、
チーム内でホワイトボードなどを用いて、さっと記載したりすることもあるので書き方を抑えておくのは重要です。

基本的には、以下の3ステップでER図を作成します。

#### ①エンティティ(テーブル)を記入する
#### ②カラム(属性)を記入する
#### ③リレーションを記入する

なお、エンティティ間の関連性を表現する際には、1以上の多か0以上の多のどちらかによって、
ビジネスルールとして表現する内容が異なることに注意する必要があります。

(なんでもかんでも0以上の多とかにすると、かえって混乱してしまう場合がある)

# まとめ

SQL Volt の基礎部分では、ここまでの内容になっています。

他にも、サブクエリ や UNION INTERSECTION EXCEPTなど抑えておくべきポイントはありますが、SQLの基礎としては一旦ここまでの内容にします（追加で学習したら、まとめます）。

今後は「達人に学ぶSQL徹底指南書 第2版 初級者で終わりたくないあなたへ」や
「SQLアンチパターン」などを読み進めて、さらにDBの取り扱いに関して習得していきたいと思います。