MySQL-3 - マルチテーブルのクエリとトランザクション (ケーススタディと組み合わせたもの)



複数テーブルのクエリ

複数テーブルの関係

プロジェクト開発中、データベースのテーブル構造を設計する際、ビジネス要件とビジネス モジュール間の関係に従ってテーブル構造が分析および設計されます。ビジネスは相互に関連しているため、テーブル構造間にはさまざまな接続があります。基本的には、 3 つのタイプ:

  • 1対多(多対1)
  • 多対多
  • 一対一

1対多

  • 事例:部門と従業員の関係
  • 関係: 1 つの部門が複数の従業員に対応し、1 人の従業員が 1 つの部門に対応します。
  • 実装:多側に外部キーを作成し、一方側の主キーを指します。
    ここに画像の説明を挿入

多対多

  • 例: 学生とコースの関係
  • 関係: 1 人の学生が複数のコースを受講でき、1 つのコースを複数の学生が選択することもできます。
  • 実装: 3 番目の中間テーブルを作成します。中間テーブルには少なくとも 2 つの外部キーが含まれ、
    ここに画像の説明を挿入 2 つのパーティの主キーに対応する SQL スクリプトを関連付けます。
create table student(
    id int auto_increment primary key comment '主键ID',
    name varchar(10) comment '姓名', no varchar(10) comment '学号'
) comment '学生表';

insert into student
values (null, '黛绮丝', '2000100101'),
       (null, '谢逊', '2000100102'),
       (null, '殷天正', '2000100103'),
       (null, '韦一笑', '2000100104');


create table course(
    id int auto_increment primary key comment '主键ID',
    name varchar(10) comment '课程名称'
) comment '课程表';

insert into course
values (null, 'Java'),
       (null, 'PHP'),
       (null , 'MySQL') ,
       (null, 'Hadoop');


create table student_course(
    id int auto_increment comment '主键' primary key,
    student_id int not null comment '学生id',
    course_id int not null comment '课程id',
    constraint fk_courese_id foreign key (course_id) references course(id),
    constraint fk_student_id foreign key (student_id) references student(id)
)comment '学生课程中间表';

insert into student_course
values (null,1,1),(null,1,2),(null,1,3),(null,2,2), (null,2,3),(null,3,4);

次の方法を使用して、それらの間の関係図を表示できます。
ここに画像の説明を挿入

一対一

  • 事例: ユーザーとユーザー詳細の関係
  • リレーションシップ: 1 対 1 のリレーションシップ。主に単一テーブルの分割に使用され、1 つのテーブルの基本フィールドを 1 つのテーブルに配置し、他の詳細フィールドを別のテーブルに配置して、操作効率を向上させます。
  • 実現:いずれかのパーティに外部キーを追加し、もう一方のパーティの主キーを関連付け、その外部キーを一意の (UNIQUE)
    対応する SQL スクリプトとして設定します。ここに画像の説明を挿入
create table tb_user(
    id int auto_increment primary key comment '主键ID',
    name varchar(10) comment '姓名', age int comment '年龄',
    gender char(1) comment '1: 男 , 2: 女', phone char(11) comment '手机号'
) comment '用户基本信息表';

create table tb_user_edu(
    id int auto_increment primary key comment '主键ID',
    degree varchar(20) comment '学历',
    major varchar(50) comment '专业',
    primaryschool varchar(50) comment '小学',
    middleschool varchar(50) comment '中学',
    university varchar(50) comment '大学',
    userid int unique comment '用户ID',
    constraint fk_userid foreign key (userid) references tb_user(id)
) comment '用户教育信息表';

insert into tb_user(id, name, age, gender, phone)
values (null,'黄渤',45,'1','18800001111'),
       (null,'冰冰',35,'2','18800002222'),
       (null,'码云',55,'1','18800008888'),
       (null,'李彦宏',50,'1','18800009999');

insert into tb_user_edu(id, degree, major, primaryschool, middleschool, university, userid)
values (null,'本科','舞蹈','静安区第一小学','静安区第一中学','北京舞蹈学院',1),
       (null,'硕士','表演','朝阳区第一小学','朝阳区第一中学','北京电影学院',2),
       (null,'本科','英语','杭州市第一小学','杭州市第一中学','杭州师范大学',3),
       (null,'本科','应用数学','阳泉第一小学','阳泉区第一中学','清华大学',4);

データの準備

1). 以前の emp テーブルと dept テーブルのテスト データを削除します
2). 以下のスクリプトを実行して、emp テーブルと dept テーブルを作成し、テスト データを挿入します

drop table if exists emp;
drop table if exists dept;

create table dept(
    id int auto_increment comment 'ID' primary key,
    name varchar(50) not null comment '部门名称'
)comment '部门表';
INSERT INTO dept (id, name)
VALUES (1, '研发部'), (2, '市场部'),(3, '财务部'), (4, '销售部'), (5, '总经办'), (6, '人事部');


create table emp(
    id int auto_increment comment 'ID' primary key,
    name varchar(50) not null comment '姓名',
    age int comment '年龄',
    job varchar(20) comment '职位',
    salary int comment '薪资',
    entrydate date comment '入职时间',
    managerid int comment '直属领导ID',
    dept_id int comment '部门ID',
    constraint fk_emp_dept_id foreign key (dept_id) references dept(id)
)comment '员工表';
INSERT INTO emp (id, name, age, job,salary, entrydate, managerid, dept_id)
VALUES (1, '金庸', 66, '总裁',20000, '2000-01-01', null,5),
       (2, '张无忌', 20, '项目经理',12500, '2005-12-05', 1,1),
       (3, '杨逍', 33, '开发', 8400,'2000-11-03', 2,1),
       (4, '韦一笑', 48, '开发',11000, '2002-02-05', 2,1),
       (5, '常遇春', 43, '开发',10500, '2004-09-07', 3,1),
       (6, '小昭', 19, '程序员鼓励师',6600, '2004-10-12', 2,1),
       (7, '灭绝', 60, '财务总监',8500, '2002-09-12', 1,3),
       (8, '周芷若', 19, '会计',48000, '2006-06-02', 7,3),
       (9, '丁敏君', 23, '出纳',5250, '2009-05-13', 7,3),
       (10, '赵敏', 20, '市场部总监',12500, '2004-10-12', 1,2),
       (11, '鹿杖客', 56, '职员',3750, '2006-10-03', 10,2),
       (12, '鹤笔翁', 19, '职员',3750, '2007-05-09', 10,2),
       (13, '方东白', 19, '职员',5500, '2009-02-12', 10,2),
       (14, '张三丰', 88, '销售总监',14000, '2004-10-12', 1,4),
       (15, '俞莲舟', 38, '销售',4600, '2004-10-12', 14,4),
       (16, '宋远桥', 40, '销售',4600, '2004-10-12', 14,4),
       (17, '陈友谅', 42, null,2000, '2011-10-12', 1,null);

概要

マルチテーブル クエリとは、複数のテーブルからデータをクエリすることを指します。
本来、単一テーブル データをクエリする場合、実行される SQL 形式は次のとおりです:select * from emp;
複数テーブル クエリを実行したい場合は、カンマを使用して複数のテーブルを区切るだけです。たとえば、 :select * from emp , dept ここに画像の説明を挿入
結果は従業員テーブル emp の全レコードと部門テーブル dept の全レコードのすべての組み合わせです。この現象はデカルト積と呼ばれます。

デカルト積: デカルト積とは、数学における 2 つのセット A と B のすべての組み合わせを指します。
ここに画像の説明を挿入
SQL ステートメントでは、複数テーブル クエリに接続クエリの条件を追加できます。ここでの外部キーは emp.dept_id で、主キーは dept.id であるため、
実行後select * from emp , dept where emp.dept_id = dept.id;に必要な結果を保持できます。
ここに画像の説明を挿入ただし、ID 17 の従業員には dept_id フィールド値がないため、emp.dept_id = dept.id という条件は存在せず、クエリは実行されません。

分類

ここに画像の説明を挿入

  • 接続クエリ
    • 内部結合: A と B の交差データのクエリと同等
    • 外部結合:
      • 左外部結合:左側のテーブル内のすべてのデータと、2 つのテーブルの交差部分にある一部のデータをクエリします。
      • 右外部結合:右テーブル内のすべてのデータと、2 つのテーブルの交差部分にある一部のデータをクエリします。
    • 自己結合: 現在のテーブルとそれ自体の間の接続クエリ。自己結合ではテーブルのエイリアスを使用する必要があります。
  • 集計クエリ
  • サブクエリ

接続クエリ

内部結合

内部結合クエリは2 つのテーブルの共通部分です。内部結合には、暗黙的な内部結合と明示的な内部結合
の 2 つの構文がありますまずは具体的な文法構造を学びましょう。

暗黙的な内部結合

SELECT 字段列表 FROM 表1 , 表2 WHERE 条件 ... ;(上記の例)

明示的な内部結合

SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ... ;

ケース

  • 各従業員の名前と関連する部門の名前をクエリします (暗黙的な内部結合の実装)
    ・テーブル構造: emp、dept
    ・結合条件: emp.dept_id = dept.id ステートメント
    select emp.name,dept.name from emp, dept where emp.dept_id = dept.id;
    を簡略化するためにエイリアスを使用できます。省略される。
    select e.name '姓名',d.name '部门' from emp as e, dept as d where e.dept_id = d.id;
  • 各従業員の名前と関連する部門の名前をクエリします (明示的な内部接続の実装) —INNER JOIN ... ON ...
    テーブル構造: emp 、 dept
    接続条件: emp.dept_id = dept.id
    select e.name, d.name from emp e inner join dept d on e.dept_id = d.id;または
    select e.name, d.name from emp e join dept d on e.dept_id = d.id;

外部結合

外部結合には、左外部結合右外部結合の 2 種類があります。具体的な文法構造は次のとおりです。

左外部結合

SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ... ; 1
左外部結合は、テーブル 1 (左側のテーブル) 内のすべてのデータをクエリすることと同等で、当然、テーブル 1 とテーブル 2 の共通部分のデータも含まれます。

右外部結合

SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ... ;
右外部結合は、テーブル 2 (右テーブル) 内のすべてのデータをクエリすることと同等であり、当然、テーブル 1 とテーブル 2 の共通部分のデータも含まれます。

ケース

  • emp テーブル内のすべてのデータと、対応する部門情報をクエリします
    。要件で述べたように、emp 内のすべてのデータをクエリする必要があるため、内部接続をクエリすることはできません。外部接続クエリの使用を検討する必要があります。 。
    ・テーブル構造:emp、dept
    ・結合条件:emp.dept_id = dept.id
    select e.*, d.name from emp e left join dept d on e.dept_id = d.id;
    ここに画像の説明を挿入

  • dept テーブル内のすべてのデータと、対応する従業員情報 (右外部結合) をクエリします。
    要件で述べたように、dept テーブル内のすべてのデータをクエリする必要があるため、内部結合クエリは不可能であり、以下を行う必要があります。外部結合クエリの使用を検討してください

    ・テーブル構造:emp、dept
    ・結合条件:emp.dept_id = dept.id
    select e.*, d.name from emp e right join dept d on e.dept_id = d.id;
    or (右外部結合を左外部結合として記述する)
    select e.*, d.name from dept d left join emp e on e.dept_id = d.id;
    ここに画像の説明を挿入

注:
左外部結合と右外部結合は相互に置き換えることができます。クエリを接続するときに SQL でテーブル構造の順序を調整するだけで済みます
私たちは日々の開発と使用において、左外側の接続を好みます。

自己参加

自己結合クエリ

自己結合クエリは、名前が示すように、それ自体を接続すること、つまりテーブルを複数回接続してクエリを実行することです。まず、自己結合のクエリ構文を学びましょう。
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ... ;
自己結合クエリの場合、内部結合クエリまたは外部結合クエリにすることができます。

ケース

  • 従業員とそのリーダーの名前をクエリします
    。テーブル構造: emp
    アイデア: managerid が id に対応する人物であるため、emp テーブルをテーブル a とテーブル b にコピーします。テーブル a の ID が主キーであり、テーブルb managerid は外部キーです。この時点で接続クエリを使用できます。
    select a.name , b.name from emp a , emp b where a.managerid = b.id;(内部結合)

  • すべての従業員 emp とそのリーダーの名前 emp を照会します。従業員にリーダーがいない場合は、それも照会する必要があります。
    テーブル構造: emp
    select a.name '员工', b.name '领导' from emp a left join emp b on a.managerid = b.id;(外部結合)

注:
自己結合クエリでは、テーブルにエイリアスを指定する必要があります。そうでないと、指定された条件、返されたフィールド、および
それがどのテーブルのフィールドであるかがわかりません。

共同クエリ

ユニオン クエリの場合、複数のクエリの結果が結合されて、新しいクエリ結果セットが形成されます。

SELECT 字段列表 FROM 表A ... 
UNION [ ALL ] 
SELECT 字段列表 FROM 表B ....;

ケース

  • 給与が 5,000 未満で、年齢が 50 歳を超えるすべての従業員をクエリします。
    現時点では、この要件に対して、複数条件クエリを直接使用し、論理演算子 or を使用して接続できます。したがって、ここでは、union/union all を使用して共同でクエリを実行することもできます。
select * from emp where salary < 5000 
union all 
select * from emp where age > 50;
select * from emp where salary < 5000 
union  
select * from emp where age > 50;

知らせ

  • 結合クエリの場合、複数のテーブルの列数とフィールド タイプが一致している必要があります。
  • Union all はすべてのデータを直接マージしunion はマージされたデータの重複を除去します。

サブクエリ

概要

  • SQL ステートメント内で SELECT ステートメントをネストする概念は、
    ネストされたクエリと呼ばれ、サブクエリとも呼ばれます。
    SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2 );
    サブクエリの外側のステートメントは、INSERT / UPDATE / DELETE / SELECT のいずれかになります。

分類

  • さまざまなサブクエリ結果に応じて、次のように分類されます。
    • スカラー サブクエリ (サブクエリの結果は単一の値)
    • 列サブクエリ (サブクエリの結果は列です)
    • 行サブクエリ(サブクエリの結果は1行)
    • テーブルサブクエリ (サブクエリの結果は複数の行と複数の列です)
  • サブクエリの位置に応じて、次のように分類されます。
    • どこの後
    • FROMの後
    • 選択後

スカラーサブクエリ

サブクエリは単一の値 (数値、文字列、日付など) を返します。最も単純な形式では、このようなサブクエリはスカラー サブクエリと呼ばれます。
一般的に使用される演算子: = 、<>/!=、>、>=、<、<=

ケース

  • 「営業部」の全従業員情報を問い合わせる
    この要件を完了すると、要件は 2 つのステップに分解できます:
    ①.「営業部」の部門 ID を問い合わせる
    ②.「営業部」の部門 ID に応じて従業員情報を問い合わせる
    select * from emp where dept_id = (select id from dept where name = '销售部');(サブクエリが返す値は 1 つだけなので、スカラー サブクエリと呼ばれます)

  • 「Fang Dongbai」がジョブに入社した後の従業員情報を照会します
    。この要件を完了すると、要件を 2 つのステップに分解できます:
    ①. Fang Dongbai の入社日を照会する
    ②. 指定された入社日以降に入社した従業員情報を照会する
    select * from emp where entrydate > (select entrydate from emp where name = '方东 白');

列クエリ

サブクエリによって返される結果は列 (複数行の場合もあります) であり、このサブクエリは列サブクエリと呼ばれます。
一般的に使用される演算子: IN 、 NOT IN 、 ANY 、 SOME 、 ALL

一般的な演算子

オペレーター 説明
指定された収集範囲内で、もう 1 つ選択します
ありませんで 指定されたコレクション内にありません
どれでも サブクエリによって返されるリストでは、どれも満たすことができます。
いくつかの ANY と同等であり、SOME が使用される場合はどこでも ANY を使用できます。
全て サブクエリによって返されるリスト内のすべての値は次の条件を満たす必要があります

ケース

  • 「営業部」「マーケティング部」の全従業員情報の問い合わせは、①「営業部」「マーケティング部」の部門IDの問い合わせ②.部門IDに応じた従業員情報の問い合わせの
    2ステップに分かれます。

select * from emp where dept_id in (select id from dept where name = '销售部' or name = '市场部');

  • 財務部門の全員より給与が高い従業員情報の問い合わせは、
    次の 2 つのステップに分解できます:
    ①. 財務部門の全員の給与を問い合わせる
    ②. 財務部門の全員より給与が高い従業員の情報を問い合わせる財務部門
    。ここにデータ エラーがあります。修正してください。update emp set salary = 4800 where salary = 48000;
    select * from emp where salary > all (select salary from emp where dept_id = (select id from dept where name = '财务部'));

  • 研究開発部門の誰よりも給与が高い従業員情報を問い合わせるには、
    次の 2 つのステップに分解できます:
    ①. 研究開発部門全員の給与を問い合わせる
    ②. 研究開発部門の誰よりも給与が高い従業員情報を検索する
    select * from emp where salary > any (select salary from emp where dept_id = (select id from dept where name = '研发部'));
    select * from emp where salary > some (select salary from emp where dept_id = (select id from dept where name = '研发部'));

行サブクエリ

サブクエリによって返される結果は行 (複数の列の場合もあります) であり、このサブクエリは行サブクエリと呼ばれます。
一般的に使用される演算子: = 、 <>/!= 、 IN 、 NOT IN

ケース

  • 「Zhang Woji」の給与および直属のリーダーと同じ従業員情報を照会します。
    この要件は、次の 2 つのステップに分解することもできます:
    ①. 「Zhang Woji」の給与および直属のリーダーを照会
    する ②. 「Zhang Woji」の給与および直属のリーダーを照会する「張無記」の社員情報
    select * from emp where (salary,managerid) = (select salary,managerid from emp where name = '张无忌') ;

テーブルサブクエリ

サブクエリによって返される結果は複数の行と複数の列であり、この種のサブクエリはテーブル サブクエリと呼ばれます。
よく使用される演算子: IN

ケース

  • 「Luzhangke」と「Song Yuanqiao」と同じ役職と給与の従業員情報をクエリします

    ①. 「Luzhangke」と「Song Yuanqiao」の同じ役職と給与をクエリします。 ②. 「Luzhangke」と「Song Yuanqiao」と
    同じ役職と給与をクエリします。 Luzhangke」、「Song Yuanqiao」と同じ役職および給与の従業員に関する情報
    select * from emp where (job, salary) in (select job, salary from emp where name = '鹿杖客' or name = '宋远桥');

  • 入力日が「2006-01-01」以降の従業員情報を問い合わせる場合、その部門情報は

    . 入力日が「2006-01-01」以降の従業員情報
    ②. この部分を問い合わせる場合従業員数、該当部門情報
    select e.*, d.name from (select * from emp where entrydate > '2006-01-01') e left join dept d on e.dept_id = d.id;

複数テーブルクエリの包括的なケース

データ環境の準備:

create table salgrade( 
    grade int, 
    losal int, 
    hisal int
) comment '薪资等级表'; 
insert into salgrade values (1,0,3000), (2,3001,5000),
                            (3,5001,8000), (4,8001,10000),
                            (5,10001,15000), (6,15001,20000),
                            (7,20001,25000), (8,25001,30000);

この場合、主に上記で説明したマルチテーブル クエリ構文を使用して、次の 12 の要件を満たしますが、その方法は一意ではありません。ここには、emp 従業員テーブル、部門部門テーブル、および給与等級テーブルという 3 つの主要なテーブルが関係しています。

  1. 従業員の名前、年齢、役職、部門情報をクエリ(暗黙的な内部結合)
    テーブル:emp、dept
    結合条件:emp.dept_id = dept.id
    select e.name, e.age, e.job, d.name from emp e left join dept d on e.dept_id = d.id;

  2. 30歳未満の従業員の氏名、年齢、役職、所属情報を問い合わせる(明示的内部結合)
    テーブル:emp、dept
    結合条件:emp.dept_id = dept.id
    select e.name, e.age, e.job, d.name from emp e inner join dept d on e.dept_id = d.id where e.age < 30;SQLの実行順序に従い、最初に結合条件で 2 つのテーブルが接続され、その後、where の後の条件に従って最終結果が返されます。

  3. 従業員がいる部門IDと部門名を問い合わせる
    テーブル: emp, dept
    接続条件: emp.dept_id = dept.id
    select distinct d.* from emp e, dept d where e.dept_id = d.id;わかりにくい場合は、内部接続が実際にはデカルト積の結果であると想像してください条件に従ってフィルタリングすると、部門に従業員がいない場合、条件 e.dept_id = d.id を持つことは不可能であるため、正しい結果を直接取得できます。

これら 3 つはすべて内部結合の問題であり、内部結合で得られるものが 2 つのテーブルの交差結果であることも完全に反映しています。

  1. 40 歳以上のすべての従業員とその部門名をクエリします。従業員に部門が割り当てられていない場合は、その部門も表示する必要があります (外部接続) テーブル: emp、dept 接続条件: emp.dept_id
    =
    dept.id
    select e.*, d.name from emp e left join dept d on e.dept_id = d.id where e.age > 40;

  2. すべての従業員の給与等級をクエリする
    テーブル: emp 、 salgrade
    結合条件: emp.salary >= salgrade.losal および emp.salary <= salgrade.hisal
    select e.*, s.* from emp e left join salgrade s on e.salary between s.losal and s.hisal;

  3. 「研究開発部門」の全従業員の情報と給与レベルを照会
    テーブル: emp 、 salgrade 、 dept
    接続条件:給与.losal と salgrade.hisal の間の emp.salary 、 emp.dept_id = dept.id
    クエリ条件: dept.name =「研究開発部門」
    select e.*, d.name, s.grade from emp e, dept d, salgrade s where e.dept_id = d.id and (e.salary between s.losal and s.hisal) and d.name = '研发部';

  4. 「研究開発部門」の従業員の平均給与をクエリする
    テーブル: emp 、 dept
    接続条件: emp.dept_id = dept.id
    select d.name, avg(e.salary) from emp e left join dept d on d.id = e.dept_id where d.name = '研发部';

  5. 給与が「消滅」よりも高い従業員に関する情報を照会します。
    ①.「Extinction」の給与を問い合わせる
    ②.自分より給与が高い従業員のデータを問い合わせる
    select * from emp where salary > (select salary from emp where name = '灭绝');

  6. 平均よりも給与が高い従業員の情報を問い合わせる
    ①. 従業員の平均給与を問い合わせる
    ②. 平均よりも給与が高い従業員の情報を問い合わせる
    select * from emp where salary > (select avg(salary) from emp);

  7. 部門の平均給与より低い従業員の情報を問い合わせる
    ① 指定した部門の平均給与を問い合わせる
    ② 部門の平均給与より低い従業員の情報を問い合わせる
    select *, (select avg(salary) from emp e1 where e1.dept_id = e2.dept_id) '平均薪资' from emp e2 where e2.salary < (select avg(salary) from emp e1 where e1.dept_id = e2.dept_id);

  8. すべての部門情報を照会し、部門内の従業員数をカウントします。
    select dept.*, (select count(*) from emp where emp.dept_id = dept.id ) '人数' from dept;

  9. 全学生のコース選択を問い合わせ、学生名、学生番号、コース名を表示
    テーブル:student 、 course 、student_course
    接続条件:student.id =student_course.studentid 、course.id =
    student_course.courseid
    select t.name, t.no, c.name from (select s.*, sc.course_id from student s left join student_course sc on s.id = sc.student_id) t left join course c on c.id = t.course_id;

事務

概要

トランザクションは操作の集合であり、分割できない作業単位です。トランザクションは、システム全体に対して操作リクエストを送信または取り消します。つまり、これらの操作は同時に成功するか失敗します

たとえば、Zhang San が Li Si に 1,000 元を送金すると、Zhang San の銀行口座のお金は 1,000 減り、Li Si の銀行口座のお金は 1,000 増加します。この一連の操作はトランザクションのスコープ内にある必要があり、すべて成功するかすべて失敗します。

  • 通常の状況: 転送操作は次の 3 つのステップに分けて完了する必要があります。3 つのステップが完了すると、Zhang San は 1000 減少し、Li Si は 1000 増加し、転送は成功します。
  • 異常事態: 転送操作も次の 3 つのステップに分かれて完了します。3 番目のステップでエラーが報告された場合、張三は 1,000 元を失うことになりますが、李斯の金額は変更されず、データ破損が発生します。一致しない場合は問題があります。
  • 上記の問題を解決するには、ビジネスロジックの実行前にトランザクションを開始し、実行完了後にトランザクションを送信するだけで、データトランザクションによって完了する必要があります。実行中にエラーが報告された場合、トランザクションはロールバックされ、データはトランザクション開始前の状態に復元されます

注: デフォルトの MySQL トランザクションは自動的にコミットされます。つまり、DML ステートメント (データの追加、削除、変更) が実行されると、MySQL は即座に暗黙的にトランザクションをコミットするため、トランザクションを手動で開く必要があります。

トランザクション操作

データを準備する

drop table if exists account;
create table account
(
    id    int primary key AUTO_INCREMENT comment 'ID',
    name  varchar(10) comment '姓名',
    money double(10, 2) comment '余额'
) comment '账户表';

insert into account(name, money)
VALUES ('张三', 2000),
       ('李四', 2000);

制御されていないトランザクション

通常の状況

select * from account where name = '张三';
update account set money = money - 1000 where  name = '张三';
update account set money = money + 1000 where  name = '李四';
update account set money = 2000 where name = '张三' or name = '李四';

異常事態

#把下面的语句作为一个整体执行,报错后,数据就会出现我们解释的错误情况
update account set money = money - 1000 where  name = '张三';
update account set money = money + 1000 where;

制御トランザクション 1

  1. トランザクション送信方法の表示/設定
    SELECT @@autocommit ;
    ここに画像の説明を挿入
    SET @@autocommit = 0 ;1 は自動送信、0 は手動送信
  2. トランザクションをコミットする
    COMMIT;
  3. トランザクションのロールバック
    ROLLBACK;

注: 上記のメソッドでは、トランザクションの自動コミット動作を変更し、デフォルトの自動コミットを手動コミットに変更しました。
この時点では、実行する DML ステートメントはどれもコミットされないため、手動でコミットを実行する必要があります。コミットする。

デモ

プログラムはエラーなく正常です
update account set money = 2000 where name = '张三' or name = '李四';
set @@autocommit = 0;
update account set money = money - 1000 where  name = '张三';
update account set money = money + 1000 where  name = '李四';
select * from account;

このとき、出力は変化していますが(トランザクションを申請したことが分かりますが、データベースを直接操作するのではなく、このトランザクションを直接操作していることがわかります)、実行後は変化がないことがわかります。 DataGripのデータベース更新を開きます
ここに画像の説明を挿入
したがって、トランザクションを送信する
ここに画像の説明を挿入
必要があります

commit

ここに画像の説明を挿入

プログラムエラー

update account set money = money - 1000 where  name = '张三';
commit;
set @@autocommit = 0;
update account set money = money - 1000 where  name = '张三';
update account set money = money + 1000 where;

上記のコードを実行すると、プログラムはエラーを報告します。この時点では、データベースは変更されていないことがわかりますが、申請したトランザクションは変更されました (Zhang San のお金を 1000 減らしたためですが、データは変更しませんでした)データベース内)。
ここに画像の説明を挿入
現時点では、トランザクションの処理プロセスに問題があるため、トランザクションを元の外観に戻し、ロールバック トランザクションを実行できます

rollback;

ここに画像の説明を挿入

管理業務2

  1. オープントランザクション
    START TRANSACTION 或 BEGIN ;
  2. トランザクションをコミットする
    COMMIT;
  3. トランザクションのロールバック
    ROLLBACK;

デモ

set @@autocommit = 1;
update account set money = 2000 where name = '张三' or name = '李四';
start transaction;
update account set money = money - 1000 where  name = '张三';
update account set money = money + 1000 where name = '李四';
commit;

update account set money = 2000 where name = '张三' or name = '李四';
start transaction;
update account set money = money - 1000 where  name = '张三';
update account set money = money + 1000 where name = '李四';
rollback;

ビジネスの4大特徴(頻繁に試される面接)

  • 原子性: トランザクションは、分割できない最小の操作単位であり、すべてが成功するかすべてが失敗します (すべてが成功または失敗するわけではありません)。
  • 一貫性: トランザクションが完了すると、すべてのデータが一貫した状態に保たれる必要があります。
  • 分離: データベース システムによって提供される分離メカニズムにより、トランザクションは外部の同時操作の影響を受けない独立した (複数のトランザクションが同時に実行されている場合に相互に分離される) 環境で実行されます。
  • 耐久性: トランザクションがコミットまたはロールバックされると、データベース内のデータに対する変更は永続的になります。

以上が、ACIDと呼ばれるトランザクションの4つの特徴です。

同時トランザクションの問題

ダーティリード

ここに画像の説明を挿入トランザクション A がデータを更新した後、トランザクション B はトランザクション A のコミットされていないデータを読み取ります。

反復不可能な読み取り

ここに画像の説明を挿入
トランザクションAが1回の操作を行った後、トランザクションBがデータを更新しますが、このときトランザクションAが2回の操作を行った場合に読み込まれるデータが異なります。

ファントムリーディング

ここに画像の説明を挿入
トランザクション A はデータのクエリ時にデータがないことを検出し、挿入の準備をしますが、トランザクション A が挿入する前にトランザクション B がデータを挿入します。

トランザクション分離レベル

分離レベル

分離レベル ダーティリード 反復不可能な読み取り ファントムリーディング
コミットされていない読み取り (コミットされていない読み取り)
コミットされた読み取り (コミットされた読み取り) ×
反復可能読み取り (デフォルト、反復可能読み取り) × ×
シリアル化可能 × × ×
  • √ は、現在の分離レベルで問題が発生することを示します。
  • 上から下に向かって、分離レベルはますます高くなっています。
  • Serializable はパフォーマンスが最も低く、データ セキュリティが最も高く、Read uncommitted はパフォーマンスが最も高く、データ セキュリティが最も低くなります。

トランザクション分離レベルの表示

SELECT @@TRANSACTION_ISOLATION;

トランザクション分離レベルを設定する

SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };
SESSION はセッション レベルで、現在のセッションに対してのみ有効であることを意味し、GLOBAL はすべてのセッションに対して有効であることを意味します。

デモ

ここでは、2 つのクライアントを開いて表示します。これは 2 つのトランザクションに相当します。

コミットされていない読み取り

最初のクライアント入力を開きます

set session transaction isolation level read uncommitted;
start transaction;
select * from account;

次に、2 番目のクライアントを開いて次のように入力します。

start transaciton;
 update account set money = money - 1000 where name = '张三';

このとき、最初のクライアントで再度選択すると、データが変更されていることがわかります。
つまり、2番目のトランザクションがコミットしていないデータを最初のトランザクションが読み取っていることがわかります。ダーティリードの問題。
最後に、トランザクションを送信します ( commit )。その後、最初のクライアント選択を開いて、データが変更されたこと、つまり反復不可能な読み取りの問題が存在することを確認します。
このとき、2 番目のクライアントでデータ挿入が実行されると、最初のクライアントもデータ挿入を読み取ることになり、同じデータ ID を挿入する場合にファントム読み取りの問題が発生します。

コミットされた読み取り

前の実行シーケンスは上記と同じです。
最後に、2 番目のクライアントがトランザクションを送信した後、最初のクライアント選択でデータが変更されたことがわかります。これは反復不可能読み取りの問題です。
つまり、最初のクライアントのトランザクションは送信されたデータを読み取るだけであり、2 番目のクライアントがトランザクションをコミットする前は、最初のクライアントは 2 番目のクライアントによってデータベースに対して行われた更新操作を読み取ることができません。
この時点で、データが 2 番目のクライアントに挿入されて送信された場合、最初のクライアントはすでに変更を認識しているため、同じデータ ID を挿入するときにファントム読み取りの問題も発生します。

反復可能な読み取り

最初のクライアント入力を開きます

set session transaction isolation level repeatable read;
start transaction;
select * from account;

次に、2 番目のクライアントを開いて次のように入力します。

start transaciton;
 update account set money = money - 1000 where name = '张三';
 commit;

このとき、最初のクライアントがselectに入ると、2番目のクライアントのトランザクションによってデータが変更されていないことがわかり、最初のクライアントのトランザクションがコミット(commit)された後、トランザクションの変更を読み取ることができます。もう一度 select を入力すると、反復不可能な読み取りの問題は発生しません。
しかし、2 番目のクライアントがデータを挿入して送信した後、最初のクライアントはデータを見つけることができませんが、データの挿入時にエラーが報告されます。これは幻想のようなもの、つまりファントム読み取りの問題です。

シリアル化可能

ここではデータの挿入についてのみ説明します。
最初のクライアント入力を開きます。

set session transaction isolation level repeatable read;
start transaction;
select * from account;

次に、2 番目のクライアントを開いて次のように入力します。

start transaciton;
insert account(id) values(4);

カーソルが点滅し続けていることがわかります。これは、
ここに画像の説明を挿入最初のトランザクションが送信されるのを待っているため (優先度が低い)、プログラムがブロックされていることを示します。最初のトランザクションが送信されると、プログラムが実行を継続していることがわかります。ファントムリーディングの問題を回避します。
これはシリアル化の特性であり、一度に 1 つのトランザクションでしか操作できず、1 つのトランザクションがコミットされた後でのみ次のトランザクションが動作し続けることができます。

おすすめ

転載: blog.csdn.net/qq_49030008/article/details/126711094