SQLお役立ち記事まとめや勉強記録

### SQL整形ツール

https://rakko.tools/tools/37/

インデント、カラムの小文字化など設定できて便利に綺麗に描ける


### SQL攻略について
[SQL攻略](http://sql.main.jp/)
⇨SQL実践もできるし、正規化についてわかりやすい


##### ・第一正規化とは
一つのセルに複数の項目が入っている箇所（繰り返し項目）を別行にわけてそれぞれの主キーやそれに関数従属する項目をそれぞれいれてあげること
手順は繰り返し項目と主キーをまず別表にする

##### ・主キーとは
例えば伝票番号のようにそれがあれば付随する項目がわかる顕著なもの
y=2xのようにxがきまればyが決まる。そんな関係

##### ・候補キーとは
レコードを特定できる項目のこと
その中で中心的なキーを主キーという

##### ・関数従属とは
y=2xのyのようなもの。主キーがあったら自ずと決まるもの

##### ・部分関数従属とは
主キー以外の項目に関数従属している関係の項目

そこで
##### ・第二正規化とは
第一正規化から部分関数従属している項目を別のテーブルに切り離すこと
なにがメリットなのかというと例えばある商品の単価を変更したくなった時に第一正規化のままだと更新する箇所が多いので無駄が多い。商品テーブル（商品コード、商品名、単価）のように第二正規化を行なっておくと更新する箇所が少ないのでコスパが高くなる。


##### ・第三正規化とは
難しくいうと推移的関数従属を排除して別のテーブルに切り離すこと。
簡単にいうとAであればBがきまり、Bが決まればCがきまるという状況になっているテーブルをもう一度切り離すこと
ただし推移的関数従属が第二正規化で無くなる時もあるのでそのときは第三正規形まで完了する

### SQLクライアントツールについて
A5:SQL Mk-2, DBeaverといった無料で使えるソフトがあり現場ではそれを使う。
様々なDBMS（データベースマネジメントシステム）MySQL,Oracle,PostgreSQLといったデータベースが変わっても一括で利用できる。


### DBeaverについていろいろ

[モダンなSQLクライアントソフト「DBeaver」の紹介
](https://zenn.dev/datsukan/articles/d1d5aa1148e3b4)
[DBeaverとは？DBeaverの特徴と環境構築・導入方法まとめmac
](https://zenn.dev/aiq_dev/articles/2629b53f1298bc)

https://qiita.com/12345/items/48f6856e32fd618ea307

↑こちらの使い方メモが初めて触るのにわかりやすかった


そもそもMySQLやPostgresなどを使おうにもそれがmacにはいってなくてconnected Refusedとされ弾かれてしまったのでMySQLの導入方法なども勉強した

https://prog-8.com/docs/mysql-env

#### mysql_secure_installationでセキュリティ改善する
セキュリティ面の設定はこちらが役に立った

https://blog.mothule.com/db/mysql/db-mysql-install-mysql8-on-centos8

https://weblabo.oscasierra.net/mysql-57-init-setup/

#### DBeaverでローカルのsqlファイルを読みこむ
![スクリーンショット 2023-11-24 12.36.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627182/1b24ffc3-e851-176e-0002-83308c501ecf.png)

#### テーブルの作り方

https://kotetsu1701.com/note/getting-started-with-dbeaver-ce_2/


## SQL構文集
##### テータベース、テーブルの作成、定義変更
```sql:
--データベース一覧を見る（MySQLの場合）
SHOW DATABASES;

--データベースの作成
CREATE DATABASE my_db;

--DBの削除
DROP DATABASE my_db;

--使うDBの選択
USE my_db;

--選択しているDBをみる
SELECT DATABASE();


/* テーブル操作 */
--テーブル作成
CREATE TABLE users(
	id INT PRIMARY KEY,　--主キーの設定
	name VARCHAR(10),
	age INT,
	phone_number CHAR(13),
	message TEXT
)
--テーブル一覧を確認する
SHOW TABLES;

--テーブルの定義を確認する
DESCRIBE users;

--テーブルを削除する
DROP TABLE users;



/* テーブルの変更系*/
--テーブル名の変更
ALTER TABLE users RENAME TO users_table;

--カラムを削除
ALTER TABLE users_table DROP COLUMN message;

--カラムを追加（テーブルの末尾に追加）
ALTER TABLE users_table
ADD  post_code CHAR(8);

--カラムを追加（ageカラムの後に追加）
ALTER TABLE users_table
ADD gender CHAR(1) AFTER age;

--カラムを追加(カラム先頭に追加)
ALTER TABLE users_table
ADD new_id INT FIRST;

-- カラムの定義変更（実務で多く使う）
ALTER TABLE users_table
MODIFY name VARCHAR(50);

--カラム名の変更
ALTER TABLE users_table
CHANGE COLUMN name 名前 VARCHAR(50); -- 実際には日本語名はよくない

--カラムの位置変更(post_codeの後に変更)
ALTER TABLE users_table
CHANGE COLUMN gender gender CHAR(1) AFTER post_code;

--主キーの削除
ALTER TABLE users_table
DROP PRIMARY KEY;
```

##### テーブルCRUD操作の基礎
```sql
-- データを挿入する
INSERT INTO people VALUES(1,"name","2001-01-01");

-- カラムを指定して挿入する
INSERT INTO people(id,name) VALUES(2,"Jiro");

-- シングルクォートをいれたいとき２つ重ねる
INSERT INTO people VALUES(5,'John''s son','2021-01-01');

-- テーブルを全て見る
SELECT * FROM people;

-- 一部のカラムだけ見る
SELECT id,birth_day FROM people;

-- カラムの名前を変えてみる
SELECT id AS '番号',name AS '名前' FROM people;

-- 条件を指定して見る
SELECT * FROM people WHERE id=1;
SELECT * FROM people WHERE id>3;

-- 更新（全レコード）
UPDATE people SET birth_day='1900-01-01',name="";

-- 更新（条件を指定して）
UPDATE people SET name='Taro',birth_day='2000-01-01' WHERE id=3;
UPDATE people SET name='Jiro',birth_day='2001-01-01' WHERE id>3;

--　削除（条件を指定して）
DELETE FROM people WHERE id=2;
DELETE FROM people WHERE id>4;

--　削除（全レコード）
DELETE FROM people;
TRUNCATE people;
/* TRNCATEとDELETEの違い
TRNCATEはデータの完全削除なのでロールバックできない
高速で処理
ハイウォーターマークといわれDELETEは削除してもディスク容量としては残っている状態。それをTRUNCATEは完全削除する
*/


/* LIMIT ORDER BY DISTINCT */
-- 並び替え
SELECT * FROM people ORDER BY birth_day;

-- 並び替え（age優先でnameが次の条件）
SELECT * FROM people ORDER BY age,name;
-- 並び替え(ageとnameに降順)
SELECT * FROM people ORDER BY age DESC,name DESC;

-- 並び替え（条件指定）
SELECT * FROM people WHERE id>3 ORDER BY age DESC;

-- 一意に取り出す（重複を削除）
SELECT DISTINCT birth_day FROM people;
SELECT DISTINCT birth_day FROM people ORDER BY birth_day DESC;

-- 一意に取り出す条件を２つ設定
SELECT DISTINCT name,birth_day FROM people

-- 取り出す行数を指定
SELECT * FROM people LIMIT 3;

-- 最初の2行は飛ばして3行表示
SELECT * FROM people LIMIT 2,3;
SELECT * FROM people LIMIT 3 OFFSET 2;
```
##### WHEREを用いた絞り込み
```sql
SELECT * FROM users WHERE birth_place='日本';
UPDATE users SET name='奥山 成美' WHERE id =1;
```

##### NULL,BETWEEN,LIKEを用いた絞り込み
```sql
-- NULLを取り出す
SELECT * FROM customers WHERE name IS NULL;

-- NULL以外を取り出す
SELECT * FROM customers WHERE name IS NOT NULL;

-- 空白とNULLは別物
SELECT * FROM prefectures WHERE name IS NULL;
SELECT * FROM prefectures WHERE name =''; -- こちらは空白の値を取り出している
-- NULLについては廃止すべきとの意見がでてきているため使わない方がいいとのこと

-- BETWEEN(以上〜以下)
SELECT * FROM users WHERE age BETWEEN 5 AND 10;

-- NOT BETWEEN(〜未満、より大きい〜)
SELECT * FROM users WHERE age NOT BETWEEN 5 AND 10;

-- LIKE(一致を見つける)
SELECT * FROM users WHERE name LIKE '村%'; -- 村からはじまる
SELECT * FROM users WHERE name LIKE '%郎'; -- 郎でおわる
SELECT * FROM users WHERE name LIKE '%ab%'; -- abを含む
SELECT * FROM prefectures WHERE name LIKE "福_県"; -- _は任意の一文字
```

##### IN,ANY,ALL,AND,OR,NOTを用いた絞り込み

:::note warn
ただしALLとINを使う場合は　NULL の存在に気を付ける（後述）
:::

```sql
-- IN(IN内の条件で検索)
SELECT * FROM users WHERE age IN (12,24,36);
-- NOT IN
SELECT * FROM users WHERE birth_place NOT IN ('France','Germany','Itary');
-- IN（別テーブルの条件をSQLで絞り込んでさらに絞り込む）
SELECT * FROM customers WHERE id IN(SELECT customer_id FROM receipts);


-- ALL(employeesテーブルのsalaryが500万以上の人の年齢全てより(最小45)年齢が高い人をusersテーブルから抽出)
SELECT * FROM users WHERE age > ALL(SELECT age FROM employees WHERE salary >= 5000000);
-- ANY(employeesテーブルのsalaryが500万以上の人の年齢のいずれかより(最小23)年齢が高い人をusersテーブルから抽出)
SELECT * FROM users WHERE age > ANY(SELECT age FROM employees WHERE salary >= 5000000);


-- AND(「departmentが営業部」かつ「名前に田を含む」かつ「35歳未満」)
SELECT * FROM employees WHERE department =' 営業部 ' AND name LIKE '%田%' AND age <35;
-- OR(departmentが「営業部」もしくは「開発部」) INでも同じことがかけてスマート
SELECT * FROM employees WHERE department = ' 営業部 ' OR department = ' 開発部 ';
SELECT * FROM employees WHERE department IN(' 営業部 ',' 開発部 ');
-- ANDとORの複合()をつけると良い
SELECT * FROM employees WHERE department =' 営業部 ' AND (name LIKE '%田%' OR name LIKE '%西%') AND age <35;

-- NOT(直後の否定)
SELECT * FROM employees WHERE NOT department =' 営業部 '
```

##### NULLの扱いについて
:::note
基本的にはテーブルにNULLをいれないのがいいとおもうが、NULLがまだまだ入ってるプロジェクトもあるので要注意。NULLの影響で意図しないSQLが取り出される可能性がある。
基本的にはALLやINを使う場合に、NULLをケアして　IS　NULL や　IS NOT　NULLを外だしする
```sql
-- NULLの取り出し方はIS NULLで
SELECT * FROM customers WHERE name IS NULL;

-- 間違った取り出し方(NULL=NULLでは真にならないのでnameがNULLの者はとりだせない)
SELECT * FROM customers WHERE name IN("河野 文典","稲田 季雄",NULL);
-- なのでするとすればこう→　IN + NULL
SELECT * FROM customers WHERE name IN("河野 文典","稲田 季雄") OR name IS NULL;

-- NOT INの場合
SELECT * FROM customers WHERE name NOT IN("河野 文典","稲田 季雄",NULL);
/*この場合何も取り出せない。どういうことかというとINの中身をいずれも満たさないものを
 取り出すという。あんまり理解できていない*/
 -- 書き換えるとすればこう
 SELECT * FROM customers WHERE name NOT IN("河野 文典","稲田 季雄") AND name IS NOT NULL;
 
-- ALL(ALLはひとつでもNULLがはいってるとすべての条件を満たせなくなってしまうのでなにも返せなくなる)
SELECT * FROM users WHERE birth_day <=ALL(SELECT birth_day FROM customers WHERE id<10);
-- なのでNULLは弾く
SELECT * FROM users WHERE birth_day <=ALL(SELECT birth_day FROM customers WHERE id<10 AND birth_day IS NOT NULL);
```
:::

##### 算術演算子(+,-,/,*,%,CONCAT,NOW,CURDATE,DATE_FORMAT)
```sql
-- 算術演算子
SELECT 1+1;
-- 取り出した値に足し算したりもできる（ASはエイリアス）
SELECT name,age,age+3 AS age_3 FROM users LIMIT 10;

SELECT 10-5;

SELECT birth_day,birth_day+3 FROM users;

SELECT 3*5;

SELECT * FROM employees;
SELECT department,name,salary,salary*1.1 AS saraly_next_year FROM employees;

-- CONCAT(文字列の連結)
SELECT CONCAT(department,": ",name) AS '部署名と名前' FROM employees;

-- NOW,CURDATE,DATE_FORMAT
SELECT NOW(); -- 現在時刻の表示
SELECT NOW(),name,age FROM users;

SELECT CURDATE(),name,age FROM users; -- 日付

-- DATEFORMATは好きな時刻日付を好きなように表せる
SELECT DATE_FORMAT(NOW(),"%Y/%m/%d");  -- >>2023/11/27
```
%Yなどの表記については下記が参考になる

https://www.javadrive.jp/mysql/function/index49.html

##### 文字列関数(LENGTH,CHAR_LENGTH,TRIM,REPLACE,UPPER,LOWER,SUBSTR,REVERSE)
```sql
-- LENGTH,CHAR_LENGTH
-- ローマ字は１文字１バイト、日本語は１文字３バイトとなる
SELECT LENGTH("ABC"); -- 3バイト
SELECT LENGTH("あいう"); -- 9バイト

-- CHAR_LENGTHは文字の長さ
SELECT CHAR_LENGTH('ABC'); -- 3文字
SELECT CHAR_LENGTH('あいう'); -- 3文字

-- TRIM,LTRIM,RTRIM 空白削除
SELECT LTRIM("   ABC   "); -- 左の空白削除
SELECT RTRIM("   ABC   "); -- 右の空白削除
SELECT TRIM("   ABC   "); -- 両脇の空白削除

-- TRIMの応用的な使い方(空白が入ってるnameを摘出)
SELECT * FROM employees WHERE CHAR_LENGTH(name) <> CHAR_LENGTH(TRIM(name)) ;
-- それをUPDATEしてみる
UPDATE employees SET name =TRIM(name) WHERE CHAR_LENGTH(name)<> CHAR_LENGTH(TRIM(name));

-- LENGTH,CHAR_LENGTH
-- ローマ字は１文字１バイト、日本語は１文字３バイトとなる
SELECT LENGTH("ABC"); -- 3バイト
SELECT LENGTH("あいう"); -- 9バイト

SELECT name, LENGTH(name) FROM users;

-- CHAR_LENGTHは文字の長さ
SELECT CHAR_LENGTH('ABC'); -- 3文字
SELECT CHAR_LENGTH('あいう'); -- 3文字

-- TRIM,LTRIM,RTRIM 空白削除
SELECT LTRIM("   ABC   "); -- 左の空白削除
SELECT RTRIM("   ABC   "); -- 右の空白削除
SELECT TRIM("   ABC   "); -- 両脇の空白削除

-- TRIMの応用的な使い方(空白が入ってるnameを摘出)
SELECT * FROM employees WHERE CHAR_LENGTH(name) <> CHAR_LENGTH(TRIM(name)) ;
-- それをUPDATEしてみる
UPDATE employees SET name =TRIM(name) WHERE CHAR_LENGTH(name)<> CHAR_LENGTH(TRIM(name));


-- REPLACE:置換
SELECT REPLACE ("I like apple","apple","lemon");

SELECT * FROM users WHERE name LIKE "Mrs%";
-- nameにMrsがついている人をMsに変えて取り出す
SELECT REPLACE(name,"Mrs","Ms") FROM users WHERE name LIKE "Mrs%";
-- UPDATEしたい場合
UPDATE users SET name = REPLACE(name,"Mrs","Ms") WHERE name LIKE "Mrs%";
SELECT * FROM users WHERE name LIKE "Ms%";

-- UPPER LOWER(大文字、小文字)
SELECT UPPER('apple');
SELECT LOWER("APPLE");
SELECT UPPER(name),LOWER(name),name FROM users;

-- SUBSTRING （文字列の一部取り出し)
SELECT name,SUBSTRING(name,2,3) FROM employees; -- 2文字目から3文字分取り出す
SELECT * FROM employees WHERE SUBSTR(name,2,1) ='田'; -- 2文字目が'田'の人を取り出す

-- REVERSE
SELECT REVERSE(name) FROM employees;

```

##### 数学関数(ROUND,FLOOR,CEILING,RAND,POWER,COALESCE)
```sql
-- ROUND（四捨五入する。,1は少数第１を残して第２を四捨五入する）
SELECT ROUND(3.14); -- 3
SELECT ROUND(3.14,1); -- 3.1
SELECT ROUND(13.14,-1); -- 10

-- FLOOR （切り捨て）
SELECT FLOOR(3.14); -- 3

-- CEILING(切り上げ)
SELECT CEILING(3.14); -- 4

-- RAND
SELECT RAND(); -- 0<x<1 0と1は含まない
SELECT FLOOR(RAND()*10); -- 0から10の整数値

-- POWER(べき乗)
SELECT POWER(3,4); -- 81 3の4乗

-- BMIの算出
SELECT weight/POWER(height/100,2) AS BMI FROM students;

-- COALESCE(コーアレス：最初に登場するNULLでない値を返す関数)
-- 使い所：国語、英語、数学などの項目があって生徒はどれか一つしか受けていない時など他のカラムはNULLになっているので受けた一つの点数を取り出したい時など
SELECT * FROM tests_score;
SELECT COALESCE (NULL,NULL,NULL,'A',NULL,"B"); -- A
SELECT  COALESCE (test_score_1,test_score_2,test_score_3) AS score,test_score_1,test_score_2,test_score_3 FROM tests_score;
```

##### IF文
```sql
-- IF(条件式、真の値、偽の値)
SELECT IF(10<20,'真','偽'); -- 真
SELECT *,IF(birth_place="日本",'日本人','その他') AS '国籍' FROM users;
SELECT name,age,IF(age<20,'未成年','成人') FROM users;

-- IF(ANDと併用)
SELECT *,IF(height>170 AND class_no=6,'彼氏候補','その他') FROM students;
-- IF(LIKEと併用)
SELECT name,IF(name LIKE "%田%",'田を含む','その他') AS name_check FROM users;
```

##### CASE文(IFの上位互換)
CASEはSQLではIFより存在感が大きい印象
IFと違い、複数の条件をチェックする時
```sql
SELECT
    CASE (〇〇としてもいい)
        WHEN 評価式1　THEN 評価式1が真の時に返す値
        （WHEN 評価式2　THEN 評価式2が真の時に返す値)
        ELSE デフォルト値
    END　（AS 〇〇）
FROM テーブル名
```
##### CASE式使い方１
```sql
SELECT 
	*,
	CASE birth_place
	WHEN "日本" THEN '日本人'
	WHEN 'Iraq' THEN 'イラク人'
	ELSE "外国人"
	END AS '国籍'
FROM users
WHERE id>30; -- WHEREとの併用も可

SELECT
	name,
	CASE
		WHEN name IN ('香川県','愛媛県','徳島県','高知県') THEN '四国'
		WHEN name IN ('兵庫県','大阪府','京都府','滋賀県','奈良県','三重県','和歌山県') THEN '近畿'
		ELSE 'その他'
	END AS '地域名'
FROM prefectures;

-- DATE_FORMATと併用（うるう年、4の余り==０、100の余り!=0）
SELECT
	name,
	birth_day,
	CASE 
		WHEN DATE_FORMAT(birth_day,"%Y") %4 = 0  AND DATE_FORMAT(birth_day,"%Y") %100 <>100 THEN 'うるう年'
		ELSE 'うるう年ではない'
	END AS 'うるう年か'
FROM users;

SELECT 
	*,
	CASE 
		WHEN student_id % 3 =0 THEN test_score_1
		WHEN student_id % 3 =1 THEN test_score_2
		WHEN student_id % 3 =2 THEN test_score_3
	END AS 'score'
FROM tests_score;
```

##### CASE式使い方2(ORDER BY,UPDATE)
```sql

-- ORDER BY + CASE
-- 条件によって地域名を割り振り、それらをORDER BYによって並び替える
SELECT
	*,
	CASE 
	WHEN name IN ('香川県','愛媛県','徳島県','高知県') THEN '四国'
	WHEN name IN ('兵庫県','大阪府','京都府','滋賀県','奈良県','三重県','和歌山県') THEN '近畿'
	ELSE 'その他'
END AS '地域名'
FROM prefectures
ORDER BY
CASE 
	WHEN name IN ('香川県','愛媛県','徳島県','高知県') THEN '四国'
	WHEN name IN ('兵庫県','大阪府','京都府','滋賀県','奈良県','三重県','和歌山県') THEN '近畿'
	ELSE 'その他'
END DESC,id;

-- 並び替える順番を指定したい時は0,1,2とエイリアスをつけるイメージ？
SELECT
	*,
	CASE 
	WHEN name IN ('香川県','愛媛県','徳島県','高知県') THEN '四国'
	WHEN name IN ('兵庫県','大阪府','京都府','滋賀県','奈良県','三重県','和歌山県') THEN '近畿'
	ELSE 'その他'
END AS '地域名'
FROM prefectures
ORDER BY
CASE 
	WHEN name IN ('香川県','愛媛県','徳島県','高知県') THEN 0
	WHEN name IN ('兵庫県','大阪府','京都府','滋賀県','奈良県','三重県','和歌山県') THEN 1
	ELSE 2
END,id;

-- UPDATE + CASE
SELECT * FROM users;
-- 一旦カラム追加
ALTER TABLE users ADD birth_era VARCHAR(2) AFTER birth_day;

-- UPDATE前にSELECT文でしっかりSQLが成されるかを確認するといい
SELECT
	*,
	CASE 
		WHEN birth_day < "1989-01-07" THEN "昭和"
		WHEN birth_day < "2019-05-01" THEN "平成"
		WHEN birth_day >= "2019-05-01" THEN "令和"
		ELSE "不明"
	END AS "元号"
FROM users;

-- UPDATEする
UPDATE users SET birth_era=
	CASE 
		WHEN birth_day < "1989-01-07" THEN "昭和"
		WHEN birth_day < "2019-05-01" THEN "平成"
		WHEN birth_day >= "2019-05-01" THEN "令和"
		ELSE "不明"
	END;

--UPDATEが完了したかの確認
SELECT * FROM users WHERE birth_era = '令和';
```

##### CASE式使い方３（NULLを扱う場合）
ここでもIS　NULLを使う
```sql
-- NULLを扱う場合
SELECT * FROM customers WHERE name IS NULL;

-- だめな例
SELECT
	*,
	CASE name
	WHEN NULL THEN "不明"
	ELSE ""
	END AS 'NULL_CHECK'
FROM customers;

-- いい例
SELECT
	*,
	CASE
	WHEN name IS NULL THEN "不明"
	WHEN name IS NOT NULL THEN "NULL以外"
	ELSE ""
	END AS 'NULL_CHECK'
FROM customers 
	
```

## トランザクション、ロールバック、コミット
トランザクションとはDBの原子性を保つためのもである。
原子性とは決められた処理が完了しないまま中断されるとそれまで行なっていたことをすべてしなかったことにすること。処理を０か１００の状態にすること
```sql
-- TRANSACTIONの開始
START TRANSACTION;
-- 奥村さんを奥山さんに変更する
UPDATE users SET name ="奥山 成美" WHERE id =1;

/*
このときトランザクション上ではデータが変更されているが
DBにはまだ更新されていない
*/

-- ROLLBACK(トランザクション開始前に戻す)
ROLLBACK; -- 奥村さんに戻る

-- COMMIT(トランザクションをDBに反映)
COMMIT; -- 奥山さんに変更する
```
##### オートコミットの設定変更
多くのDBMSでは自動的にコミットされる（UPDATEなどしたときにDBに即反映すること）オートコミットモードになっているがそれを変更する方法もある
```sql:MySQLの場合
-- 自動コミットモードかどうか確認する
SHOW VARIABLES WHERE variable_name="autocommit";
-- おまけ：SHOW VARIABLES;　を確認するといろんな変数をみることができる
-- 自動コミットモードを解除する　（元に戻すには１）
SET AUTOCOMMIT=0

COMMIT; --明示的にコミットすることによって慎重に作業を進められる
```

##### ロックとは
DBのテーブルや行を固定して、別のトランザクションからテーブルや行の参照・更新ができないようにすること(順番待ちになる)
ロックには大きく２種類あり、共有ロックと排他ロックがある。
ロック対象としてはテーブルに対してのロック、行に対してのロックもある

:::note
共有ロック・・・他のトランザクション（ユーザ）から参照はできるが更新はできなくなるロック
例：SELECTは⚪︎  UPDATE,DELETE,INSERT は×
共有ロックは複数ユーザで共存可能　
:::

:::note
排他ロック・・・他のトランザクション（ユーザ）から参照も更新もできなくなるロック
例：SELECT,UPDATE,DELETE,INSERT すべて×
排他ロックは1人しかかけれない
:::
テーブルロック、行ロックが発生する条件は下記
```sql:MySQLの場合かつSELECT処理
/*テーブルロック*/
-- 共有ロック
SELECT * FROM table_name FOR SHARE(MySQL5.7ではサポートされていない)
-- 排他ロック
SELECT * FROM table_name FOR UPDATE(NOWAIT)

/*行ロック（主キーまたはユニークカラムで絞り込んだ場合）*/
-- 共有ロック
SELECT * FROM table_name WHERE id=1 FOR SHARE
-- 排他ロック
SELECT * FROM table_name WHERE id=1 FOR UPDATE

```
UPDATE,DELETE,INSERTについては自動的に排他ロックされる。
行ロックに関してもSELECTと同じく主キーまたはユニークカラムで絞り込んだ場合行ロックになる。
つまり主キーやユニークキー以外で更新されているとテーブル全体にロックがかかる。

##### 明示的なテーブルロックとデッドロック
:::note
デッドロックとは：２つのセッションが互いの更新対象のテーブルをロックしていて処理が進まない状態のこと。デッドロックが起こらないような仕組みを作ることが大事。
発生させないためにテーブルを更新する順番を揃えること。
:::
```sql
-- 下記はMySQLの手法
-- LOCK TABLE READ(読み込みのみOKなロック。他のセッションからも読み込みのみOK)
LOCK TABLE customers READ; -- READでロックすると
SELECT * FROM customers; -- 読み込めるが
UPDATE customers SET age =39 WHERE id=1; -- 本人も更新はできない

UNLOCK TABLES; -- ロックを解除

-- LOCK TABLE WRITE(実行したユーザは読み書きOK。他のセッションからはどちらも不可)
LOCK TABLE customers WRITE; -- WRITEでロック
SELECT * FROM customers; -- 可能
UPDATE customers SET age=12 WHERE id=1; -- 可能

UNLOCK TABLES; -- ロックを解除

-- DEAD LOCK
START TRANSACTION;
UPDATE customers SET age=79 WHERE id=1;
UPDATE users SET age = 33 WHERE id=1;

SELECT * FROM users;

/*他のセクションにて下記を１行ずつ同時に行なった場合に
 *DEADLOCK状態になってしまうがMySQLの場合先に実行した方が
 *実行待ちになり、他のセッションから操作を受けると
 *相手のLOCKを解除し、こちらのセッションを通す動きになる
START TRANSACTION;
UPDATE users SET age = 50 WHERE id=1;
UPDATE customers SET age=70 WHERE id=1;
 
*/

ROLLBACK;

```
##### ORマッパー
アプリケーションとDBを橋渡しする機能やソフトウェアのこと。SQLを書かずに、プログラムでDB操作ができる

### 勉強続編
記事が重くなってきたので続きは下記

https://qiita.com/Ryo_Kitamoto/items/9786e8af7ba79ebe56bf

## 番外編(エラー記録)


#### リストアできない
初めはリストアしようとするとこちらの表記がでてリストアできなかった
「クライアントホームが接続先として指定されていません」

なのでこのあたりをみた

https://github.com/dbeaver/dbeaver/issues/1361

ネイティブクライアント（またはローカルクライアント？）が設定できていないためであるとのことだったので
データベースタブ→ドライブマネージャー→MySQL→編集→ネイティブクライアント→ホームを追加からMySQL5.7.44を追加。
再びリストアすると![スクリーンショット 2023-11-27 11.54.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627182/b55fd843-fc2f-cbde-0e9a-0906e5e53ad2.png)

エラーを見ると'utf8mb4_0900_ai_ci'がUnknownだと。
結論から言うと文字コードのエラーらしく

https://asagao13a.hatenablog.com/entry/2019/11/25/212649

こちらの記事を参考にsqlファイル内の'utf8mb4_0900_ai_ci'をすべて'utf8mb4_general_ci'に置換すると無事リストアできた！