1. 程式人生 > 其它 >題解 CF1361B Johnny and Grandmaster

題解 CF1361B Johnny and Grandmaster

MySQL總結

目錄

SQL

1、什麼是SQL?

Structured Query Language:結構化查詢語言

其實就是定義了操作所有關係型資料庫的規則。每一種資料庫操作的方式存在不一樣的地方,稱為“方言”。

2、SQL通用語法
  • SQL語句可以單行或多行書寫,以分號結尾。

  • 可使用空格和縮排來增強語句的可讀性。

  • MySQL資料庫的SQL語句不區分大小寫,關鍵字建議使用大寫。

  • 3種註釋:

    • 單行註釋:-- 註釋內容 或 # 註釋內容(mysql特有)

    • 多行註釋: /* 註釋 */

3、SQL分類
  • DDL(Data Definition Language)資料定義語言

    用來定義資料庫物件:資料庫,表,列等。關鍵字:create,drop,alter等

  • DML(Data Manipulation Language)資料操縱語言

    用來對資料庫中表的資料進行增刪改。關鍵字:insert.delete,update等

  • DQL(Data Query Language)資料查詢語言

    用來查詢資料庫中表的記錄(資料)。關鍵字:select,where等

  • DCL(Data Control Language)資料控制語言

    用來定義資料庫的訪問許可權和安全級別,及建立使用者。關鍵字:GRANT,REVOKE等

DDL:操作資料庫、表
3.1 操作資料庫:CRUD
  • C(Create):建立

    • 建立資料庫

       create database 資料庫名稱;
      
    • 建立資料庫,判斷不存在,再建立:

       create database if not exists 資料庫名稱;
      
    • 建立資料庫,並指定字符集:

       create database 資料庫名稱 character set 字符集名;
      
    • 練習:建立db4資料庫,判斷是否存在,並指定字符集為gbk

       create database if not exists db4 character set gbk; 
      
  • R(Retrieve):查詢

    • 查詢所有資料的名稱:

      show databases;
      
    • 查詢某個資料庫的字符集:查詢某個資料庫的建立語句

      show create database 資料庫名稱; 
      
  • U(Update):修改

    • 修改資料庫的字符集:

       alter database db4 character set utf8;
      
  • D(Delete):刪除

    • 刪除資料庫:

       drop database 資料庫名稱;
      
    • 判斷資料庫存在,存在再刪除:

      drop database if exists 資料庫名稱;
      
  • 使用資料庫

    use 資料庫名稱;
    
  • 查詢當前正在使用的資料庫名稱

     select database();
    
3.2 操作表 CRUD
  • C(Create):建立

    --  語法:
    create table 表名(
    
       列名1 資料型別1,
    
       列名2 資料型別2,
    
       ......
    
       列名3 資料型別n
    );
    
    --- 注意:最後一列,不需要加逗號
    
  • 資料型別:

    • int:整數型別

      age int,

    • double:小數型別

      score double(5,2)

    • date:日期,只包含年月日,yyyy-MM-dd

    • datetime:日期,包含年月日時分秒 yyyy-MM-dd HH:mm:ss

    • timestamp:時間戳型別 包含年月日時分秒 yyyy-MM-dd HH:mm:ss

    • varchar:字串

      name varchar(20):姓名最大20字元

  • 練習:建立表

    create table student(
         id int,
         name varchar(32),
         age int,
         score double(4,1),
         birthday date,
         insert_time timestamp
    );
    
  • 複製表

    create table if not exists 表名 like 被複制的表名;
    create table if not exists 表名 as select語句
    
  • R(Retrieve):查詢

    • 查詢某個資料庫中所有的表名稱

      show tables;
      
    • 查詢表結構

      desc 表名;
      show create table 表名;
      
  • U(Update):修改

    • 修改表名

       alter table 表名 rename to 新表名;
      
    • 修改表的字符集

      alter table 表名 character set 字符集名稱;
      
    • 檢視某張表的字符集

      show create table 表名;
      
    • 新增一列

      alter table 表名 add 列名 資料型別;
      
    • 修改列名稱 型別

      alter table 表名 change 列名 新列名 新資料型別;
      
      alter table 表名 modify 列名 新資料型別;
      
    • 刪除列

       alter table 表名 drop 列名;
      
  • D(Delete):刪除

    drop table 表名;
    
    drop table if exists 表名;
    
DML:增刪改表中資料
1.新增資料:
  • 語法:

    insert into 表名(列名1,列名2,列名3,......,列名n) values(值1,值2,......值n);
    
  • 注意:

    1.列名和值要一一對應;

    2.如果表名後,不定義列名,則預設給所有列新增值

    insert into 表名 values(值1,值2,值3,......值n);

    3.除了數字型別,其他型別需要使用引號(單雙都可以)引起來

2.刪除資料:
  • 語法:

    delete from 表名 [where條件]
    
  • 注意:

    1.如果不加條件,則刪除表中所有記錄

    2.如果要刪除所有記錄

    delete from 表名; --不推薦使用。有多少條記錄就會執行多少次刪除操作

    truncate table 表名; --推薦使用,效率高 先刪除表,然後再建立一張一樣的表。

3.修改資料:
  • 語法:

    update 表名 set 列名1 = 值1,列名2 = 值2,......[where條件];
    
  • 注意:

    1.如果不加任何條件,則會將表中所有記錄全部修改。

DQL:查詢表中的記錄
1.語法:
select
   欄位列表
from
   表名列表
where
   條件列表
group by
   分組欄位
having
   分組之後的條件
order by
   排序
limit
   分頁限定
2.基礎查詢
  • 1.多個欄位的查詢

    select 欄位名1,欄位名2... from 表名;
    

    注意:如果查詢所有欄位,則可以使用*來替代欄位列表。

  • 2.去除重複:distinct

    ​ 一般跟在selecth後,欄位前

  • 3.計算列

    一般可以使用四則運算計算一些列值。(一般只會進行數值型的計算)

    null參與的運算,計算結果都為null

  • 4.起別名:

    as

    as也可以省略

3.條件查詢
  • 1.where子句後跟條件

  • 2.運算子

    >、<、<=、>=、=、
    <> 不等於
    <=>等於
    
    BETWEEN...AND
    
    IN(集合)
    
    LIKE:模糊查詢
    	   _:單個任意字元,例如:like '張_',查詢姓張的且是兩個字的人
    	   					:like '張__',查詢姓張的且是三個字的人
    	   %:多個任意字元,例如:like '張%',查詢姓張的所有人
    IS NULL
    
    and
    
    or
    
    not   
    
  • 示例

    SELECT * FROM student WHERE age > 20;
    
    SELECT * FROM student WHERE age >= 20;
    
    SELECT * FROM student WHERE age =20;
    
    SELECT * FROM student WHERE age != 20;
    
    SELECT * FROM student WHERE age <> 20;
     
    SELECT * FROM student WHERE age >=20 && age <=30;
    
    SELECT * FROM student WHERE age >=20 AND age <=30;
    
    SELECT * FROM student WHERE age BETWEEN 20 AND 30;
    
    SELECT * FROM student WHERE age=22 OR age=20 OR age=25;
    
    SELECT * FROM student WHERE age IN(22,18,55);
    
    SELECT * FROM student WHERE english IS NOT NULL;
    
    SELECT * FROM student WHERE NAME LIKE '馬%';
    
    SELECT * FROM student WHERE NAME LIKE "_化%";
    
    SELECT * FROM student WHERE NAME LIKE "___";
    
    SELECT * FROM student WHERE NAME LIKE "%景%" 
    
4.排序查詢
  • 語法:order by 子句

    order by 排序欄位1 排序方式1,排序欄位2 排序方式2...

  • 排序方式:

    • ASC:升序,預設的

    • DESC:降序

  • 注意:

    如果又多個排序條件,則當前邊的條件值一樣時,才會判斷第二條件

5.聚合函式

將一列資料作為一個整體,進行縱向的計算

注意:聚合函式的計算,排除null值(可使用ifnull函式)

  • count:計算個數

    一般選擇非空的列:主鍵

    例:SELECT COUNT(NAME) FROM student;

  • max:計算最大值

    例:SELECT MAX(math) FROM student;

  • min:計算最小值

    例:SELECT MIN(math) FROM student;

  • sum:計算和

    例:SELECT SUM(english) FROM student;

  • avg:計算平均值

    例:SELECT AVG(math) FROM student;

6.分組查詢
  • 語法:group by 分組欄位

    • 例如:SId = 01,CId='語文',SId = 01,CId = '數學'

      ​ 按照SId分組,只能分成一組;

      ​ 按照CId分組,分為兩組

  • 注意:

    1.分組之後查詢的欄位:分組欄位、聚合函式

    2.where和having的區別?

    ​ where在分組之前進行限定,如果不滿足條件,則不參與分組。

    ​ having在分組之後進行限定,如果不滿足結果,則不會被查詢出來

    ​ where後不可以跟聚合函式,having可以進行聚合函式的判斷

  • 例:

    • 按照性別分組,分別查詢男、女同學的平均分

      SELECT sex,AVG(math) FROM student GROUP BY sex;

    • 按照性別分組,分別查詢男、女同學的平均分,人數

      SELECT sex,AVG(math),COUNT(id) FROM student GROUP BY sex;
      
    • 按照性別分組,分別查詢男、女同學的平均分,人數 要求:分數不低於70分的人,不參與分組。

      SELECT sex,AVG(math),COUNT(id) FROM student WHERE math>70  GROUP BY sex;
      
    • 按照性別分組,分別查詢男、女同學的平均分,人數 要求:分數不低於70分的人,不參與分組,分組之後,人數大於2人。

      SELECT sex,AVG(math),COUNT(id) FROM student WHERE math>70  GROUP BY sex HAVING COUNT(id)>2;
      
7.分頁查詢:
  • 語法:limit 開始的索引,每頁查詢的條數

  • 公式:開始的索引 = (當前的頁碼 -1) * 每頁顯示的條數

    查詢顯示第x頁的記錄,每頁有y條記錄:
    		limit  (x-1)*y , y;
    
  • 例:

    • 只顯示前10條記錄

      SELECT * FROM student LIMIT 10;
      
    • 每頁顯示8條記錄

      SELECT * FROM student LIMIT 0,8; -------第一頁
      
      SELECT * FROM student LIMIT 8,8; -------第二頁
      
      SELECT * FROM student LIMIT 16,8; ------第三頁
      
      SELECT * FROM student LIMIT 24,8; ------第四頁
      
DCL:管理使用者,授權
1.管理使用者
  • 新增使用者:

    • 關閉密碼複雜驗證

      set global validate_password_policy=0;
      set global validate_password_length=1;
      
    • 語法:

      CREATE USER '使用者名稱'@'主機名' IDENTIFIED BY '密碼';
      
      給使用者開放遠端登入(在其他操作軟體上可以連線該使用者)
      CREATE USER '使用者名稱'@'%' IDENTIFIED BY '密碼';
      
    • 例子:

      CREATE USER 'zhangsan'@'localhost' IDENTIFIED BY '123';
      
      CREATE USER 'lisi'@'%' IDENTIFIED BY '123';
      
  • 刪除使用者:

    • 語法:

      DROP USER '使用者名稱'@'主機名';
      
    • 例子:

      DROP USER 'zhangsan'@'localhost';
      
  • 修改使用者密碼:

    • 語法:

      -- 老版本的寫法 
      -- UPDATE USER SET PASSWORD = PASSWORD('新密碼') WHERE USER = '使用者名稱';
      
      -- 新版本
      -- SET PASSWORD FOR '使用者名稱'@'主機名' = PASSWORD('新密碼');
      
    • 例子:

      set password for 'lisi'@'%' = password('234567');
      
  • 查詢使用者:

    • 1.切換到mysql資料庫

      USE mysql;
      
    • 2.查詢user表

      select * from user;
      

      萬用字元: % 表示可以在任意主機使用使用者登入資料庫

2.許可權管理:
  • 查詢許可權:show ...for...

    • 語法:

       SHOW GRANTS FOR '使用者名稱'@'主機名';
      
       SHOW GRANTS FOR 'lisi'@'%';
      
  • 授予許可權:grant...on...

    • 語法:

      with grant option 表示帶上授權的功能

      指定許可權授權給使用者
      grant 許可權列表 on 資料庫名.表名 to '使用者名稱'@'主機名' [with grant option];
      
      將所有許可權授權給使用者
      GRANT ALL  ON *.* TO '使用者名稱'@'主機名';
      
      指定某一個庫中的表的所有操作許可權授權給使用者
      grant all privileges on 資料庫名.表名 to '使用者名稱'@'主機名';
      
      授權之後,需要重新整理一下許可權:
      flush privileges;
      
    • 例子:

      -- 將表db3.account的SELECT ,DELETE, UPDATE許可權授予使用者'lisi'@'%'
      GRANT SELECT ,DELETE, UPDATE  ON db3.account TO 'lisi'@'%';
      
      -- 給zhangsan使用者所有許可權  
      GRANT ALL  ON *.* TO 'zhangsan'@'localhost'
      
      建立超級使用者:
      CREATE USER '使用者名稱'@'%' IDENTIFIED BY '密碼';
      GRANT ALL  ON *.* TO '使用者名稱'@'%' with grant option
      
  • 撤銷許可權:

    • 語法:

      revoke 許可權列表 on 資料庫名.表名 from '使用者名稱'@'主機名';
      
    • 例子:

      -- 將使用者'lisi'@'%'對於表db3.account的更新許可權撤銷
      REVOKE UPDATE ON db3.account FROM 'lisi'@'%';
      
      -- 給lisi使用者撤銷所有許可權
      REVOKE ALL ON *.* FROM 'lisi'@'%';
      

約束

概念:對錶中的資料進行限定,保證資料的正確性、有效性和完整性。

  • 分類:

    • 1.主鍵約束:primary key-------------主鍵保證唯一性

    • 2.非空約束:not null

    • 3.唯一約束:unique---------------------想保證唯一,又不想成為主鍵

    • 4.外來鍵約束:foreign key

非空約束:not null,值不能為null
  • 1.建立表時新增約束

    CREATE TABLE stu(
       id INT,
       NAME VARCHAR(20) NOT NULL  -- name為非空
    );
    

    舉例:

    向表格中新增資料:
    insert into  stu values(1,'張三');
    insert into  stu values(2,'李四');
    
    單獨向表裡新增id,會報錯
    因為新增id時,未新增name,但是name指定為非空了,不新增就會報錯
    insert into  stu (id) values(3);------報錯
    
    為了不讓其報錯
    要麼新增name
    insert into  stu values(3,'王二');
    要麼建立表格的時候,新增預設值(新增預設值後,再單獨新增id,就不會報錯了)
    NAME VARCHAR(20) NOT NULL default ''
    
  • 2.建立表完後,如果創表的時候未新增非空約束,可以指定給欄位新增非空約束

    實際上就是修改欄位為非空

    ALTER TABLE stu MODIFY NAME VARCHAR(20) NOT NULL;
    
  • 3.刪除name的非空約束,實際上就是修改欄位,將非空去掉

    ALTER TABLE stu MODIFY NAME VARCHAR(25);
    
唯一約束:unique,值不能重複
  • 1.建立表時新增唯一約束

    CREATE TABLE stu(
     id INT,
     phone_num VARCHAR(20) UNIQUE --添加了唯一的約束後,如果電話號碼有重複的,執行時會報錯
    );
    

    注意mysql中,唯一約束限定的列的值,不可以有重複的值,但可以有多個null

  • 2.刪除唯一約束

    -- alter table stu modify phone_num varchar(20);  -- 刪不掉
    
    ALTER TABLE stu DROP INDEX phone_num;
    
  • 3.在建立表後,新增唯一約束

    ALTER TABLE stu MODIFY phone_num VARCHAR(20) UNIQUE;
    
主鍵約束:primary key
  • 1.注意:

    含義:非空且唯一

    主鍵就是表中記錄的唯一標識

  • 2.在建立表時,新增主鍵約束

    CREATE TABLE stu (
       id INT PRIMARY KEY,  -- 給id新增主鍵約束
       NAME VARCHAR(20)
    );
    
  • 3.刪除主鍵

    ALTER TABLE stu DROP PRIMARY KEY; -- 去除主鍵
    
    alter table stu modify id int;  -- 移除not null的約束
    
  • 4.建立完表後,新增主鍵

    ALTER TABLE stu MODIFY id INT PRIMARY KEY;  
    
  • 5.自動增長:

    • 1.概念:如果某一列時數值型別的,使用auto_increment可以來完成值的自動增長

    • 2.在建立表時,新增主鍵約束,並且完成主鍵自動增長

      CREATE TABLE stu(
          id INT PRIMARY KEY AUTO_INCREMENT,  -- 給id新增主鍵約束  並完成主鍵自動增長
          NAME VARCHAR(20)
      );
      

      舉例:

      CREATE TABLE stu(
          id INT PRIMARY KEY AUTO_INCREMENT,  -- 給id新增主鍵約束,並完成主鍵自動增長
          NAME VARCHAR(20)
      );
      insert into stu (name) values('張三');
      insert into stu (name) values('李四');
      insert into stu (name) values('王二');
      
      雖然我們未新增id,但執行不會報錯,而且資料還會新增進表格裡,id在表格裡為1、2、3
      因為我們給id添加了主鍵約束和自動增長,id的值,系統會自動從1開始往上增長
      
      當我們刪除了'王二',那麼id=3也會被刪除,若是再次將'王二'新增進去,那麼id會從4開始
      
    • 3.刪除自動增長

      ALTER TABLE stu MODIFY id INT;
      
    • 4.新增自動增長

      ALTER TABLE stu MODIFY id INT AUTO_INCREMENT;
      
外來鍵約束:foreign key,讓表與表產生關係,從而保證資料的正確性。
  • 1.在建立表時,可以新增外來鍵

    create table 表名(
    
       ...
    
       指定外來鍵列
    
       constraint 外來鍵名稱 foreign key (外來鍵列名稱) references 主表名稱(主表列名稱) 
    
    );
    
  • 2.刪除外來鍵

    ALTER TABLE 表名 DROP FOREIGN KEY 外來鍵名稱;
    
  • 3.建立表之後,新增外來鍵

    ALTER TABLE 表名 ADD CONSTRAINT 外來鍵名稱 FOREIGN KEY(外來鍵列名稱) REFERENCES 主表名稱(主表列名稱) ;
    
  • 4.級聯操作

    • 新增級聯操作

      ALTER TABLE 表名 ADD CONSTRAINT 外來鍵名稱 FOREIGN KEY(外來鍵列名稱) REFERENCES 主表名稱(主表列名稱) ON UPDATE CASCADE ON DELETE CASCADE;
      
    • 分類:

      級聯更新:ON UPDATE CASCADE
      
      級聯刪除:ON DELETE CASCADE
      

資料庫的設計

1.多表之間的關係
  • 一對一(瞭解)

    如:人和身份證

    分析:一個人只有一個身份證,一個身份證只能對應一個人

  • 一對多(多對一)

    如:部門和員工

    分析:一個部門有多個員工,一個員工只能對應一個部門

  • 多對多

    如:學生和課程

    分析:一個學生可以選擇很多門課程,一個課程也可以被很多學生選擇

2.實現關係
  • 一對多(多對一)

    如:部門和員工

    實現方式:在多的一方建立外來鍵,指向一另一方的主鍵。

  • 多對多

    如:學生和課程

    實現方式:多對多關係實現需要藉助第三張中間表。中間表至少包含兩個欄位,

    這兩個欄位作為第三張表的外來鍵,分別指向兩張表的主鍵。

  • 一對一(瞭解)

    如:人和身份證

    實現方式:一對一關係實現,可以在任意一方新增唯一外來鍵指向另一方的主鍵。

3.資料庫設計的正規化

設計資料庫時,需要遵循的一些規範。要遵循後邊的正規化要求,必須先遵循前邊的所有正規化要求。

基本表及其欄位之間的關係, 應儘量滿足第三正規化。

但是,滿足第三正規化的資料庫設計,往往不是最好的設計。

為了提高資料庫的執行效率,常常需要降低正規化標準:適當增加冗餘,達到以空間換時間的目的。

在實際開發中最為常見的設計正規化有三個:

  • 1.第一正規化(確保每列保持原子性)

第一正規化是最基本的正規化。如果資料庫表中的所有欄位值都是不可分解的原子值,就說明該資料庫表滿足了第一正規化。

第一正規化的合理遵循需要根據系統的實際需求來定。比如某些資料庫系統中需要用到“地址”這個屬性,本來直接將“地址”屬性設計成一個數據庫表的欄位就行。但是如果系統經常會訪問“地址”屬性中的“城市”部分,那麼就非要將“地址”這個屬性重新拆分為省份、城市、詳細地址等多個部分進行儲存,這樣在對地址中某一部分操作的時候將非常方便。這樣設計才算滿足了資料庫的第一正規化,如下表所示。

上表所示的使用者資訊遵循了第一正規化的要求,這樣在對使用者使用城市進行分類的時候就非常方便,也提高了資料庫的效能。

  • 2.第二正規化(確保表中的每列都和主鍵相關)

第二正規化在第一正規化的基礎之上更進一層。第二正規化需要確保資料庫表中的每一列都和主鍵相關,而不能只與主鍵的某一部分相關(主要針對聯合主鍵而言)。也就是說在一個數據庫表中,一個表中只能儲存一種資料,不可以把多種資料儲存在同一張資料庫表中。

比如要設計一個訂單資訊表,因為訂單中可能會有多種商品,所以要將訂單編號和商品編號作為資料庫表的聯合主鍵,如下表所示。

訂單資訊表

這樣就產生一個問題:這個表中是以訂單編號和商品編號作為聯合主鍵。這樣在該表中商品名稱、單位、商品價格等資訊不與該表的主鍵相關,而僅僅是與商品編號相關。所以在這裡違反了第二正規化的設計原則。

而如果把這個訂單資訊表進行拆分,把商品資訊分離到另一個表中,把訂單專案表也分離到另一個表中,就非常完美了。如下所示。

這樣設計,在很大程度上減小了資料庫的冗餘。如果要獲取訂單的商品資訊,使用商品編號到商品資訊表中查詢即可。

  • 3.第三正規化(確保每列都和主鍵列直接相關,而不是間接相關)

第三正規化需要確保資料表中的每一列資料都和主鍵直接相關,而不能間接相關。

比如在設計一個訂單資料表的時候,可以將客戶編號作為一個外來鍵和訂單表建立相應的關係。而不可以在訂單表中新增關於客戶其它資訊(比如姓名、所屬公司等)的欄位。如下面這兩個表所示的設計就是一個滿足第三正規化的資料庫表。

這樣在查詢訂單資訊的時候,就可以使用客戶編號來引用客戶資訊表中的記錄,也不必在訂單資訊表中多次輸入客戶資訊的內容,減小了資料冗餘。

多表查詢

  • 準備sql:

    -- 建立部門表
    CREATE TABLE dept(
       id INT PRIMARY KEY auto_increment,
       NAME VARCHAR(20)
    );
    -- 插入資料
    INSERT INTO dept(NAME) VALUES ('開發部'),('市場部'),('財務部'); 
    
    
    -- 建立員工表
    CREATE TABLE emp(
       id INT PRIMARY KEY auto_increment,
       NAME VARCHAR(10),
       gender CHAR(1),  -- 性別
       salary DOUBLE,  -- 工資
       join_date DATE, -- 入職日期
       dept_id INT,
       FOREIGN KEY(dept_id) REFERENCES dept(id)  -- 外來鍵,關聯部門表的主鍵
    );
    -- 插入資料
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) 
    VALUES 	('孫悟空','男',7200,'2013-02-24',1),
     		('豬八戒','男',3600,'2010-12-02',2),
     		('唐僧','男',9000,'2008-08-08',2),
     		('白骨精','女',5000,'2015-10-07',3),
     		('蜘蛛精','女',4500,'2011-03-14',1);
    
  • 笛卡爾積:

    有兩個集合A,B 取這兩個集合的所有組成情況。

    例如:

    A:(a,b,c)

    B:(1,2,3)

    A與B作笛卡爾積---> a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3

多表查詢會出現一個笛卡爾積的情況

要完成多表查詢,需要消除無用的資料,避免出現笛卡爾積

  • 1.內連線查詢:

    • 隱式內連線:使用where條件消除無用資料

      --  查詢所有員工資訊和對應的部門資訊
      SELECT * FROM emp,dept WHERE emp.dept_id=dept.id;
      
      -- 查詢員工表的名稱,性別。部門表的名稱
      SELECT 	emp.`NAME`
      		,emp.gender
      		,dept.`NAME` 
      FROM emp,dept WHERE emp.dept_id=dept.id;
      
      -- 或者
      SELECT	t1.`NAME`
      		,t1.gender
      		,t2.`NAME`
      FROM emp t1,dept t2
      WHERE t1.dept_id=t2.id;
      
    • 顯式內連線:

      --語法:select 欄位列表 from 表名1 [inner] join 表名2 on 條件
      
      SELECT * FROM emp INNER JOIN dept ON emp.dept_id=dept.id;
      
  • 2.外連線查詢:

    • 左外連線:

      --語法:select 欄位列表 from 表1 left [outer] join 表2 on 條件;
      
      --查詢的是左表所有資料以及其交集部分。 
      
    • 右外連線:

      --語法:select 欄位列表 from 表1 right [outer] join 表2 on 條件;
      
      --查詢的是右表所有資料以及其交集部分。
      
  • 3.子查詢:

    查詢中巢狀查詢,稱巢狀查詢為子查詢。

    -- 查詢工資最高的員工資訊
    
    -- 1.查詢最高的工資是多少 9000
    SELECT MAX(salary) FROM emp;
     
    -- 2.查詢員工資訊,並且工資等於9000的
    SELECT * FROM emp WHERE emp.salary=9000;
    
    -- 一條sql就完成這個操作
    SELECT * FROM emp WHERE emp.salary = (SELECT MAX(salary) FROM emp);
    

    子查詢不同情況:

    • 子查詢的結果是單行單列的:

      --子查詢可以作為條件,使用運算子去判斷。  運算子:> >= < <= =
      
      --查詢員工工資小於平均工資的人
      SELECT * FROM emp WHERE emp.salary < (SELECT AVG(salary) FROM emp);
      
    • 子查詢的結果是多行單列的:

      --子查詢可以作為集合,使用in、not int
      
      --查詢財務部和市場部所有員工資訊
      SELECT id FROM dept WHERE `NAME`='財務部' OR `NAME`='市場部';
      SELECT * FROM emp WHERE dept_id=3 OR dept_id=2;
      
      --使用子查詢
      SELECT * FROM emp WHERE dept_id in (SELECT id FROM dept WHERE `NAME`='財務部' OR `NAME`='市場部');
      
    • 子查詢的結果是多行多列的

      --子查詢可以作為一張虛擬表參與查詢
      --查詢員工入職日期是2011-11-11日之後的員工資訊和部門資訊
      
      -- 子查詢
      select * from dept t1 (select * from emp where emp.join_date > '2011-11-11') t2 where t1.id = t2.dept_id;
      
      --普通內連線查詢
      select * from emp t1,dept t2 where t1.dept_id = t2.id and t1.join_date > '2011-11-11'  
      
  • 表的拼接:聯合查詢-結果合併

    拼接時兩張表的結構必須完全一致(欄位的型別完全一致)

    • A表 union all B表:拼接後的有記錄總和 = A表記錄數 + B表記錄數
    • union 對資料進行去重的時候,select後要指定欄位,如果用*,無法去重

事務

1.事務的基本介紹

如果一個包含多個步驟的業務操作,被事務管理,那麼這些操作要麼同時成功,要麼同時失敗。

  • 操作:

    1.開啟事務:start transaction;

    2.回滾:rollback;

    3.提交:commit;

  • 例子:

    --建表
    CREATE TABLE account(
       id INT PRIMARY KEY AUTO_INCREMENT,
       NAME VARCHAR(10),
       balance DOUBLE
    );
    
    --插入資料
    INSERT INTO account(NAME,balance) VALUES ('zhangsan',1000),('lisi',1000);
    
    SELECT * FROM account;
    
    -- 張三給李四轉賬500元
    -- 0.手動開啟事務
    START TRANSACTION;
    -- 1.張三賬戶 -500
    UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan';
    -- 2.李四賬戶 + 500
    UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi';
    
    -- 開啟事物後就需要手動進行提交
    
    -- 發現沒有問題了,提交事務
    COMMIT;
    -- 發現出問題了,回滾事務
    ROLLBACK;
    
  • MySQL資料庫中事務預設自動提交,事務提交的兩種方式:

    • 自動提交:

      mysql就是自動提交的

      例如:一條DML(增刪改)語句會自動提交一次事務。

    • 手動提交:

      需要先開啟事務,再提交

      Oracle資料庫預設是手動提交事務

  • 修改事務的預設提交方式:

    --檢視事務的預設提交方式:
    SELECT @@autocommit; -- 1 代表自動提交  0 代表手動提交
    
    --修改預設提交方式:
    SET @@autocommit = 0;   
    
2.事務的四大特徵ACID
  • 原子性(Atomicity)

    原子性是指事務包含的所有操作要麼全部成功,要麼全部失敗回滾。

  • 一致性(Consistency)

    一致性是指事務必須使資料庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態。

    拿轉賬來說,假設使用者A和使用者B兩者的錢加起來一共是5000,那麼不管A和B之間如何轉賬,轉幾次賬,事務結束後兩個使用者的錢相加起來應該還得是5000,這就是事務的一致性。

  • 隔離性(Isolation)

    隔離性是當多個使用者併發訪問資料庫時,比如操作同一張表時,資料庫為每一個使用者開啟的事務,不能被其他事務的操作所幹擾,多個併發事務之間要相互隔離。

    即要達到這麼一種效果:對於任意兩個併發的事務T1和T2,在事務T1看來,T2要麼在T1開始之前就已經結束,要麼在T1結束之後才開始,這樣每個事務都感覺不到有其他事務在併發地執行。

  • 永續性(Durability)

    永續性是指一個事務一旦被提交了,那麼對資料庫中的資料的改變就是永久性的,即便是在資料庫系統遇到故障的情況下也不會丟失提交事務的操作。

    例如我們在使用JDBC操作資料庫時,在提交事務方法後,提示使用者事務操作完成,當我們程式執行完成直到看到提示後,就可以認定事務以及正確提交,即使這時候資料庫出現了問題,也必須要將我們的事務完全執行完成,否則就會造成我們看到提示事務處理完畢,但是資料庫因為故障而沒有執行事務的重大錯誤。

3.事務的隔離級別(瞭解)

多個事務之間隔離的,相互獨立的。但是如果多個事務操作同一批資料,則會引發一些問題,設定不同的隔離級別就可以解決這些問題。

存在問題:

  • 1.髒讀

    髒讀是指在一個事務處理過程裡讀取了另一個未提交的事務中的資料。

      當一個事務正在多次修改某個資料,而在這個事務中這多次的修改都還未提交,這時一個併發的事務來訪問該資料,就會造成兩個事務得到的資料不一致。例如:使用者A向用戶B轉賬100元,對應SQL命令如下

    update account set money=money+100 where name=’B’;  (此時A通知B)
    update account set money=money - 100 where name=’A’;
    

      當只執行第一條SQL時,A通知B檢視賬戶,B發現確實錢已到賬(此時即發生了髒讀),而之後無論第二條SQL是否執行,只要該事務不提交,則所有操作都將回滾,那麼當B以後再次檢視賬戶時就會發現錢其實並沒有轉。

  • 2.不可重複讀

    不可重複讀是指在對於資料庫中的某個資料,一個事務範圍內多次查詢卻返回了不同的資料值,這是由於在查詢間隔,被另一個事務修改並提交了。

      例如事務T1在讀取某一資料,而事務T2立馬修改了這個資料並且提交事務給資料庫,事務T1再次讀取該資料就得到了不同的結果,發生了不可重複讀。

      不可重複讀和髒讀的區別是,髒讀是某一事務讀取了另一個事務未提交的髒資料,而不可重複讀則是讀取了前一事務提交的資料。

      在某些情況下,不可重複讀並不是問題,比如我們多次查詢某個資料當然以最後查詢得到的結果為主。但在另一些情況下就有可能發生問題,例如對於同一個資料A和B依次查詢就可能不同,A和B就可能打起來了……

  • 3.虛讀(幻讀)

    幻讀是事務非獨立執行時發生的一種現象。例如事務T1對一個表中所有的行的某個資料項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行資料項,而這個資料項的數值還是為“1”並且提交給資料庫。而操作事務T1的使用者如果再檢視剛剛修改的資料,會發現還有一行沒有修改,其實這行是從事務T2中新增的,就好像產生幻覺一樣,這就是發生了幻讀。

      幻讀和不可重複讀都是讀取了另一條已經提交的事務(這點就髒讀不同),所不同的是不可重複讀查詢的都是同一個資料項,而幻讀針對的是一批資料整體(比如資料的個數)。

隔離級別

  • Serializable (序列化):可避免髒讀、不可重複讀、幻讀的發生。

  • Repeatable read (可重複讀)(預設):可避免髒讀、不可重複讀的發生。

  • Read committed (讀已提交):可避免髒讀的發生。

  • Read uncommitted (讀未提交):最低級別,任何情況都無法保證。

    注意:隔離級別從低到高到安全性越來越高,但是效率越來越低

    查詢資料庫隔離級別:

    select @@tx_isolation;

    資料庫設定隔離級別:

    set global transaction isolation level 級別字串;

JDBC連線

Maven依賴
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.49</version>
</dependency>
示例程式碼
package com.shujia;

import java.sql.*;

public class MySQLJDBCDemo {
    public static void main(String[] args) throws Exception {
        // 1、載入驅動
        Class.forName("com.mysql.jdbc.Driver");

        // 2、建立連線
        Connection conn = DriverManager.getConnection("jdbc:mysql://master:3306/db1?useSSL=false","root","123456");

        // 3、建立Statement

        Statement st = conn.createStatement();

        // 4、通過Statement執行SQL
        ResultSet rs = st.executeQuery("select * from student");

        // 5、遍歷ResultSet 獲取返回的記錄
        while (rs.next()) {
            int id = rs.getInt("id");
            String name = rs.getString("name");
            int age = rs.getInt("age");
            String gender = rs.getString("gender");
            String clazz = rs.getString("clazz");

            System.out.println(id+","+name+","+age+","+gender+","+clazz);
        }

        // 6、關閉連線
        st.close();
        conn.close();
    }
}

JDBC連線池

1、連線池是什麼?

連線池是建立和管理一個連線的緩衝池的技術,這些連線準備好被任何需要它們的執行緒使用。 連線池用於提高在資料庫上執行命令的效能。連線池本質上就是資料庫連線的快取。使用資料庫連線時,如果池中有一個可用,它將使用該連線而不是重新建立另一個新的連線 ,用完後不是關閉它,而是將其放回池中。

開啟和關閉資料庫連線可能看起來不是昂貴的費用,但它可以累加起來。假設建立連線需要5ms,執行查詢需要5ms(完全編號),50%的時間是建立連線。將此擴充套件到數千或數萬個請求,就會浪費了大量網路時間。

2、為什麼要用連線池?
  • 資源重用:由於資料庫連線得以重用,避免了頻繁建立,釋放連線引起的大量效能開銷。在減少系統消耗的基礎上,另一方面也增加了系統執行環境的平穩性。
  • 更快的系統反應速度:資料庫連線池在初始化過程中,往往已經建立了若干資料庫連線置於連線池中備用。此時連線的初始化工作均已完成。對於業務請求處理而言,直接利用現有可用連線避免了資料庫連線初始化和釋放過程的時間開銷,從而減少了系統的響應時間
  • 新的資源分配手段:對於多應用共享同一資料庫的系統而言,可在應用層通過資料庫連線池的配置實現某一應用最大可用資料庫連線數的限制避免某一應用獨佔所有的資料庫資源.
  • 統一的連線管理:避免資料庫連線洩露在較為完善的資料庫連線池實現中,可根據預先的佔用超時設定,強制回收被佔用連線,從而避免了常規資料庫連線操作中可能出現的資源洩露。
3、常見的資料庫連線池?
  • proxool

    更新時間截止2008年。速度可以,穩定性稍差,發較高的情況下會出錯。

  • c3p0

    太古老,程式碼及其複雜,不利於維護。貌似都比它強。

  • dbcp

    apache上的 java 連線池專案,也是 tomcat 使用的連線池元件。

  • druid

    alibaba出品的功能比較全面且擴充套件性較好的資料庫連線池,比較方便對jdbc介面進行監控跟蹤等

  • BoneCP

    2013年前最快的連線池專案。2013年後不再更新,心灰意冷。

  • HikariCP

    光連線池,目前被SpringBoot2官方推薦使用的資料庫連線池。

4、各資料庫連線池測試結論
  • 1:效能方面 HikariCP>Druid>tomcat-jdbc>dbcp>c3p0 。

    hikariCP的高效能得益於最大限度的避免鎖競爭。

  • 2:druid功能最為全面,sql攔截等功能,統計資料較為全面,具有良好的擴充套件性。

  • 3:綜合性能,擴充套件性等方面,可考慮使用druid或者hikariCP連線池。

  • 4:可開啟prepareStatement快取,對效能會有大概20%的提升。

5、HikariCP連線池的使用

官網地址:https://github.com/brettwooldridge/HikariCP

Maven依賴
<dependency>
   <groupId>com.zaxxer</groupId>
   <artifactId>HikariCP</artifactId>
   <version>4.0.3</version>
</dependency>
連線池初始化
// 配置
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://master:3306/db1");
config.setUsername("root");
config.setPassword("123456");
config.addDataSourceProperty("connectionTimeout", "1000"); // 連線超時:1秒
config.addDataSourceProperty("idleTimeout", "60000"); // 空閒超時:60秒
config.addDataSourceProperty("maximumPoolSize", "16"); // 最大連線數:16
config.addDataSourceProperty("useSSL", "false"); // 關閉使用SSL連線

// 建立連線池例項
DataSource ds = new HikariDataSource(config);
使用連線池
try (Connection conn = ds.getConnection()) { // 通過ds獲取連線
    ...
} // 在此“關閉”連線

索引

MySQL索引的建立對於MySQL的高效執行是很重要的,索引可以大大提高MySQL的檢索速度。

普通索引
建立索引
CREATE INDEX indexName ON table_name (column_name)
修改表結構(新增索引)
ALTER table tableName ADD INDEX indexName(columnName)
建立表的時候直接指定
CREATE TABLE mytable(  
 
ID INT NOT NULL,   
 
username VARCHAR(16) NOT NULL,  
 
INDEX [indexName] (username(length))  
 
);  
刪除索引的語法
DROP INDEX indexName] ON mytable; 

唯一索引

它與前面的普通索引類似,不同的就是:索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。它有以下幾種建立方式:

建立索引
CREATE UNIQUE INDEX indexName ON mytable(username(length)) 
修改表結構
ALTER table mytable ADD UNIQUE [indexName] (username(length))
建立表的時候直接指定
CREATE TABLE mytable(  
 
ID INT NOT NULL,   
 
username VARCHAR(16) NOT NULL,  
 
UNIQUE [indexName] (username(length))  
 
);  

使用ALTER 命令新增和刪除索引

有四種方式來新增資料表的索引:

  • ALTER TABLE tbl_name ADD PRIMARY KEY (column_list):

    該語句新增一個主鍵,這意味著索引值必須是唯一的,且不能為NULL,

  • ALTER TABLE tbl_name ADD UNIQUE index_name (column_list):

    這條語句建立索引的值必須是唯一的(除了NULL外,NULL可能會出現多次)。

  • ALTER TABLE tbl_name ADD INDEX index_name (column_list):

    新增普通索引,索引值可出現多次。

  • ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):

    該語句指定了索引為 FULLTEXT ,用於全文索引。

以下例項為在表中新增索引。

ALTER TABLE testalter_tbl ADD INDEX (c);

你還可以在 ALTER 命令中使用 DROP 子句來刪除索引。嘗試以下例項刪除索引:

ALTER TABLE testalter_tbl DROP INDEX c;

使用 ALTER 命令新增和刪除主鍵

主鍵作用於列上(可以一個列或多個列聯合主鍵),新增主鍵索引時,你需要確保該主鍵預設不為空(NOT NULL)。例項如下:

ALTER TABLE testalter_tbl MODIFY i INT NOT NULL;
ALTER TABLE testalter_tbl ADD PRIMARY KEY (i);

你也可以使用 ALTER 命令刪除主鍵:

ALTER TABLE testalter_tbl DROP PRIMARY KEY;

刪除主鍵時只需指定PRIMARY KEY,但在刪除索引時,你必須知道索引名。


顯示索引資訊

你可以使用 SHOW INDEX 命令來列出表中的相關的索引資訊。可以通過新增 \G 來格式化輸出資訊。

嘗試以下例項:

SHOW INDEX FROM table_name;

檢視

1、什麼是檢視?

​ 檢視是基於 SQL 語句的結果集的視覺化的表,即檢視是一個虛擬存在的表,可以包含表的全部或者部分記錄,也可以由一個表或者多個表來建立。使用檢視就可以不用看到資料表中的所有資料,而是隻想得到所需的資料。當我們建立一個檢視的時候,實際上是在資料庫裡執行了SELECT語句,SELECT語句包含了欄位名稱、函式、運算子,來給使用者顯示資料。使用檢視查詢可以使查詢資料相對安全,通過檢視可以隱藏一些敏感欄位和資料,從而只對使用者暴露安全資料。檢視查詢也更簡單高效,如果某個查詢結果出現的非常頻繁或經常拿這個查詢結果來做子查詢,將查詢定義成檢視可以使查詢更加便捷。

2.檢視建立及使用方法
  • 完整建立語法:

    CREATE
        [OR REPLACE]
        [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
        [DEFINER = user]
        [SQL SECURITY { DEFINER | INVOKER }]
        VIEW view_name [(column_list)]
        AS select_statement
        [WITH [CASCADED | LOCAL] CHECK OPTION]
    

    語法解讀:

    1)OR REPLACE:表示替換已有檢視,如果該檢視不存在,則CREATE OR REPLACE VIEW與CREATE VIEW相同。

    2)ALGORITHM:表示檢視選擇演算法,預設演算法是UNDEFINED(未定義的):MySQL自動選擇要使用的演算法 ;merge合併;temptable臨時表,一般該引數不顯式指定。

    3)DEFINER:指出誰是檢視的建立者或定義者,如果不指定該選項,則建立檢視的使用者就是定義者。

    4)SQL SECURITY:SQL安全性,預設為DEFINER。

    5)select_statement:表示select語句,可以從基表或其他檢視中進行選擇。

    6)WITH CHECK OPTION:表示檢視在更新時保證約束,預設是CASCADED。

  • 一般用法:

    CREATE VIEW 檢視名 AS SELECT 查詢語句;
    
  • 修改檢視

    ALTER VIEW 檢視名 AS SELECT 查詢語句;
    
  • 刪除檢視

    DROP VIEW 檢視名;
    
3、注意事項

要通過檢視更新基本表資料,必須保證檢視是可更新檢視,即可以在INSET、UPDATE或DELETE等語句當中使用它們。對於可更新的檢視,在檢視中的行和基表中的行之間必須具有一對一的關係。還有一些特定的其他結構,這類結構會使得檢視不可更新。

一般情況下不建議對檢視做DML操作

如果檢視包含下述結構中的任何一種,那麼它就是不可更新的:

  • 聚合函式;
  • DISTINCT關鍵字;
  • GROUP BY子句;
  • ORDER BY子句;
  • HAVING子句;
  • UNION運算子;
  • 位於選擇列表中的子查詢;
  • FROM子句中包含多個表;
  • SELECT語句中引用了不可更新檢視;
  • WHERE子句中的子查詢,引用FROM子句中的表;
  • ALGORITHM 選項指定為TEMPTABLE(使用臨時表總會使檢視成為不可更新的)
4、檢視的優點
  • 簡單:使用檢視的使用者完全不需要關心後面對應的表的結構、關聯條件和篩選條件,對使用者來說已經是過濾好的複合條件的結果集。

  • 安全:使用檢視的使用者只能訪問他們被允許查詢的結果集,對錶的許可權管理並不能限制到某個行某個列,但是通過檢視就可以簡單的實現。

  • 資料獨立:一旦檢視的結構確定了,可以遮蔽表結構變化對使用者的影響,源表增加列對檢視沒有影響;源表修改列名,則可以通過修改檢視來解決,不會造成對訪問者的影響。

總而言之,使用檢視的大部分情況是為了保障資料安全性,提高查詢效率。比如說我們經常用到幾個表的關聯結果,那麼我們就可以使用檢視來處理,或者說第三方程式需要呼叫我們的業務庫,可以按需建立檢視給第三方程式查詢