Mysql筆記

(六天玩轉mysql數據庫筆記)

insert into stu_info(name, score) values('劉', 10);


-- 查看所有字符集
show character set;

-- 查看服務器、客戶端默認字符集
show variables like 'character_set%';

-- set name = value 作用域是一次會話
set names gbk;

-- 數據類型(列類型)
-- 所謂數據類型,對數據進行統一分類,從系統的角度出發,爲了能夠使用
-- 統一的方式進行管理,更好的利用有限的空間

-- sql中將數據分成了三大類:數值類型、字符串類型、時間日期類型

--   數值型 : 整數型、小數型
  -- 整數型 :  5類
  tinyint : 迷你整形 1字節 256
  smallint : 小整形  2字節 65536
  mediumint: 中整型  3字節
  int : 標準整型     4字節
  bigint: 大整型     8字節

-- 創建 整型表
create table my_int(
int_tiny tinyint,
int_small smallint,
int_3 int,
int_4 bigint
)charset utf8;

-- 插入
insert into my_int values(100, 100, 100, 100);

-- 不合法的插入
insert into my_int values ('a', 'b', '199', 'f');
-- ERROR 1366 (HY000): Incorrect integer value: 'a' for column 'int_tiny' at row 1

-- 超出範圍
insert into my_int values (255, 100000, 100000000, 10000000);
-- ERROR 1264 (22003): Out of range value for column 'int_tiny' at row 1

-- SQL中的數值類型全都是默認有符號,分正負,有時候需要使用無符號類型
int unsigned

-- 增加無符號類型
alter table my_int add int_5 tinyint unsigned;

-- 插入數據
insert into my_int values (127,1, 1, 1, 255);

-- desc
mysql> desc my_int;
+-----------+---------------------+------+-----+---------+-------+
| Field     | Type                | Null | Key | Default | Extra |
+-----------+---------------------+------+-----+---------+-------+
| int_tiny  | tinyint(4)          | YES  |     | NULL    |       |
| int_small | smallint(6)         | YES  |     | NULL    |       |
| int_3     | int(11)             | YES  |     | NULL    |       |
| int_4     | bigint(20)          | YES  |     | NULL    |       |
| int_5     | tinyint(3) unsigned | YES  |     | NULL    |       |
+-----------+---------------------+------+-----+---------+-------+
-- 括號後面的()內有個數字,代表顯示寬度,數據最終顯示的位數,沒有特別的含義
-- 知識默認的告訴用戶可以顯示的形式而已

alter table my_int add int_6 tinyint(1) unsigned;
insert into my_int values (127,1, 1, 1, 255, 255);

-- 顯示寬度的意義:當數據不夠顯示寬度時,會自動讓數據編程對應的顯示寬度
-- 通常需要搭配一個前導0來補齊,零填充會導致數值自動轉換無符號

alter table my_int add int_7 tinyint(2) zerofill;

insert into my_int values (127,1, 1, 1, 255, 1, 1);

-- 零填充的意義(顯示寬度):保證數據格式


-- 小數型:帶有小數點,或者範圍超出整值類型
-- 浮點數:小數點浮動,精度有限,會丟失精度
-- 定點型:小數點固定,精度固定,不丟失精度

浮點型:
float : 單精度,四字節,精度範圍大約7位左右
double : 雙精度,八字節,精度範圍大約15位

-- 創建浮點表,直接float表示沒有小數部分,float(M,D),M表示總長度,D表示小數部分

create table my_float(
  f1 float,
  f2 float(10,2),
  f3 float (6, 2)
)charset utf8;

-- 插入數據
insert into my_float values (1000.10, 1000.10, 1000.10);
insert into my_float values (9999999999, 99999999.99, 9999.99);
insert into my_float values (3e38, 3.01e7, 1234.56);
insert into my_float values (1234567890, 12345678.90, 1234.56);

-- 浮點型,整數部分不能超過長度,小數部分可以(系統會自動四捨五入)

-- 超出長度插入數據

insert into my_float values(123456, 1234.12345678, 123.98765432);
insert into my_float values(123456, 1234.12, 1234.56);

-- 結果:浮點數一定會進行四捨五入(超出精度範圍):浮點數如果因爲系統進位導致整數
-- 部分超出指定長度,那麼系統也允許成立

--  定點型:絕對保證整數部分不會被四捨五入,不丟失精度,小數部分有可能
-- (理論上小數部分也不會丟失精度)
decimal 變長,大致是每9個數字,採用4個字節存儲,整數和小數分開計算
-- M最大是65,D最大是30

-- 創建定點整數表
create table my_decimal(
f1 float (10, 2),
d1 decimal (10, 2)
)charset utf8;

-- 定點數的整數部分一定不能超過長度(進位也不可以)
-- insert
insert into my_decimal values (12345678.90, 12345678.90); -- 有效
insert into my_decimal values (1234.123456, 1234.123456); -- 小數部分超出
-- Query OK, 1 row affected, 1 warning (0.03 sec)
-- 查看warning
show warnings;

-- 浮點數如果進位導致長度溢出是允許的,定點不允許
insert into my_decimal values (99999999.99, 99999999.99); -- 沒有問題
insert into my_decimal values (99999999.99, 99999999.999); -- 有問題

-- 如果需要粗略、級別大的用浮點  精確的 用定點

-- 時間日期類型
Datetime 格式是YYYY-mm-dd HH:ii:ss, 表示的範圍從1000到9999年,有0值
DATE 就是datetime中的date部分
TIME 時間段,指定的某個區間之間,時間到+時間
timestamp  時間戳,格式與datetime一致
year 年份,兩種形式

-- 創建時間日期表
create table my_table(
d1 datetime,
d2 date,
d3 time,
d4 timestamp ,
d5 year
)charset utf8;

-- 插入數據 : 注意:(1) time 可以爲負數 (2) year 可以用兩位數插入

insert into my_table values ('2015-9-28 11:50:54', '2015-9-28', '11:50:54', '2015-9-28 11:50:54', 2015);

-- 時間使用負數
insert into my_table values ('2015-9-28 11:50:54', '2015-9-28', '-11:50:54', '2015-9-28 11:50:54', 2015);
insert into my_table values ('2015-9-28 11:50:54', '2015-9-28', '-2 11:50:54', '2015-9-28 11:50:54', 2015);-- 過去兩天


-- year 可以使用兩位
insert into my_table values ('2015-9-28 11:50:54', '2015-9-28', '-11:50:54', '2015-9-28 11:50:54', 69);
insert into my_table values ('2015-9-28 11:50:54', '2015-9-28', '-11:50:54', '2015-9-28 11:50:54', 15);

-- 當條目更新時,timestamp 字段會自動更新


-- 字符串類型
-- 定長字符串 char,磁盤(二維表)在定義的時候,就已經確定了最終數據的存儲長度
char(L):L代表length,可以存儲的長度,單位字符,最大長度可以爲255
char(4):在UTF8下,需要4 * 3 = 12 個字節

-- 變長字符串: varchar(L), L表示字符長度,理論長度是65536個字符,但是會多出1到2個字節來存儲
-- 實際長度
varchar (10) 的確存了10個漢字,utf8環境 10 * 3 + 1 = 31

-- 當實際長度如果超過255,既不用定長,也不用變長,用文本字符串text

-- 如何選擇定長或變長字符串??
-- 定長的磁盤空間比較浪費,但是效率高:如果數據基本上確定長度都一樣,就使用定長
-- 如:身份證、電話號碼、手機號碼
-- 變長不浪費空間節省,但效率低:如果數據只能確定最大的長度,但具體長度有變化,如:姓名,家庭住址等

-- 文本字符串 如果數據量非常大,通常超過255個字符,就會使用文本字符串,文本字符串會根據存儲格式分類:
-- text 和 blob
text : 存文本(二進制文件通常存儲路徑)
blob : 存二進制數據(通常不用)

-- 枚舉:enum,事先將可能出現的結果都設計好,實際存儲的數據必須是規定好的數據中的一個
--- 枚舉使用方式:
-- 定義:enum(可能出現的元素列表)
-- 使用:存儲數據,只能存儲上面定義好的數據

-- 創建枚舉表
create table my_enum(
gender enum('男', '女', '保密')
)charset utf8;

-- 插入,枚舉作用之一,規範數據   作用二,節省存儲空間,枚舉通常有一個別名,單選框,
insert into my_enum values ('男'),('保密'); -- 有效數據
insert into my_enum values ('male');  -- 錯誤

------- ***************-----------------
在mysql中,系統也會自動轉換數據格式,枚舉原理是:在進行數據規範(定義)時,系統會自動建立一個數字
與枚舉元素的對應關係(關係放到日誌中),然後在進行數據插入的時候,系統自動將字符串轉換成相應
的數字存儲,然後在進行數據提取的時候,系統會自動將數值轉換成對應的字符串顯示。
-- 枚舉可以直接插入數值
insert into my_enum values (1), (2);

-- 創建集合表
create table my_set(
hobby set('籃球','排球','網球','足球')
)charset utf8;
-- 插入:既可以使用多個字符串,也可直接數值
insert into my_set values ('籃球,排球,網球');
insert into my_set values (3); -- 3 = 1 + 2 籃球加排球

-- 查看
select hobby + 0, hobby from my_set;
+-----------+----------------+
| hobby + 0 | hobby          |
+-----------+----------------+
|         3 | 籃球,排球      |
|         7 | 籃球,排球,網球 |
+-----------+----------------+
-- 每一位用01(有點像chmod裏面的權限設置),但是是反過來的,集合中的第一個在最終的數值裏是最低位
-- 集合的強大在於能夠規範數據和節省空間


-- mysql 記錄長度
mysql規定,任何一條長度不能超過65535個字節(varchar 永遠達不到理論值)
varchar 的實際存儲長度能達到多少呢?看字符集編碼。UTF8下,varchar 的實際在utf8 21844

--- 求出varchar在utf8和gbk下的實際最大值
create table my_varchar_max_utf8(
name varchar(65535)
)charset utf8;

-- ERROR 1074 (42000): Column length too big for column 'name' (max = 21845);
-- use BLOB or TEXT instead

create table my_varchar_max_gbk(
name varchar(65535)
)charset gbk;

-- ERROR 1074 (42000): Column length too big for column 'name' (max = 32767);
-- use BLOB or TEXT instead

-- 減小空間
create table my_varchar_max_utf8(
name varchar(21845) -- 21845 * 3 = 65535, 但是varchar還需要一個存儲長度的空間 2 字節
)charset utf8;

-- ERROR 1118 (42000): Row size too large. The maximum row size for the used table type,
--  not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs
-- 這樣是ok的
create table my_varchar_max_utf8(
name varchar(21844) -- 21844 * 3 =65532 + 2 = 65534
)charset utf8;

-- 補一個字節,從用戶角度看,補齊65535個字節
create table my_varchar_max_2(
int_1 tinyint,
name varchar(21844) -- 21844 * 3 =65532 + 2 = 65534
)charset utf8;
-- ERROR 1118 (42000): Row size too large. The maximum row size for the used table type,
-- not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs


-- ***  原因是:mysql記錄中,如果有任何一個字段允許爲空,那麼系統會自動從整個記錄
-- *** 中保留一個字節來存儲null,如果想釋放NULL所佔用的字節,必須保證所有
-- *** 字段不允許爲空。
-- 下面這樣是 ok 的
create table my_varchar_max_2(
int_1 tinyint not null,
name varchar(21844) not null -- 21844 * 3 =65532 + 2 = 65534
)charset utf8;

-- MySQL中text文本字符串不佔用記錄長度(額外存儲),但是text文本字符串也是屬於記錄的一部分:
-- 一定需要佔據記錄中的部分長度:10個字節(保存數據的地址以及長度)

-- text佔用是個字節
create table my_utftext(
name varchar (21841) not null, -- 21841 * 3 + 2 = 65523 + 2 = 65525
content text
)charset utf8;

-- ERROR 1118 (42000): Row size too large. The maximum row size for the used table type,
-- not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs

-- 減去一個null,就成功了
create table my_utftext(
name varchar (21841) not null, -- 21841 * 3 + 2 = 65523 + 2 = 65525
content text not null
)charset utf8;

-- *****列屬性
-- 真正約束字段的是數據類型,但是數據類型的約束很單一,需要有一些額外的約束,來更加保證數據的合法性
-- * null/not null,
  -- 兩個值,NULL(默認的)和NOT NULL(不爲空)
  -- 雖然默認的,數據庫基本都是字段爲空,但是實際上在真實開發的時候,
  -- 要儘可能保證所有的數據都不應該爲空,空數據沒有意義,空數據沒有辦法參加運算
-- * comment
  -- 描述,沒有實際意義,是用來專門描述字段的,會根據表創建語句保存,用來給數據庫管理員來進行了解的

  create table my_teacher(
  name varchar (20) not null comment '姓名',
  money decimal (10,2) not null comment '薪水'
  )charset utf8;

-- * default
  -- 默認值,某一種數據會經常性的出現某個具體的值,可以在一開始就指定好,在需要真實數據的時候,用戶可以自己設置

  create table my_default(
  name varchar(20) not null,
  age tinyint unsigned default 0,
  gender enum('男', '女', '保密') default '男'
  )charset utf8;
  insert into my_default values ('劉', default , default );
  insert into my_default(name) values ('劉');
-- * primary key
  -- 主鍵,一張表中只能有一個字段可以使用對應的主鍵,用來唯一的約束該字段裏面的數據,不能重複,主鍵默認不爲空
  -- 一張表中最多有一個主鍵

  -- 增加主鍵,sql中有三種方式給表增加主鍵:
  -- a.增加主鍵
  create table my_pri1(
  name varchar (20) not null comment '姓名',
  number char (10) primary key comment '學號(不能重複)'
  )charset utf8;
  -- b.在創建表的時候,在所有的字段之後,使用primary key(主鍵字段列表)來創建主鍵,如果有多個字段作爲主鍵,
  -- 可以使複合主鍵

  create table my_pri2(
  number char(10) comment '學號',
  subject char(10) comment '課程代碼',
  score tinyint unsigned default 60 comment '成績',
  -- 增加主鍵限制 : 學號 + 課程號 具有唯一性
  primary key(number, subject)
  )charset utf8;

+---------+---------------------+------+-----+---------+-------+
| Field   | Type                | Null | Key | Default | Extra |
+---------+---------------------+------+-----+---------+-------+
| number  | char(10)            | NO   | PRI |         |       |
| subject | char(10)            | NO   | PRI |         |       |
| score   | tinyint(3) unsigned | YES  |     | 60      |       |
+---------+---------------------+------+-----+---------+-------+

  -- c.當表已經創建好之後,再次額外增加主鍵,可以通過修改字段屬性,也可以直接追加
  alter table 表名 add primary key(字段列表);
  -- 創建一個沒有主鍵的表
  create table my_pri3(
  subject char(10) not null comment '課程編號',
  name varchar(10) comment '課程名字'
  )charset utf8;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| subject | char(10)    | NO   |     | NULL    |       |
| name    | varchar(10) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
  -- 追加主鍵,前提是:表中字段對應的數據本身是獨立的(不重複的)
  alter table my_pri3 modify subject char (10) primary key comment '課程編號';
  alter table my_pri3 add primary key(subject);
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| subject | char(10)    | NO   | PRI | NULL    |       |
| name    | varchar(10) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
  -- 主鍵的約束:主鍵對應的字段數據不允許重複,一旦重複,增和改操作失敗,測試:
  -- 向pri1表插入數據
  insert into my_pri1 values ('張三','07151111'), ('李四','07151112'); -- 成功
  insert into my_pri2 values ('07151111', '0000', 60), ('07151112', '0001', 59); -- 成功
  -- 插入衝突數據
  insert into my_pri1 value ('王無', '07151111'); -- 失敗
  -- ERROR 1062 (23000): Duplicate entry '07151111' for key 'PRIMARY'
  insert into my_pri2 values ('07151111', '0000', 50), ('07151112', '0001', 30);
  -- ERROR 1062 (23000): Duplicate entry '07151111-0000' for key 'PRIMARY'
  -- 更新主鍵 & 刪除主鍵:沒有辦法更新主鍵,主鍵必須先刪除,才能增加
  -- 刪除主鍵
  alter table 表名 drop primary key;
  -- 主鍵分類:在實際創建表的過程中,很少使用真實業務數據作爲主鍵字段(業務主鍵,如學號,課程號),大部分的時候是
  -- 使用邏輯性字段(字段沒有業務含義,值是什麼都沒有關係),將這這種字段成爲邏輯主鍵,因爲主鍵的作用就是保證唯一,
  -- 所以只要能實現這個目的就是可以的

-- * auto_increment
  -- 自動增長,當對應的字段不給值、或者給默認值或者給null的時候,系統會自動從當前字段中已有的最大值在進行+1操作,
  -- 得到一個新的值,自增長通常是跟主鍵搭配

  -- 自增長特點:auto_increment 1.任何一個字段要做自增長,必須本身是一個索引(key一欄有值)
                          --  2. 數字(整型)
                          --  3. (整型)
    create table my_auto(
    id int auto_increment comment '自動增長',
    name varchar (10) not null
    )charset utf8;
-- ERROR 1075 (42000): Incorrect table definition;
--  there can be only one auto column and it must be defined as a key  (必須是key)

    create table my_auto(
    id varchar (1) primary key auto_increment comment '自動增長',
    name varchar (10) not null
    )charset utf8;
-- ERROR 1063 (42000): Incorrect column specifier for column 'id' (必須是整型)

    create table my_auto(
    id int primary key auto_increment comment '自動增長',
    name varchar (10) not null
    )charset utf8;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(10) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+

  -- 自增長的使用,當自動增長被給定的值爲null或不給的時候,會觸發自動增長
  insert into my_auto(name) values ('張三');
  insert into my_auto values (null, '張三');
  insert into my_auto values (default , '李四');

                +----+------+
                | id | name |
                +----+------+
                |  1 | 張三 |
                |  2 | 張三 |
                |  3 | 李四 |
                +----+------+
  -- 每次自增1,注意::如果對應的字段輸入了值,那麼自增長失效,但是下一次還是能使能夠正確
  insert into my_auto values (6, '王五');
  insert into my_auto values (null, '李六');

  -- 刪掉一部分
            +----+------+
            | id | name |
            +----+------+
            |  1 | 張三 |
            |  2 | 張三 |
            +----+------+
  -- 重新插入,會從原來的位置開始(之前插入了很多,過程中沒寫)
            +----+------+
            | id | name |
            +----+------+
            |  1 | 張三 |
            |  2 | 張三 |
            | 11 | 李四 |
            +----+------+
  -- 確定下一次,show create table
  show create table my_auto;

  | Table   | Create Table                                                                                                                                                                                   |
+---------+-------------------------------------------------
my_auto | CREATE TABLE `my_auto` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自動增長',
  `name` varchar(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8
-- 下次是 12

-- 修改自增長:自增長如果是涉及到字段改變,必須先刪除原來的自增長,然後增加(一張表只能有一個自增長)
          -- 修改當前自增長已經存在的值,修改只能比當前已有的自增長的最大值大,不能小(小不生效)
alter table 表名 auto_increment = 值;
alter table my_auto auto_increment = 4; -- 向下修改,不生效

my_auto | CREATE TABLE `my_auto` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自動增長',
  `name` varchar(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 |


alter table my_auto auto_increment = 15; -- 向上修改, 生效

| my_auto | CREATE TABLE `my_auto` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自動增長',
  `name` varchar(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 |

-- 自增長從1開始,每次增長1???所有系統的表現如字符集、校對集都是由系統內部的變量進行控制的
-- 查看自增長對應的變量值:
show variables like 'auto_increment%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| auto_increment_increment | 1     | ---------
| auto_increment_offset    | 1     | ---------
+--------------------------+-------+

-- 修改變量可以實現不同的效果,修改是對整個數據庫的修改,而不是單張表(修改是會話級別)
set auto_increment_increment = 5; -- 修改自增初值

-- 刪除自增長:自增長是字段的一個屬性:可以通過modify來進行修改(保證字段沒有auto_increment即可)
alter 表名 modify 字段 類型;
alter table my_auto modify id int primary key; -- 錯誤的:主鍵理論單獨存在,此句被認爲增加主鍵
-- ERROR 1068 (42000): Multiple primary key defined
alter table my_auto modify id int; -- 正確的,只是改變了字段屬性,數據還再。

| my_auto | CREATE TABLE `my_auto` (
  `id` int(11) NOT NULL DEFAULT '0',
  `name` varchar(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

--  * unique key:唯一鍵,一張表往往需要很多字段需要具有唯一性,數據不能重複,但是一張表中只能有一個主鍵
--                唯一鍵就可以解決表中有多個字段需要唯一性約束的問題
-- 唯一鍵的本質與主鍵差不多:唯一鍵默認的允許自動爲空,而且可以多爲空(空字段不參與唯一性比較)

  -- 增加唯一鍵:基本與主鍵差不多,有三種
     -- a.在創建表的時候,字段之後直接跟
create table my_unique1(
number char(10) unique comment '學號:唯一',
name varchar (20) not null
)charset utf8;

+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| number | char(10)    | YES  | UNI | NULL    |       |
| name   | varchar(20) | NO   |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+
     -- b.在所有的字段之後增加unique(字段列表)
create table my_unique2(
number char(10) not null comment '學號',
name varchar (20) not null,
unique key(number)
)charset utf8;

+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| number | char(10)    | NO   | PRI | NULL    |       |
| name   | varchar(20) | NO   |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+

-- 顯示爲pri:剛好是一個不爲空的唯一鍵,而該表並沒有主鍵
     -- c.創建表之後增加唯一鍵
create table my_unique3(
id int primary key auto_increment,
number char(10) not null,
name varchar (20) not null
)charset utf8;
-- 追加
alter table my_unique3 add unique key(number);

+--------+-------------+------+-----+---------+----------------+
| Field  | Type        | Null | Key | Default | Extra          |
+--------+-------------+------+-----+---------+----------------+
| id     | int(11)     | NO   | PRI | NULL    | auto_increment |
| number | char(10)    | NO   | UNI | NULL    |                |
| name   | varchar(20) | NO   |     | NULL    |                |
+--------+-------------+------+-----+---------+----------------+

-- 唯一鍵作用 : 唯一鍵與主鍵本質相同,唯一區別就是可以爲空,如果不爲空,則與主鍵相同,不能重複
 insert into my_unique1 values (null, '張三'),('000999', '李四'),(null, '王五');
 insert into my_unique1 values ('000999', '李四四'); --失敗
 -- ERROR 1062 (23000): Duplicate entry '000999' for key 'number'

-- 更新唯一鍵 & 刪除唯一鍵
-- 更新唯一鍵:先刪除後新增(唯一鍵可以有多個,可以不刪除)
-- 刪除唯一鍵 :
alter table 表名 drop unique key; -- 錯誤,因爲唯一鍵有多個,數據庫不認識
alter table 表名 drop index 索引名字; -- 唯一鍵默認使用字段名作爲索引

alter table my_unique3 drop index number;
+--------+-------------+------+-----+---------+----------------+
| Field  | Type        | Null | Key | Default | Extra          |
+--------+-------------+------+-----+---------+----------------+
| id     | int(11)     | NO   | PRI | NULL    | auto_increment |
| number | char(10)    | NO   |     | NULL    |                |
| name   | varchar(20) | NO   |     | NULL    |                |
+--------+-------------+------+-----+---------+----------------+

-- ** 索引
-- 幾乎所有的索引都建立在字段之上,索引是系統根據某種算法,將已有的數據(或未來可能新增的數據),單獨
-- 建立一個文件,文件能夠實現快速的匹配數據,並且能夠快速找到表中對應的記錄。

-- 索引的意義:
-- 1.提升查詢數據的效率
-- 2.約束數據的有效性(唯一性等)
-- 增加索引的前提條件:索引本身會產生索引文件(有時候有可能筆數據文件還大),會非常消耗磁盤空間。
-- 如果某個字段需要作爲查詢的條件經常使用,那麼可以使用索引(一定會想辦法增加)。
-- 如果某個字段需要進行數據的有效性約束,也可能使用索引(主鍵,唯一鍵)。

-- MySQL中提供了多種索引
-- 1. 主鍵索引 primary key
-- 2. 唯一索引 unique key
-- 3. 全文索引: fulltext index
-- 4. 普通索引: index

-- 全文索引:針對文章內部的關鍵字進行索引
-- 最大弊端在於:如何確定關鍵字
-- 英文很容易(英文單詞與單詞之間有空格),
-- 中文很難(沒有空格,隨意組合 分詞:sphinx)

-- ***************************數據庫設計****************************

-- 關係(表與表):將實體與實體的關係,反應到最終數據庫表的設計上來:將關係分爲三種:
-- 一對一、一對多(多對一)和多對多(朋友圈)

-- ** 一對一:一張表中的一條記錄一定只能與另外一張表的一條記錄進行對應,反之亦然。
學生表:姓名,性別,年齡,身高,體重,婚姻狀況,籍貫,家庭住址,緊急聯繫人
對應到數據庫:

id(primary key)|姓名|性別|年齡|身高|體重|婚姻狀況|籍貫|家庭住址|緊急聯繫人

表設計成以上形式,符合要求,其中姓名,性別,身高,體重使用常用數據,但是婚姻,籍貫,住址
和聯繫人不屬於常用數據,但是我們習慣上用select * 查詢,不常用的數據就會影響效率,實際又
不常用。

解決方案:將常用和不常用信息分開存儲,分成兩張表:
*常用信息表*
id(primary key)|姓名|性別|年齡|體重|身高|
*不常用信息表*(保證不常用信息與常用信息一定能夠對應上:找一個具有唯一性的字段來連接兩張表)
id(primary key)|婚姻|籍貫|地址|聯繫人|

一個常用表中的一條記錄,永遠只能在一張不常用表中匹配一條記錄,反過來,一個不常用表的一條記錄在常用
表中也只能匹配一條記錄:一一對應關係

-- ** 一對多:一張表中有一條記錄可以對應另外一張表中的多條記錄,但是反過來,另外一張表
-- 中的一條記錄只能對應第一張表的一條記錄,這種關係就是一對多或多對一

老師與代課班級之間的關係:

*老師表*
id(primary key)|名字|年齡|性別|科目|

*班級表*
id(primary key)|年級|班號|

以上關係,一個老師可以在班級表中查找到多條記錄(也可能是一條),但是一個班級只能被一個老師
帶,是一種典型的一對多關係。
但是以上設計:解決了實體的設計表問題,但是沒有解決實際關係問題:班級表找不到老師記錄,
老師也找不到班級

解決方案:在班級表中增加一個字段指向老師表,因爲班級能唯一匹配到一個老師表的記錄
id(primary key)|年級|班號|老師id|

-- * 多對多,一張表A中的一條記錄能夠對應另外一張表B中的多條記錄,同時B表中的一條記錄也能對應
-- 到A表多條記錄,老師和學生
*老師表*
T_id(primary key) | 姓名 | 性別|

*學生表*
S_id(primary key) | 姓名 | 性別|

以上設計方案:實現了實體的設計,但是沒有維護實體的關係。
一個老師教過多個學生,一個學生也被多各老師教過
解決方案:增加一張新表,專門維護兩張表之間的關係

*中間關係表*
|ID | T_id | S_id |
增加中間表之後:中間表與老師表形成了一對多的關係,而且中間表示奪標,維護了能夠
唯一找到一表的關係,同樣的,學生表與中間表也是一個一對多的關係,一對多的關係可以匹配到
關聯表之間的數據

學生找老師:找出S_id -> 中間表尋找匹配記錄(多條) -> 老師表匹配(一條)
學生找老師:找出T_id -> 中間表尋找匹配記錄(多條) -> 學生表匹配(一條)

-- * 範式:Normal Format,是一種離散數學中的知識,是爲了解決一種數據的存儲與優化的問題
-- 保證數據存儲之後,凡是能夠通過關係尋找出來的數據,堅決不再重複存儲,終極目標是爲了減少
-- 數據的冗餘

-- 範式:是一種分層結構的規範,分爲6層,每一層都比上一層更加嚴格,若要滿足下一層範式,
-- 前提是滿足上一層範式。
-- 六層範式:1NF,2NF,...,6NF,其中,1NF是最低層,要求最低,6NF最高層,最嚴格
-- MySQL屬於關係型數據庫,有空間浪費,也是致力於節省存儲空間,與範式所解決的問題不謀而合
-- 再設計數據庫的時候,會利用到範式來指導設計。
-- 但是數據庫不單是要解決空間問題,要保證效率問題,範式只爲了解決空間問題,所以數據庫的設計
-- 又不可能完全按照範式的眼球來實現,一般情況下,只有前三種範式需要滿足。

-- 範式在數據庫的設計中有指導意義,但是不是強制規範


-- ** 1NF,第一範式,在設計表存儲數據的時候,如果表中設計的字段數據,在取出來使用之前
-- 還需要額外的處理(拆分),那麼說表的設計不滿足第一範式:第一範式要求字段數據具有原子性
-- 解決方案:字段拆離(主要看是夠符合實際需求,沒有絕對的原子非原子)

-- ** 2NF,第二範式,在數據表設計的過程中,如果有複合主鍵(多字段主鍵),且表中有字段
-- 並不是由整個主鍵來確定,而是依賴主鍵中某個字段(主鍵的一部分),存在字段依賴之間的部分
-- 問題,稱之爲部分依賴,第二範式用來解決設計表不允許出現部分依賴
-- 一個不符合規範的表
create table not_sat_2NF(
name varchar (20) not null comment '講師姓名',
gender enum('男','女'),
`班級` varchar (10) not null comment '課程:編程語言+序號',
`教室` char(4) comment 'A-D + 三位數字',
`開始時間` datetime comment '這裏用date更合理,不是關注點',
`結束時間` datetime,
primary key (name, `班級`)
)charset utf8;
insert into not_sat_2NF values ('張三', '男' , 'php260','A332', '2018-6-21 14:09:14', '2018-6-21 14:09:14');

+------+--------+--------+------+---------------------+---------------------+
| name | gender | 班級   | 教室 | 開始時間            | 結束時間            |
+------+--------+--------+------+---------------------+---------------------+
| 張三 | 男     | php260 | A332 | 2018-06-21 14:09:14 | 2018-06-21 14:09:14 |
+------+--------+--------+------+---------------------+---------------------+
上面表中,因爲老師沒有辦法作爲獨立主鍵,需要結合班級才能作爲主鍵(複合主鍵,一個老師在一個
班永遠只帶一階段課程),代課時間,開始和結束字段與單籤代課主鍵(講師和班級),但是性別並不
依賴班級,教室不依賴講師,教室不依賴講師,性別只依賴講師,教室只依賴班級,出現了性別和教室
只依賴複合主鍵中的一部分,部分依賴,不符合第二範式。

解決方案:1.可以將性別與講師單獨成表,班級與教室也單獨成表
         2.主鍵取消複合主鍵,使用邏輯主鍵(多增加一個id)
alter table not_sat_2NF drop primary key;
alter table not_sat_2NF add id int not null auto_increment primary key first;

-- ** 3NF 要滿足第三範式,必須滿足第二範式(無部分依賴),第一範式(原子性),理論上講
-- 應該一張表中的所有字段都應該直接依賴主鍵(代表的是業務主鍵),如果表在設計中存在一個字段
-- 並不直接依賴主鍵,而是通過某個非主鍵字段依賴,最終實現依賴主鍵,把這種不是直接依賴主鍵
-- 而是依賴非主鍵字段的依賴關係稱之爲傳遞依賴。
-- 第三範式就是要解決傳遞依賴問題,以上表格已經滿足第一第二範式:
+----+------+--------+--------+------+---------------------+---------------------+
| id | name | gender | 班級   | 教室 | 開始時間            | 結束時間            |
+----+------+--------+--------+------+---------------------+---------------------+
|  1 | 張三 | 男     | php260 | A332 | 2018-06-21 14:09:14 | 2018-06-21 14:09:14 |
+----+------+--------+--------+------+---------------------+---------------------+

以上設計方案中,性別依賴講師存在,講師依賴主鍵,教室依賴班級,性別和教室都存在傳遞依賴
解決方案:將存在傳遞依賴的字段,以及依賴的字段本身單獨取出
第一個:講師表:
|T_id|講師姓名|性別|  -- 其實ID=講師
第二個:班級表:
|class_id|班級|教室| -- class_id = 班級

--************因爲邏輯主鍵本身沒有實際意義,所以主要有邏輯主鍵,永遠有傳遞依賴,他僅
-- 僅是保護數據安全,提高查詢效率

-- ** 逆規範化:磁盤的利用率與效率的對抗
-- 有時候,再設計表的時候,如果一張表中有幾個字段是需要從另外一張表中去獲取信息,理論上講,
-- 的確可以獲取到想要的數據,但是就是效率低一些,會刻意的在某些表中,不去保存另外表的主鍵
--(邏輯主鍵),而是直接保存想要的數據信息,這樣一來,在查詢的時候,一張表可以直接提供數據
-- 而不需要多表查詢(效率低),但是會導致數據冗餘增加。

----------------*********************數據高級操作***********************
-- 圍繞數據增刪改查
-- * 新增數據:基本操作:insert into 表名 (字段列表) values(值列表)
-- 在數據插入的時候,假設主鍵對應的值已經存在,插入一定會失敗!

-- 主鍵衝突,當主鍵存在衝突的時候,可以選擇性進行處理,更新和替換
-- 更新: insert into 表名(字段列表:包含主鍵)values(值列表) duplicate key update
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| name   | varchar(20) | NO   |     | NULL    |       |
| number | char(10)    | NO   | PRI | NULL    |       |
+--------+-------------+------+-----+---------+-------+
裏面的數據爲:
+------+----------+
| name | number   |
+------+----------+
| 張三 | 07151111 |
| 李四 | 07151112 |
+------+----------+

insert into my_pri1 values ('張叄', '07151111');
-- ERROR 1062 (23000): Duplicate entry '07151111' for key 'PRIMARY'
insert into my_pri1 values ('張叄', '07151111') on duplicate key update name='張叄';

+------+----------+
| name | number   |
+------+----------+
| 張叄 | 07151111 |
| 李四 | 07151112 |
+------+----------+

-- 替換 replace into 表名(字段列表:包含主鍵) values(列表)
replace into my_pri1 values ('李思', '07151112');
+------+----------+
| name | number   |
+------+----------+
| 張叄 | 07151111 |
| 李思 | 07151112 |
+------+----------+
-- 使用replace,假設沒有主鍵衝突,直接生效
replace into my_pri1 values ('王無', '07152222');

-- 蠕蟲複製:從已有的數據中,去獲取數據,然後又進行新增操作,導致的結果是數據成倍的增加。
-- 表創建的高級操作,從已有表創建新表。
create table 表名 like 表名;
-- 只複製結構,不復制數據
-- 蠕蟲賦值:先查數據,然後將查出的數據新增一遍,可以從別的表,也可以從自己的表
insert into 表名(字段列表) select 字段列表\* from 表名;
-- 意義:1.從已有表拷貝數據到新表2.可以迅速讓表中的數據膨脹到一定的數量級,測試表的壓力及效率

-- 高級更新
-- 基本語法:update 表名 set 字段 = 值 where ;
-- 高級新增語法:update 表名 set 字段 = 值 where [條件] limit 更新數量; 限制數量

-- **** 沒有校對集,不區分大小寫

-- * 刪除數據
-- 與更新類似,可以通過limit來限制數量
delete form table where [條件] limit 數量;

-- 清空表,重置自增長
truncate 表名; -- 先刪除該表,再增加該表,數據沒了

-- ****** 高級查詢
-- 基本語法
select 字段列表 from 表名 [where 條件];

-- 完整語法
select [select 選項] 字段列表[字段別名] from 數據源
[where 條件子句][group by 子句] [having 子句] [order by 子句][limit 子句];

-- select 選項:select對查出來的結果的處理方式:
all : 默認的,保留所有結果,即select * from table = select all * from table;
distinct : 去重,查出來的結果,將重複的字段去除(所有字段都相同),select distinct (字段列表) from table 是不能成立的

-- 字段別名 : 當數據進行查詢出來的時候(多表查詢的時候)會有同名字段,需要對字段名進行重命名:別名

字段名 [as] 別名;
select id, number as 學號, name as 姓名, sex as 性別 from  table;

-- 數據源:數據的來源,關係型數據的來源都是數據表,本質上只要保證數據類似二維表,最終都可以作爲數據源
-- 數據源分爲多種:單表數據源,多表數據源,查詢語句。
-- 單表:上面的所有
-- 多表:表名,表名
select * from talbe1, table2;
-- 默認是笛卡爾積,表一中的每條記錄都要與另外一個表的全部連一次,沒什麼用,儘量避免

-- 子查詢
select * from (select * from stu_info);
-- ERROR 1248 (42000): Every derived table must have its own alias
select * from (select * from stu_info) as s; -- 給個表名就正確了

-- where 子句,用來篩選數據,where 子句返回的結果:0或1,0代表false,1代表true
-- 判斷條件:<,>,<>,<=,>=,=,like,and,in,not in,between
-- 邏輯運算符:&&(and),||(or),!(not)
where 原理:是唯一一個直接從磁盤獲取數據的時候就開始判斷的語句,從磁盤去除一條記錄,開始運行where判斷
判斷的結果如果成立,則保存到內存,否則直接放棄。

select * from stu_info where 1; -- 全部查詢

alter table stu_info add age tinyint unsigned;
alter table stu_info add height tinyint unsigned;
-- 給初值
update stu_info set age=floor(rand() * 20 + 20), height=floor(rand() * 20 + 170);
+----+------+-------+---------------------+-------+------+--------+
| id | name | score | updata_time         | name2 | age  | height |
+----+------+-------+---------------------+-------+------+--------+
|  2 | 劉   |    10 | 2018-06-21 21:16:52 | NULL  |   20 |    173 |
|  3 |    |    20 | 2018-06-21 21:16:52 | NULL  |   37 |    185 |
|  4 | 劉   |  NULL | 2018-06-21 21:16:52 | 劉    |   25 |    170 |
+----+------+-------+---------------------+-------+------+--------+
-- 條件查詢一:找出學生id = 1 或 3 或 5 的學生
select  * from stu_info where id = 1 || id = 3  || id = 5;
select  * from stu_info where id in (1, 3, 5);
-- 條件查詢二: 查出區間落在身高在180,190之間
select * from stu_info where height >= 180 and height <= 190;
select * from stu_info where height between 180 and 190;
--  between 本身是閉區間,左邊的一定要比右邊的小,否則無效,因爲內部默認替換成 >=  and <=


-- * group by 分組的意思,根據某個字段進行分組,不同的分到不同組
-- 根據性別分組
alter table stu_info add gender enum('男','女','保密');
insert into stu_info(name, score, name2, age, height, gender) (select  name, score, name2, age, height, gender from stu_info);
update stu_info set gender = (round(rand()) + 1);

+----+------+-------+---------------------+-------+------+--------+--------+
| id | name | score | updata_time         | name2 | age  | height | gender |
+----+------+-------+---------------------+-------+------+--------+--------+
|  2 | 劉   |    10 | 2018-06-21 21:39:03 | NULL  |   20 |    173 | 男     |
|  3 |    |    20 | 2018-06-21 21:39:03 | NULL  |   37 |    185 | 男     |
|  4 | 劉   |  NULL | 2018-06-21 21:42:36 | 劉    |   25 |    170 | 女     |
|  5 | 劉   |    10 | 2018-06-21 21:42:36 | NULL  |   20 |    173 | 女     |
|  6 |    |    20 | 2018-06-21 21:42:36 | NULL  |   37 |    185 | 女     |
|  7 | 劉   |  NULL | 2018-06-21 21:39:03 | 劉    |   25 |    170 | 男     |
+----+------+-------+---------------------+-------+------+--------+--------+

select * from stu_info group by gender; -- 結果如下

+----+------+-------+---------------------+-------+------+--------+--------+
| id | name | score | updata_time         | name2 | age  | height | gender |
+----+------+-------+---------------------+-------+------+--------+--------+
|  2 | 劉   |    10 | 2018-06-21 21:39:03 | NULL  |   20 |    173 | 男     |
|  4 | 劉   |  NULL | 2018-06-21 21:42:36 | 劉    |   25 |    170 | 女     |
+----+------+-------+---------------------+-------+------+--------+--------+

-- 分組的意思:是爲了統計數據(按組統計),按分組字段進行數據統計
  -- count() : 統計分組後的記錄數,每一組有多少個
  -- max() : 統計每組中最大的值
  -- min() : 統計最小值
  -- avg() : 均值
  -- sum() : 統計和

-- 分組統計:身高、平均年齡和總年齡
select gender, count(*), max(height),min(height),
avg(age), sum(age) from stu_info group by gender;

+--------+----------+-------------+-------------+----------+----------+
| gender | count(*) | max(height) | min(height) | avg(age) | sum(age) |
+--------+----------+-------------+-------------+----------+----------+
| 男     |        3 |         185 |         170 |  27.3333 |       82 |
| 女     |        3 |         185 |         170 |  27.3333 |       82 |
+--------+----------+-------------+-------------+----------+----------+

-- 對比原始數據,是正確的
-- count 函數,裏面可以使用兩種參數,*代表統計記錄,字段名代表統計對應的字段,若爲NULL,則統計時不加一
select gender, count(*), count(score), max(height),min(height),
avg(age), sum(age) from stu_info group by gender;-- 男、女score中均有一個NULL,所以count(score)值爲2

+--------+----------+--------------+-------------+-------------+----------+----------+
| gender | count(*) | count(score) | max(height) | min(height) | avg(age) | sum(age) |
+--------+----------+--------------+-------------+-------------+----------+----------+
| 男     |        3 |            2 |         185 |         170 |  27.3333 |       82 |
| 女     |        3 |            2 |         185 |         170 |  27.3333 |       82 |
+--------+----------+--------------+-------------+-------------+----------+----------+

表中結果會自動排序,男 -> 女 ,字符串字典排序,enum下標,默認升序。
group by 字段 [asc / desc] ; 對分組結果合併之後的整個結果進行排序

select gender, count(*), count(score), max(height),min(height),
avg(age), sum(age) from stu_info group by gender desc;-- 男、女score中均有一個NULL,所以count(score)值爲2

+--------+----------+--------------+-------------+-------------+----------+----------+
| gender | count(*) | count(score) | max(height) | min(height) | avg(age) | sum(age) |
+--------+----------+--------------+-------------+-------------+----------+----------+
| 女     |        3 |            2 |         185 |         170 |  27.3333 |       82 |
| 男     |        3 |            2 |         185 |         170 |  27.3333 |       82 |
+--------+----------+--------------+-------------+-------------+----------+----------+

多字段分組,先根據一個字段進行分組,然後對分組後的結果進行再次分組。

select name, gender, count(*) from stu_info group by name, gender;
+------+--------+----------+
| name | gender | count(*) |
+------+--------+----------+
| 劉   | 男     |        2 |  -- name 爲 劉的 男的有2個
| 劉   | 女     |        2 |  -- name 爲 劉 的 女的 有兩個
|    | 男     |        1 |  -- name 爲 的男的 1 個
|    | 女     |        1 |  -- name 爲 的女的 1 個
+------+--------+----------+

-- 有一個函數,可以對分組的結果中的某個字段進行字符串連接(保留該組的所有的某個字段)。
group_concat(字段)

+----+------+-------+---------------------+------+--------+--------+--------+
| id | name | score | updata_time         | age  | height | gender | class  |
+----+------+-------+---------------------+------+--------+--------+--------+
|  2 | 張三 |    11 | 2018-06-21 22:15:12 |   20 |    173 | 男     | class1 |
|  3 | 李四 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 男     | class2 |
|  4 | 王五 |    15 | 2018-06-21 22:15:12 |   25 |    170 | 女     | class1 |
|  5 | 趙柳 |    10 | 2018-06-21 22:15:12 |   20 |    173 | 女     | class3 |
|  6 | 泮七 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 女     | class2 |
|  7 | 徐八 |   100 | 2018-06-21 22:15:12 |   25 |    170 | 男     | class2 |
+----+------+-------+---------------------+------+--------+--------+--------+

select class, gender, count(*), group_concat(name) from stu_info group by class, gender;
+--------+--------+----------+--------------------+
| class  | gender | count(*) | group_concat(name) |
+--------+--------+----------+--------------------+
| class1 | 男     |        1 | 張三               |  -- class1 男生 1 個,叫張三
| class1 | 女     |        1 | 王五               |  -- class1 女生 1 個,叫王五
| class2 | 男     |        2 | 李四,徐八          |  --...
| class2 | 女     |        1 | 泮七               |
| class3 | 女     |        1 | 趙柳               |
+--------+--------+----------+--------------------+

-- * 回溯統計:with rollup;任何一個分組後都會有一個分組,最後都需要想上級分組進行彙報統計
-- 根據當前分組的字段,這就是回溯統計:回溯統計的時候會將分組字段置空
select class, count(*) from stu_info group by class;
+--------+----------+
| class  | count(*) |
+--------+----------+
| class1 |        2 |
| class2 |        3 |
| class3 |        1 |
+--------+----------+
-- 回溯統計
select class, count(*) from stu_info group by class with rollup;

+--------+----------+
| class  | count(*) |
+--------+----------+
| class1 |        2 |
| class2 |        3 |
| class3 |        1 |
| NULL   |        6 |
+--------+----------+

-- 多字段分組回溯統計
select class, gender, count(*), group_concat(name) from stu_info group by class, gender with rollup;
+--------+--------+----------+-------------------------------+
| class  | gender | count(*) | group_concat(name)            |
+--------+--------+----------+-------------------------------+
| class1 | 男     |        1 | 張三                           |
| class1 | 女     |        1 | 王五                           |
| class1 | NULL   |        2 | 張三,王五                      |
| class2 | 男     |        2 | 李四,徐八                      |
| class2 | 女     |        1 | 泮七                          |
| class2 | NULL   |        3 | 李四,徐八,泮七                 |
| class3 | 女     |        1 | 趙柳                          |
| class3 | NULL   |        1 | 趙柳                          |
| NULL   | NULL   |        6 | 張三,王五,李四,徐八,泮七,趙柳    |
+--------+--------+----------+-------------------------------+

-- having 子句,與where子句一樣:進行條件判斷的,where是針對磁盤數據進行判斷,進入內存後,
-- 會進行分組等其他操作,分組結果就需要having來處理。
-- having可以做where幾乎所有事情,但是where卻不能做having能做的很多事

1.分組統計的結果的處理,只能用having來做

-- 求出每個班人數大於等於2的學生人數
select class, count(*), group_concat(name) from stu_info group by class  having count(*) >= 2;
+--------+----------+--------------------+
| class  | count(*) | group_concat(name) |
+--------+----------+--------------------+
| class1 |        2 | 張三,王五          |
| class2 |        3 | 李四,泮七,徐八     |
+--------+----------+--------------------+

2.having 能使用字段別名,where不能,因爲where是從磁盤取數據,二名字只有在內存中才能產生
-- 優化
select class, count(*) as tatal , group_concat(name) from stu_info group by class  having tatal >= 2;
+--------+-------+--------------------+
| class  | tatal | group_concat(name) |
+--------+-------+--------------------+
| class1 |     2 | 張三,王五          |
| class2 |     3 | 李四,泮七,徐八     |
+--------+-------+--------------------+

-- order by 排序,根據某個字段升序或降序排列,依賴校對集
order by 字段名 [asc desc];
-- 可以根據多個字段排序,先跟據某個字段排序,然後排序好的內容,再按照某個數據繼續排序
select * from stu_info order by class, gender;
+----+------+-------+---------------------+------+--------+--------+--------+
| id | name | score | updata_time         | age  | height | gender | class  |
+----+------+-------+---------------------+------+--------+--------+--------+
|  2 | 張三 |    11 | 2018-06-21 22:15:12 |   20 |    173 | 男     | class1 |
|  4 | 王五 |    15 | 2018-06-21 22:15:12 |   25 |    170 | 女     | class1 |
|  3 | 李四 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 男     | class2 |
|  7 | 徐八 |   100 | 2018-06-21 22:15:12 |   25 |    170 | 男     | class2 |
|  6 | 泮七 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 女     | class2 |
|  5 | 趙柳 |    10 | 2018-06-21 22:15:12 |   20 |    173 | 女     | class3 |
+----+------+-------+---------------------+------+--------+--------+--------+

select * from stu_info order by class, gender desc;

+----+------+-------+---------------------+------+--------+--------+--------+
| id | name | score | updata_time         | age  | height | gender | class  |
+----+------+-------+---------------------+------+--------+--------+--------+
|  4 | 王五 |    15 | 2018-06-21 22:15:12 |   25 |    170 | 女     | class1 |
|  2 | 張三 |    11 | 2018-06-21 22:15:12 |   20 |    173 | 男     | class1 |
|  6 | 泮七 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 女     | class2 |
|  3 | 李四 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 男     | class2 |
|  7 | 徐八 |   100 | 2018-06-21 22:15:12 |   25 |    170 | 男     | class2 |
|  5 | 趙柳 |    10 | 2018-06-21 22:15:12 |   20 |    173 | 女     | class3 |

select * from stu_info order by class desc, gender desc;
+----+------+-------+---------------------+------+--------+--------+--------+
| id | name | score | updata_time         | age  | height | gender | class  |
+----+------+-------+---------------------+------+--------+--------+--------+
|  5 | 趙柳 |    10 | 2018-06-21 22:15:12 |   20 |    173 | 女     | class3 |
|  6 | 泮七 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 女     | class2 |
|  3 | 李四 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 男     | class2 |
|  7 | 徐八 |   100 | 2018-06-21 22:15:12 |   25 |    170 | 男     | class2 |
|  4 | 王五 |    15 | 2018-06-21 22:15:12 |   25 |    170 | 女     | class1 |
|  2 | 張三 |    11 | 2018-06-21 22:15:12 |   20 |    173 | 男     | class1 |
+----+------+-------+---------------------+------+--------+--------+--------+

-- limit:主要用來實現數據的分頁,爲用戶節省空間,提交服務器的響應效率
-- 減少資源的浪費
-- 方案一:只用來限制長度:limit 數據量

-- 方案二:限制起始位置、步長,主要作用,用來分頁
-- 對於用戶來講:可以點擊的分頁按鈕1,2,3,4
-- 對於服務器:分局用戶選擇的頁碼來獲取不同的數據:limit offset,length, 記錄數從0編號
-- length : 每頁顯示的數據量:基本不變


-- ************回顧
-- 列屬性:主鍵,一般搭配自增長,主鍵、自增長每個表都只有一個,唯一鍵,有就不能重複
-- 關係:一對一,一對多,多對多
-- 範式:三層,一層,字段設計原子性;二層,不能存在部分依賴,主要沒有複合主鍵就不會產生
--           三層,不存在傳遞依賴(單獨建表)
-- 逆規範化:效率與磁盤空間的博弈

-- 高級數據操作:
  -- 主鍵衝突 (更新、替換),蠕蟲複製
  -- 更新操作,limit限制數量
  -- 刪除操作,limit,清空表(truncate)
  -- 查詢操作,select選項(all,distinct)字段別名,
  --         數據源(單表,多表,子查詢),where子句(條件判斷:從磁盤)
  --         groupby子句(分組統計,統計函數,分組排序,多字段分組,回溯統計)
  --         having子句(針對分組統計的結果進行判斷),order by子句(排序,多字段排序)
  --         limit子句(限制記錄數,分頁)

-- 連接查詢:將多張表(可以大於2張)進行記錄的連接(按照某個指定的條件進行數據的拼接)
-- join, 左邊的表 join  右邊的表
--        最終結果:記錄數有可能變化,字段是一定會增加(至少兩張表合併)
-- 連接查詢的意義:用戶查看數據的時候需要顯示的暑假來自多張表

-- 連接查詢分類,SQL中將連接查詢分成四類,內連接、外連接、自然連接、交叉連接

-- 交叉連接,cross join,從一張表中循環取出每一條記錄,每一條記錄都去另外一張表
--            進行匹配,並且匹配一定保留(無條件匹配),而連接本身字段就會增加,
--            最終形成結果叫做:笛卡爾乘積

-- 基本語法:
左表 cross join 右表; -- 等效於 from 左表,右表
-- 笛卡爾積沒有意義,交叉連接存在的價值是保證連接這種結構的完整性

-- 內連接,inner join,從左表中取出每一條記錄,去右表中與所有記錄進行匹配,
--        匹配必須是某個條件在左表中與右表中相同,最終纔會保留結果,否則不會保留
-- 基本語法:左表 [inner] join on 左表.字段 = 右表.字段; on  表是連接連接,條件字段就是代表相同的業務含義
+------------+-----------------+--------+---------------------+---------------------+---------------------+
| seckill_id | name            | number | create_time         | start_time          | end_time            |
+------------+-----------------+--------+---------------------+---------------------+---------------------+
|       1000 | 1000秒殺iphone  |     96 | 2018-04-30 14:29:22 | 2019-11-01 00:00:00 | 2019-11-02 00:00:00 |
|       1001 | 1000秒殺iphone2 |    999 | 2018-04-30 14:29:22 | 2015-11-01 00:00:00 | 2019-11-02 00:00:00 |
|       1002 | 1000秒殺iphone3 |     50 | 2018-04-30 14:29:22 | 2015-11-01 00:00:00 | 2019-11-02 00:00:00 |
|       1003 | 1000秒殺iphone4 |     20 | 2018-04-30 14:29:22 | 2015-11-01 00:00:00 | 2019-11-02 00:00:00 |
|       1004 | 1000秒殺iphone  |    100 | 2018-05-01 14:18:33 | 2015-11-01 00:00:00 | 2019-11-02 00:00:00 |
|       1005 | 1000秒殺iphone2 |   1000 | 2018-05-01 14:18:33 | 2015-11-01 00:00:00 | 2019-11-02 00:00:00 |
|       1006 | 1000秒殺iphone3 |     50 | 2018-05-01 14:18:33 | 2015-11-01 00:00:00 | 2019-11-02 00:00:00 |
|       1007 | 1000秒殺iphone4 |     20 | 2018-05-01 14:18:33 | 2015-11-01 00:00:00 | 2019-11-02 00:00:00 |
+------------+-----------------+--------+---------------------+---------------------+---------------------+

+------------+-------------+-------+---------------------+
| seckill_id | user_phone  | state | create_time         |
+------------+-------------+-------+---------------------+
|       1000 | 15209202418 |     0 | 2018-05-20 20:06:11 |
|       1000 | 15209202419 |     0 | 2018-05-20 20:03:39 |
|       1001 | 15209202416 |     0 | 2018-05-20 20:12:43 |
+------------+-------------+-------+---------------------+

select * from seckill_info inner join success_kill on seckill_info.seckill_id = success_kill.seckill_id;
+------------+-----------------+--------+---------------------+---------------------+---------------------+------------+-------------+-------+---------------------+
| seckill_id | name            | number | create_time         | start_time          | end_time            | seckill_id | user_phone  | state | create_time         |
+------------+-----------------+--------+---------------------+---------------------+---------------------+------------+-------------+-------+---------------------+
|       1000 | 1000秒殺iphone  |     96 | 2018-04-30 14:29:22 | 2019-11-01 00:00:00 | 2019-11-02 00:00:00 |       1000 | 15209202418 |     0 | 2018-05-20 20:06:11 |
|       1000 | 1000秒殺iphone  |     96 | 2018-04-30 14:29:22 | 2019-11-01 00:00:00 | 2019-11-02 00:00:00 |       1000 | 15209202419 |     0 | 2018-05-20 20:03:39 |
|       1001 | 1000秒殺iphone2 |    999 | 2018-04-30 14:29:22 | 2015-11-01 00:00:00 | 2019-11-02 00:00:00 |       1001 | 15209202416 |     0 | 2018-05-20 20:12:43 |
+------------+-----------------+--------+---------------------+---------------------+---------------------+------------+-------------+-------+---------------------+

字段別名以及表別名的使用:在查詢數據的時候,不同表有同名字段,這個時候需要加上表名才能區分,而表名太長,通常可以使用別名

select c.user_phone as '聯繫方式', s.name as '秒殺商品', c.create_time as '秒殺時間' from seckill_info as s
inner join success_kill  as c
on s.seckill_id = c.seckill_id;

+-------------+-----------------+---------------------+
| 聯繫方式    | 秒殺商品          | 秒殺時間             |
+-------------+-----------------+---------------------+
| 15209202419 | 1000秒殺iphone  | 2018-05-20 20:03:39 |
| 15209202418 | 1000秒殺iphone  | 2018-05-20 20:06:11 |
| 15209202416 | 1000秒殺iphone2 | 2018-05-20 20:12:43 |
+-------------+-----------------+---------------------+

-- 內連接可以內有連接條件,系統會保留所有內容,笛卡爾成績

-- on可以用where替代,但是where沒有on效率高(where會一個一個匹配,在選,on先篩選,再匹配)

select c.user_phone as '聯繫方式', s.name as '秒殺商品', c.create_time as '秒殺時間' from seckill_info as s
inner join success_kill  as c
where s.seckill_id = c.seckill_id;


-- 外連接,outer join,以某張表爲主,取出裏面的所有記錄,然後每條與另外一張表進行連接,不管能不能匹配上條件,最終都會保留
--        能匹配,正確保留,不能匹配,其他表的字段都置空NULL。

-- 外連接分爲兩種,是以某張表爲主,有主表
  -- left join 左外連接(左連接),以左表爲主表
  -- right join 右連接,以右表爲主

select c.user_phone as '聯繫方式', s.name as '秒殺商品', c.create_time as '秒殺時間' from seckill_info as s
left join success_kill  as c
on s.seckill_id = c.seckill_id;
+-------------+-----------------+---------------------+
| 聯繫方式     | 秒殺商品         | 秒殺時間             |
+-------------+-----------------+---------------------+
| 15209202418 | 1000秒殺iphone  | 2018-05-20 20:06:11 |
| 15209202419 | 1000秒殺iphone  | 2018-05-20 20:03:39 |
| 15209202416 | 1000秒殺iphone2 | 2018-05-20 20:12:43 |
|        NULL | 1000秒殺iphone3 | NULL                |
|        NULL | 1000秒殺iphone4 | NULL                |
|        NULL | 1000秒殺iphone  | NULL                |
|        NULL | 1000秒殺iphone2 | NULL                |
|        NULL | 1000秒殺iphone3 | NULL                |
|        NULL | 1000秒殺iphone4 | NULL                |
+-------------+-----------------+---------------------+

-- 自然連接,nature join,就是是自動匹配連接條件,系統以字段名字作爲匹配
--         模式,同名字段作爲條件,多個同名字都作爲條件。

-- 自然連接,可以分爲自然內連接,自然外連接.
-- 自然內連接:
select * from seckill_info natural join success_kill;-- 根據同名字段自動匹配,如果有多個,則多個同時作爲條件

-- 自然外連接,左外
select * from seckill_info natural left join success_kill;

----------------------回顧------------------------------
-- 連接查詢:多張表連接到一起,不管記錄數如何,字段數一定會增加
-- 分類:內連接、外連接、自然連接、交叉連接
  -- 交叉連接:cross join(笛卡爾)
  -- 內連接:inner join,左右兩張表中有連接條件(不匹配忽略)
  -- 外連接:outer[left/right] join,主表有的記錄一定會存在,匹配了就保留副表字段數據,沒匹配置空
  -- 自然連接:natural join,自動匹配條件(相同的字段名),using關鍵字

-- 外鍵,表中一個字段指向另外一個 表的主鍵
-- 創建時增加,在所有表字段後,使用foreign key (字段名) references 外表(字段)
create table my_foreign1(
group_id int primary key auto_increment,
name varchar(20) not null comment '隊伍名',
stu_id int comment '學生id',
foreign key (stu_id) references stu_info(id)
)charset utf8;

| my_foreign1 | CREATE TABLE `my_foreign1` (
  `group_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL COMMENT '隊伍名',
  `stu_id` int(11) DEFAULT NULL COMMENT '學生id',
  PRIMARY KEY (`group_id`),
  KEY `stu_id` (`stu_id`),
  CONSTRAINT `my_foreign1_ibfk_1` FOREIGN KEY (`stu_id`) REFERENCES `stu_info` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |

-- 外鍵要求字段身,必須是一個索引,若不指定,會自動命名外鍵索引

-- 增加外鍵,在新表增加後,修改表結構
alter table 表名 add [constraint 外鍵名字]foreign key (外鍵字段) references 外表(字段)
create table my_foreign2(
group_id int primary key auto_increment,
name varchar(20) not null comment '隊伍名',
stu_id int comment '學生id'
)charset utf8;

-- 增加外鍵
alter table my_foreign2 add
-- 指定外鍵名
constraint stu_id_1
-- 外鍵
foreign key (stu_id) references stu_info(id);

-- 外鍵不可修改,外鍵刪除,要根據外鍵名字刪除,因爲外鍵不唯一,不能像刪除主鍵一樣刪除
alter table 表名 drop foreign key 外鍵名稱;
alter table my_foreign1 drop foreign key my_foreign1_ibfk_1;

+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| group_id | int(11)     | NO   | PRI | NULL    | auto_increment |
| name     | varchar(20) | NO   |     | NULL    |                |
| stu_id   | int(11)     | YES  | MUL | NULL    |                |
+----------+-------------+------+-----+---------+----------------+

| my_foreign1 | CREATE TABLE `my_foreign1` (
  `group_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL COMMENT '隊伍名',
  `stu_id` int(11) DEFAULT NULL COMMENT '學生id',
  PRIMARY KEY (`group_id`),
  KEY `stu_id` (`stu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |

--外鍵已經刪掉了,但原來字段依然保留爲普通索引,即外鍵刪除無法通過
--desc查看結果


-- 外鍵作用,默認有兩點,一個對父表(被指向的表),一個隊子表(外鍵字段所在的表)
-- 對子表約束,寫操作,若父表沒有對應條目,寫操作失敗。
-----------父表---------------
+----+------+-------+---------------------+------+--------+--------+--------+
| id | name | score | updata_time         | age  | height | gender | class  |
+----+------+-------+---------------------+------+--------+--------+--------+
|  2 | 張三 |    11 | 2018-06-21 22:15:12 |   20 |    173 | 男     | class1 |
|  3 | 李四 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 男     | class2 |
|  4 | 王五 |    15 | 2018-06-21 22:15:12 |   25 |    170 | 女     | class1 |
|  5 | 趙柳 |    10 | 2018-06-21 22:15:12 |   20 |    173 | 女     | class3 |
|  6 | 泮七 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 女     | class2 |
|  7 | 徐八 |   100 | 2018-06-21 22:15:12 |   25 |    170 | 男     | class2 |
+----+------+-------+---------------------+------+--------+--------+--------+

insert into my_foreign2 values (null, 'Group1', 8); -- 新增一個不存在的條目
-- ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`stuinfo`.`my_foreign2`,
-- CONSTRAINT `stu_id_1` FOREIGN KEY (`stu_id`) REFERENCES `stu_info` (`id`))

insert into my_foreign2 values (null, 'Group1', 2);

-- 對父表約束:父表數據進行寫操作(刪和寫,都必須涉及到主鍵本身),如果對應的主鍵在子表中已經被數據所引用,那麼就不允許操作
update stu_info set id=9 where id = 2;
-- ERROR 1451 (23000): Cannot delete or update a parent row:
--  a foreign key constraint fails (`stuinfo`.`my_foreign2`,
-- CONSTRAINT `stu_id_1` FOREIGN KEY (`stu_id`) REFERENCES `stu_info` (`id`))
update stu_info set id = 9 where id = 7; -- 可以修改成功

-- 外鍵條件:1.外鍵必須要保證表的存儲引擎是innodb(默認存儲引擎),如果不是innodb
--            那麼也可以創建成功,但是沒有約束效果
--          2.外鍵字段的字段類型(列類型),必須與父表的主鍵類型完全一致
--          3.一張表中外鍵名字不能重複
--          4.增加外鍵的字段(數據已經存在),必須保證數據與父表主鍵對應

-- 外鍵約束,就是指外鍵作用,之前所講的是默認的作用,可以通過對外鍵的需求進行定製操作。
-- 外鍵約束有三種模式:
  -- district 嚴格模式(默認的),父表不能刪除或更新已經被子表數據引用的記錄
  -- cascade 級聯模式,父表的操作,對應子表關聯的數據也跟着操作
  -- setnull 置空模式,父表操作之後,子表對應的數據(外鍵字段)被置空

-- 通常合理的做法:刪除的時候,子表置空,更新的時候,級聯
foreign key (外鍵字段) references 父表(主鍵字段) on delete set null on update cascade ;
-- 刪除置空的前提條件,外鍵字段允許爲空(如果不滿條件,外鍵無法創建成功)

-- 聯合查詢,將多次查詢(多條select語句),在記錄上進行拼接(字段不會增加)
-- 基本語法:多條select語句構成,每一條select語句獲得的字段數量必須嚴格一致(但是與字段類型無關)
select 語句1 union [union 選項] select 語句2 ...

union 選項:與select選項一樣有兩個:all , distinct (默認)

聯合查詢只要求字段數一樣,數據類型不要求,最後字段名保存前面的
select id, name, score from stu_info
union
select group_id, name, stu_id from my_foreign2;
+----+--------+-------+
| id | name   | score |
+----+--------+-------+
|  2 | 張三   |    11 |
|  3 | 李四   |    20 |
|  4 | 王五   |    15 |
|  5 | 趙柳   |    10 |
|  6 | 泮七   |    20 |
|  9 | 徐八   |   100 |
|  2 | Group1 |     2 |
+----+--------+-------+
-- 意義:1.查詢同一張表,但是需求不同,如查詢學生信息,身高升序,女生身高降序
--      2.多表查詢,多張表的結構是完全一樣的,保存的數據(結構)也是一樣的(分表,提升效率)

-- 查詢學生信息,男生升序,女生降序
select * from stu_info where gender = '男' order by age asc
union
select * from stu_info where gender = '女' order by age desc;
-- ERROR 1221 (HY000): Incorrect usage of UNION and ORDER BY
-- 在聯合查詢中,有ordby必須括起來

(select * from stu_info where gender = '男' order by age asc)
union
(select * from stu_info where gender = '女' order by age desc);
+----+------+-------+---------------------+------+--------+--------+--------+
| id | name | score | updata_time         | age  | height | gender | class  |
+----+------+-------+---------------------+------+--------+--------+--------+
|  2 | 張三 |    11 | 2018-06-21 22:15:12 |   20 |    173 | 男     | class1 |
|  3 | 李四 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 男     | class2 |
|  9 | 徐八 |   100 | 2018-06-24 10:03:56 |   25 |    170 | 男     | class2 |
|  4 | 王五 |    15 | 2018-06-21 22:15:12 |   25 |    170 | 女     | class1 |
|  5 | 趙柳 |    10 | 2018-06-21 22:15:12 |   20 |    173 | 女     | class3 |
|  6 | 泮七 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 女     | class2 |
+----+------+-------+---------------------+------+--------+--------+--------+

-- 在聯合查詢中,讓order by 生效,必須用limit

(select * from stu_info where gender = '男' order by age asc limit 999999)
union
(select * from stu_info where gender = '女' order by age desc limit 999999);

+----+------+-------+---------------------+------+--------+--------+--------+
| id | name | score | updata_time         | age  | height | gender | class  |
+----+------+-------+---------------------+------+--------+--------+--------+
|  2 | 張三 |    11 | 2018-06-21 22:15:12 |   20 |    173 | 男     | class1 |
|  9 | 徐八 |   100 | 2018-06-24 10:03:56 |   25 |    170 | 男     | class2 |
|  3 | 李四 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 男     | class2 |
|  6 | 泮七 |    20 | 2018-06-21 22:15:12 |   37 |    185 | 女     | class2 |
|  4 | 王五 |    15 | 2018-06-21 22:15:12 |   25 |    170 | 女     | class1 |
|  5 | 趙柳 |    10 | 2018-06-21 22:15:12 |   20 |    173 | 女     | class3 |
+----+------+-------+---------------------+------+--------+--------+--------+

-- 子查詢,sub query,查詢實在某個查詢結果之上進行的(一條select語句內部包含另外一條select語句)
-- 子查詢分類有兩種分類方式,按位置分類,按結果分類

-- 按位置分類:子查詢(select語句)在外部查詢(select語句)中出現的位置
-- from 子查詢:子查詢出現在from之後
-- where 子查詢,子查詢出現在where條件中
-- exists 子查詢,子查詢出現在exists裏面

--按結果分類:根據子查詢得到的數據進行分類(理論上講,任何一個查詢得到的結果都可以理解爲二維表)
  -- 標量子查詢:子查詢得到的結果是一行一列(出現在where之後)
  -- 列子查詢:子查詢得到的結果是一列多行(出現在where之後)
  -- 行子查詢:子查詢得到結果是多列,一行或多行多列(出現在where之後)
  -- 表子查詢:子查詢得到的結果是多行多列(出現的位置是在from之後)

-- 標量子查詢
-- 知道班級名字爲class1,想獲取該班所有學生
1.確定數據源,獲取所有的學生
select * from stu_info where id = ?;
2.獲取班級ID,可通過班級名字確定
select id from my_calss where  class_name = 'class1';

即:select * from stu_info where id = (select id from my_class where class_name='class1');

-- 列子查詢,一般返回的結果比較多,一列多行,需要使用in作爲條件匹配,其實在MySQL中還有幾個類似條件
-- all,some,any, =any() === in , any = some, =all
| id | number  | name | gender | age  | c_id |
+----+---------+------+--------+------+------+
|  1 | 7150000 | 張三 | 男     |   18 |    1 |
|  2 | 7150001 | 李四 | 女     |   19 |    2 |
|  3 | 7150002 | 王五 | 女     |   19 |    2 |
|  4 | 7150003 | 趙柳 | 男     |   18 |    3 |
|  5 | 7150004 | 小名 | 男     |   18 |    3 |
|  6 | 7150004 | 小剛 | 女     |   18 |    1 |
+----+---------+------+--------+------+------+
+----+------------+----------+
| id | class_name | room_add |
+----+------------+----------+
|  1 | java001    | a330     |
|  2 | php002     | a331     |
|  3 | pyhon003   | a332     |
+----+------------+----------+

-- 查詢所有在讀班級的學生(班級表中存在的班級)

1.確定數據源:學生
select * from my_stu where c_id in(?);
2.確定有效班級的id,所有班級id
select id from my_class;

select * from my_stu where c_id in(select id from my_class);

-- any, some, all
insert into my_stu values(null, '0715005', '小芳', '女', 16,5);

mysql> select * from my_stu where c_id =any (select id from my_class);
+----+---------+------+--------+------+------+
| id | number  | name | gender | age  | c_id |
+----+---------+------+--------+------+------+
|  1 | 7150000 | 張三 | 男     |   18 |    1 |
|  2 | 7150001 | 李四 | 女     |   19 |    2 |
|  3 | 7150002 | 王五 | 女     |   19 |    2 |
|  4 | 7150003 | 趙柳 | 男     |   18 |    3 |
|  5 | 7150004 | 小名 | 男     |   18 |    3 |
|  6 | 7150004 | 小剛 | 女     |   18 |    1 |
+----+---------+------+--------+------+------+
6 rows in set (0.00 sec)

mysql> select * from my_stu where c_id =some (select id from my_class);
+----+---------+------+--------+------+------+
| id | number  | name | gender | age  | c_id |
+----+---------+------+--------+------+------+
|  1 | 7150000 | 張三 | 男     |   18 |    1 |
|  2 | 7150001 | 李四 | 女     |   19 |    2 |
|  3 | 7150002 | 王五 | 女     |   19 |    2 |
|  4 | 7150003 | 趙柳 | 男     |   18 |    3 |
|  5 | 7150004 | 小名 | 男     |   18 |    3 |
|  6 | 7150004 | 小剛 | 女     |   18 |    1 |
+----+---------+------+--------+------+------+
6 rows in set (0.00 sec)

mysql> select * from my_stu where c_id =all (select id from my_class);
Empty set (0.00 sec)

--- 否定結果
select * from my_stu where c_id !=any (select id from my_class); -- 所有結果 Null除外,與後面的所有進行比較,有一個不等於的就爲true
select * from my_stu where c_id !=some (select id from my_class); -- 所有結果, null除外
select * from my_stu where c_id !=all (select id from my_class); -- 和所有的比較,都不相等,就爲true

-- 行子查詢,返回的結果可以是多行多列(一行多列)
-- 需求,要求查詢整個學生中,年齡最大同時身高最高的學生
alter my_stu add height tinyint unsigned;
update my_stu set height = round(rand() * 50 + 150);
1.確定數據源
  select * from my_stu where age = (?) and height = ?;
2.確定最大年齡和最高的身高
select max(age), max (height) from my_student;

select * from my_stu where
age = (select max(age) from my_stu)
and
height = (select max(height) from my_stu);

-- 行子查詢,需要構造行元素
select * from my_stu where
-- (age, height)稱之爲行元素
(age, height) = (select max(age), max(height) from my_stu);-- max後面千萬別加空格

-- 表子查詢,子查詢返回的結果是個多行多列的二維表,子查詢返回的結果是當做二維表來使用
-- 需求,找出每個班最高的學生
-- 1.確定數據源,按照學生身高進行排序
select * from my_stu order by height desc;
-- 2.

select * from
-- 構建表
(select * from my_stu order by height desc) as student
group by c_id;


-- exists子查詢,用來判斷某些條件是否滿足(跨表),exists是接在where之後,exists返回的結果只有0和1
-- 需求,查所有的學生,前提是班級存在
1.確定數據源
select * from my_stu where ?;
2.確定條件是夠滿足
exists (select * from my_class);

select  * from my_stu where
exists (select * from my_class);

----------------------視圖------------------------------
-- view,是一種有結構(有二維表結構),但是沒結果(結構中不真實存放數據)的虛擬表,
-- 虛擬表的結構來源不是自己定義,而是從對應的基表中產生(視圖的數據來源).

-- 創建視圖
create view 視圖名字 as select 語句;-- select 語句可以是普通查詢,可以是連接查詢,可以是聯合查詢,可以是子查詢

-- 創建單表視圖:基表只有一個
create view V1 as select * from my_stu;
create view V2 as select * from my_class;

-- 字段名重複的問題,會導致創建失敗
create view V3 as select * from my_stu as s left join my_class c on s.c_id = c.id;
-- 改成
create view V3 as select s.*, c.class_name, c.room_add from my_stu as s left join my_class c on s.c_id = c.id;

-- 查看視圖,查看視圖的結構,視圖是一張虛擬表,表的所有查看方式都適用於視圖
show tables;
desc V1;
show create table V1;

-- 視圖比表還是有一個關鍵字區別,view,查看視圖結構的創建語句可以用view關鍵字代替table
show create view V2;
-- 視圖一旦創建,系統會在視圖對應的數據庫文件夾下創建一個對應的結構文件:frm

-- 視圖的使用
-- 視圖主要是爲了查詢,將視圖當做表一樣查詢即可
-- 視圖使用:
select * from V1;
select * from V2;
select * from V3;
mysql> select * from V3;
+----+---------+------+--------+------+------+--------+------------+----------+
| id | number  | name | gender | age  | c_id | height | class_name | room_add |
+----+---------+------+--------+------+------+--------+------------+----------+
|  1 | 7150000 | 張三 | 男     |   18 |    1 |    188 | java001    | a330     |
|  2 | 7150001 | 李四 | 女     |   19 |    2 |    176 | php002     | a331     |
|  3 | 7150002 | 王五 | 女     |   19 |    2 |    165 | php002     | a331     |
|  4 | 7150003 | 趙柳 | 男     |   18 |    3 |    195 | pyhon003   | a332     |
|  5 | 7150004 | 小名 | 男     |   18 |    3 |    183 | pyhon003   | a332     |
|  6 | 7150004 | 小剛 | 女     |   18 |    1 |    179 | java001    | a330     |
|  7 | 0715005 | 小芳 | 女     |   16 |    5 |    198 | NULL       | NULL     |
+----+---------+------+--------+------+------+--------+------------+----------+
-- 視圖的執行,本質就是執行封裝的select語句。

-- 視圖的修改
-- 視圖本身不可修改,但是視圖的來源是可以修改的,修改視圖,修改視圖本身的來源語句(select語句)
alter view 視圖名字 as 新的select語句
alter view V1 as select id, number, name gender from my_stu;
mysql> select * from V1;
+----+---------+--------+
| id | number  | gender |
+----+---------+--------+
|  1 | 7150000 | 張三   |
|  2 | 7150001 | 李四   |
|  3 | 7150002 | 王五   |
|  4 | 7150003 | 趙柳   |
|  5 | 7150004 | 小名   |
|  6 | 7150004 | 小剛   |
|  7 | 0715005 | 小芳   |
+----+---------+--------+

-- 視圖刪除
drop view 視圖名字;
create view V4 as select * from my_stu;
drop view V4; -- 不能用table代替view

-- 視圖意義:1.視圖可以節省SQL語句,將一條複雜的SQL語句使用視圖進行保存,以後可以直接對
--            視圖進行操作
--          2.數據安全,視圖操作是主要針對查詢的,如果對視圖結構進行處理(刪除),不會影響
--            基表數據(相對安全)
--          3.視圖往往實在大項目中使用,而且是多系統使用,可以對外提供有效、、有用的數據,但是
--            隱藏關鍵的數據,數據安全
--          4.視圖可以對外提供友好型,不同的視圖提供不同的數據,對外好像專門設計
--          5.視圖可以更好的(更容易)的進行權限控制

--視圖數據操作,可以進行數據寫操作,但是有甚多限制
-- 將數據直接在視圖上操作

-- 新增數據,直接對視圖進行數據新增。
    -- 1.多表視圖不能新增數據
    -- 2.可以向單表視圖插入數據,但是視圖中包含的字段必須有基表中所有不能爲空的數據字段(或者沒有默認字段)
    -- 3.視圖是可以像基表插入數據的
insert into V2 values (9, 'java0012', 'A322');

+----+------------+----------+
| id | class_name | room_add |
+----+------------+----------+
|  1 | java001    | a330     |
|  2 | php002     | a331     |
|  3 | pyhon003   | a332     |
|  9 | java0012   | A322     |
+----+------------+----------+

-- 刪除數據
  -- 1.多表數據不能刪除
  delete from V3 where id = 1; --ERROR 1395 (HY000): Can not delete from join view 'stuinfo.v3'
  -- 2.單表視圖可以刪除數據
  delete from V2 where id = 9;
+----+------------+----------+
| id | class_name | room_add |
+----+------------+----------+
|  1 | java001    | a330     |
|  2 | php002     | a331     |
|  3 | pyhon003   | a332     |
+----+------------+----------+

--更新數據
-- 理論上來講,無論是單表視圖,還是多表視圖
-- 更新限制:with check option,如果對視圖在新增的時候,限制了某個字段有限制,那麼在對視圖進行數據
-- 更新操作時,系統會進行驗證,要保證更新之後,數據依然可以被

-- 視圖:age字段限制更新
create view V5 as
select * from my_stu where age > 18 with check option ;
+----+---------+------+--------+------+------+--------+
| id | number  | name | gender | age  | c_id | height |
+----+---------+------+--------+------+------+--------+
|  2 | 7150001 | 李四 | 女     |   19 |    2 |    176 |
|  3 | 7150002 | 王五 | 女     |   19 |    2 |    165 |
+----+---------+------+--------+------+------+--------+
-- 表示視圖的數據來源都是年齡大於18歲的,更新的時候不能將年齡大於18 的改成小於18的;
update V5 set age = 15 where id = 2;
-- ERROR 1369 (HY000): CHECK OPTION failed 'stuinfo.v5'

-- 獲取所有班級中,最高的一個學生
create view V6 as
select * from my_stu order by height desc;
select * from V6 group by c_id; --結果無效

-- 等效於
select * from my_stu group by  c_id order by height desc;

-- 視圖算法:系統對視圖以及外部查詢視圖的select語句的一中解析方式
-- 視圖算法分爲三類:
  -- undefine : 未定義,這不是一中實際使用的算法,是一種推卸責任的算法,告訴系統,視圖沒有定義
  --            讓系統自己看着辦
  -- temptable : 臨時表算法,系統應該先執行視圖的select語句,後執行外部查詢語句
  -- merger : 合併算法,系統應該先將視圖對應的select語句與外部查詢視圖的select語句進行合併
  --           然後執行(效率高)

-- 算法指定:
create ALGORITHM=temptable view V7 as
select * from my_stu order by height desc;
-- 再查
select * from V6 group by c_id;

-- 視圖算法選擇:如果視圖的select語句中包含一個查詢子句(五子句where,group,order,limit,having)
--              而且很有可能順序比外面的查詢語句要靠後,使用算法temptable,其他情況不用指定(默認即可)

-----------數據的備份與還原-----------
-- 備份:將當前已有的數據或記錄保留
-- 還原:將已經保留的數據恢復到對應的表中

-- 1.防止數據丟失,被盜、誤操作
-- 2.保護數據記錄,

-- 數據備份、還原方式有多種:1.直接數據表備份2.單表數據備份3.增量備份

--1.數據表備份,不需要通過sql來備份,直接進入到數據庫文件夾,複製對應的表結構以及數據文件,
             -- 以後還原的時候,直接將備份的內容放進去即可

  -- 前提條件:根據不同的存儲引擎由不同的區別,主要有兩種,innodb和mysiam(免費)
  -- innodb, 只有表結構,數據全部存儲到ibdatal文件中
  -- myisam,索引、數據、結構文件分開存儲
  -- 這種文件備份通常適用於myisam引擎,複製到其他數據庫可以直接使用,innodb只能在創建的
  -- 那個庫裏面使用

-- 單表數據備份,每次只能備份一張表,只能備份數據(表結構不能備份),通常的使用,將表結構
-- 的數據導出到文件
-- 1.備份,從表中保存一部分數據到外部文件中(outfile)
select */字段列表 into outfile 文件所在路徑 from 數據源; -- 前提是,外部文件不存在
-- 單表數據備份
select * into outfile 'D:/Program/dabase.bak/my_stu' from my_stu;
-- 千萬不能用txt打開,會改變字符集,內部字段之間默認用tab隔開

-- 高級備份,自己制定字段和行的處理方式
select */字段列表 into outfile 文件所在路徑 filds 字段處理 lines 行處理 from 數據源;
--filds:字段處理
  -- enclosed by:字段使用什麼內容包裹,默認是,空字符串
  -- terminated : 字段以什麼結束,默認是\r,tab鍵
  -- escaped by : 特殊符號用什麼方式處理,默認是“\\”,使用反斜槓轉義
-- lines:行處理
  -- starting by : 每行以什麼開始,默認是“”,空字符串
  -- endding by : “‘\r’”
select * into outfile 'D:/Program/dabase.bak/my_stu_2'
-- 字段處理
fields enclosed by '"' -- 數據使用栓引號包裹
terminated by '|' -- 使用豎線分割字段數據
-- 行處理
lines
starting by 'START:'
from my_stu;

START:"1"|"07150000"|"張三"|"男"|"18"|"1"|"188"
START:"2"|"07150001"|"李四"|"女"|"19"|"2"|"176"
START:"3"|"07150002"|"王五"|"女"|"19"|"2"|"165"
START:"4"|"07150003"|"趙柳"|"男"|"18"|"3"|"195"
START:"5"|"07150004"|"小名"|"男"|"18"|"3"|"183"
START:"6"|"07150004"|"小剛"|"女"|"18"|"1"|"179"
START:"7"|"0715005"|"小芳"|"女"|"16"|"5"|"198"

-- 數據還原:將一個在外部保存的數據重新恢復到表中(如果表結構不存在,那麼無法還原)
load data infile 文件所在路徑 into table 表名(字段列表) fields 字段處理 lines 行處理;

mysql> delete from my_stu; -- 保留表結構,僅刪除數據
Query OK, 7 rows affected (0.05 sec)

mysql> select * from my_stu;
Empty set (0.00 sec)

load data infile 'D:/Program/dabase.bak/my_stu_2' into table my_stu fields
enclosed by '"' -- 數據使用栓引號包裹
terminated by '|' -- 使用豎線分割字段數據
-- 行處理
lines
starting by 'START:';

mysql> select * from my_stu;
+----+---------+------+--------+------+------+--------+
| id | number  | name | gender | age  | c_id | height |
+----+---------+------+--------+------+------+--------+
|  1 | 7150000 | 張三 | 男     |   18 |    1 |    188 |
|  2 | 7150001 | 李四 | 女     |   19 |    2 |    176 |
|  3 | 7150002 | 王五 | 女     |   19 |    2 |    165 |
|  4 | 7150003 | 趙柳 | 男     |   18 |    3 |    195 |
|  5 | 7150004 | 小名 | 男     |   18 |    3 |    183 |
|  6 | 7150004 | 小剛 | 女     |   18 |    1 |    179 |
|  7 | 0715005 | 小芳 | 女     |   16 |    5 |    198 |
+----+---------+------+--------+------+------+--------+
7 rows in set (0.00 sec)

-- 小訪問量的備份方式sql備份,備份的是SQL語句,系統會對錶以及表結構進行處理,
-- 變成對應的SQL語句,然後恢復的時候執行SQL語句就行了

-- 備份:mysql沒有提供備份指令:需要利用mysql提供的軟件,mysqldump.exe
-- mysqldump.exe也是一種客戶端,需要操作服務器,必須連接認證
mysqldump -hPup [數據表名字1[數據表名字2...]] > 外部文件.sql

mysqldump -uroot -p12345678 stuinfo my_stu > D:/Program/dabase.bak/my_stu_3.sql
-- 注意:這個不是sql語句,最後不要分號



--結果:
-- MySQL dump 10.13  Distrib 5.5.50, for Win64 (x86)
--
-- Host: localhost    Database: stuinfo
-- ------------------------------------------------------
-- Server version	5.5.50

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `my_stu`
--

DROP TABLE IF EXISTS `my_stu`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `my_stu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `number` varchar(10) NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  `gender` enum('男','女') DEFAULT NULL,
  `age` tinyint(3) unsigned DEFAULT NULL,
  `c_id` int(11) NOT NULL,
  `height` tinyint(3) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `my_stu`
--

LOCK TABLES `my_stu` WRITE;
/*!40000 ALTER TABLE `my_stu` DISABLE KEYS */;
INSERT INTO `my_stu` VALUES (1,'7150000','張三','男',18,1,188),(2,'7150001','李四','女',19,2,176),(3,'7150002','王五','女',19,2,165),(4,'7150003','趙柳','男',18,3,195),(5,'7150004','小名','男',18,3,183),(6,'7150004','小剛','女',18,1,179),(7,'0715005','小芳','女',16,5,198);
/*!40000 ALTER TABLE `my_stu` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2018-06-24 16:30:59


-- 整庫備份:
mysqldump -uroot -p12345678 stuinfo > D:/Program/dabase.bak/stuinfo.sql


-- sql還原數據,第一種,使用mysql客戶端還原
              mysql -hPup 數據庫名字 < 備份文件目錄
              mysql -uroot -p12345678 stuinfo < D:/Program/dabase.bak/my_stu_3.sql
          --  第二種,使用SQL指令還原
              source D:/Program/dabase.bak/my_stu_3.sql;

-- sql備份優缺點:
    -- 優點: 可以備份結構
    -- 缺點:會浪費空間,會額外增加sql指令

-- 增量備份,大項目,不是針對數據或者sql指令,是針對mysql文件日誌文件進行備份
-- 增量備份:指定時間段開始備份,備份數據不會重複(不會浪費 空間),所有的操作都會備份

---------------------********************回顧
-- 外鍵:關聯關係,表中字段指向另外一張表的主鍵
      -- 外鍵條件,字段類型必須一致,存儲引擎必須爲innodb
      -- 一張表中可以有多個外鍵,不能重名
      -- 外鍵約束,子表約束,不能插入父表不存在的記錄
                --父表約束有三種:默認district嚴格,cascade,set null,on delete set null, on update cascade
-- 聯合查詢:union,多表合併和單表不同查詢條件,要求字段數量一致
    -- 聯合查詢使用order by,select語句必須用括號,還必須用limit

-- 子查詢:一條查詢語句中又出現了另外一條查詢語句
    -- 分類:按位置(from, where, exists),按返回結果(用途,標量,列,行,表)


-- 視圖:view,1.節省sql語句 2.安全性控制,視圖本質是虛擬表,有結構無數據,
        -- 視圖數據操作:多表只能改,單表合一增刪改(增刪有條件限制)
        -- 視圖算法:三種,undifined 未定義,temptable臨時表(做兩次)和merge合併(效率高,只做一次)

-- 文件備份與還原,
        -- 文件備份:存儲引擎(myisam適用,直接複製粘貼)
        -- 單表數據備份:只能備份數據
        -- SQL備份:備份的是SQL指令(mysqldump.exe 用客戶端還原或source指令)
        -- 增量備份:備份系統日誌文件

--------------------------事務-------------------------------

-- 需求,有一張銀行賬戶表,有A賬戶給B賬戶轉賬,A賬戶先減少,B賬戶增加,但是A操作完
      -- 之後斷電了。

-- 解決方案:A減少錢,但是不應該立即修改數據表,B收到錢之後,同時修改數據表
-- 事務安全問題
  -- 事務:transaction, 一系列要發生的連續的操作
  -- 事務安全:一種保護連續操作同時滿足(實現)的一種機制

  -- 事務安全的意義:保證數據操作的完整性

-- 創建一個賬戶表
create table my_account(
number char(16) not null unique comment '賬戶',
name  varchar (20) not null,
money decimal (10, 2) default 0.0 comment '賬戶餘額'
)charset utf8;

-- 插入數據
insert into my_account values
('0000000000000001', '張三', 1000),
('0000000000000002', '李四', 2000),
('0000000000000003', '王五', 700);

update my_account set money = money - 1000 where id = 1;

-- 事務操作分爲兩種:自動事務(默認的),手動事務
-- 手動事務:操作流程
      -- 1.開啓事務:高速系統以下所有操作(寫)不要直接寫到事務表,而是寫到事務日誌
          start transaction ;
      -- 2.進行事務操作:一系列操作
        -- a. 李四賬戶減少
        update my_account set money = money - 1000 where number = '0000000000000001';

        -- b. 張三賬戶增加
        update my_account set money = money + 1000 where number = '0000000000000002';

        (此時用另外的賬戶看,數據沒有變,自己的賬戶看,變了)
        -- c. 選擇性的將日誌文件操作的結果保存到數據表(同步)或者說直接清空事務日誌(原來操作全部清空)
              --c1.提交事務
              commit ;
              --c2.回滾事務
              rollback;
-- 事務原理:事務開啓後,所有的操作都會臨時保存到事務日誌中,事務日誌只有在得到commit命名纔會同步到數據表,其他任何情況都會清空

-- 回滾點:在某一個成功的操作完成之後,後續的操作有可能成功有可能失敗,但是不管成功還是失敗,前面
         -- 操作都已經成功,可以在當前成功的位置,設一個點,可供後續失敗錯做返回到該位置,而不是返回
         -- 所有操作,這個點稱之爲回滾點
  -- 設計回滾點語法:
  save point 回滾點名字;
  -- 回滾


-- 事務處理:張三加錢
update my_account set money = money + 10000 where number = 0000000000000001;

-- 設置回滾點
savepoint sp1;

-- 銀行扣稅
update my_account set money = money - 10000 * 0.05 where number = 0000000000000002;

-- 回滾到回滾點
rollback to sp1;

-- 繼續操作
update my_account set money = money - 10000 * 0.05 where number = 0000000000000001;

-- 查看結果
select * from my_account;

-- 提交
commit ;

-- 自動事務處理,在mysql中,默認的都是自動事務處理,用戶操作完會立即同步到數據表中
-- 自動事務:系統通過autocommit變量控制
show variables like 'autocommit';
-- 關閉自動提交
set autocommit = 0;
-- 關閉之後需要手動來選擇處理:commit提交,rollback回滾
-- 注意,通常都會使用自動事務

-- 事務特性:ACID
          -- A:atomic 原子性,事務整個操作是一個整體,不可分割,要麼全部成功,要麼全部失敗
          -- C:consistency:一致性,實務操作前後,數據表中的數據沒有變化
          -- I:isolation,隔離性,實務操作不互相影響
          -- D:durability,持久性,數據一旦改變,不可改變,永久的改變數據表數據

-- 鎖機制:innodb默認是行鎖,但是如果在事務操作的過程中,沒有使用到索引,系統會自動全表檢索數據,自動升級爲表鎖
        -- 行鎖:只有當前行被鎖住,別的用戶不能操作
        -- 表鎖:整張表被鎖住,別的用戶都不能操作


-- 變量,分爲兩種,系統變量和自定義變量
  -- 系統變量:系統定義好的變量,大部分用戶不需要使用系統變量,系統變量是用來控制服務器的表現的
            -- 如:autocommit,auto_increment_increament
            -- 查看所有系統變量
                show variables;
            -- 查看具體變量值:任何一個有數據返回值的內容都是有select查看
                select @@version, @@autocommit;
            -- 修改系統變量分爲兩種,會話級別和全局級別
            -- 修改會話級別
                set autocommit = 0;
                set @@變量名 = 值;
            -- 全局級別,一次修改,永久生效
                set global 變量 = 值;

-- 自定義變量
            -- 系統爲了區分系統變量,用@區別系統變量
            set @name = 值;
            set @name = '張三';
            select @name;
            -- 因爲在SQL中=常被用作比較,所以爲了區分,通過:=來賦值

-- mysql中允許從數據表中獲取數據,然後賦值給變量,兩種方式
    --方案1:邊賦值,邊查看結果
    select @變量名 := 字段名 from 數據源; -- 如果使用=會變成比較符號
    select @name := name from my_stu;
    -- 方案2:只有賦值不看結果,要求很嚴格,數據記錄最多隻允許獲取一條mysql不支持數組
    select 字段列表 fro m 表名 into 變量列表;
    select name, age from my_stu where id = 2 into @name, @age;

-- 所有自定義變量都是會話級別,當前客戶端檔次連接有效
-- 所有自定義變量不區分數據庫(用戶級別)

-- 需求:有兩張表,一張訂單表,一張商品表,每生成一個訂單,意味着商品的庫存就要減少

-- 觸發器,trigger,事先爲某張表綁定好一段代碼,當表中的某些內容發生改變時(增刪改)
        -- 系統會自動觸發代碼,執行。
-- 觸發器:事件類型,觸發時間,觸發對象
      -- 事件類型:增(insert)刪(delete)改(update),三種類型
      -- 觸發時間:前(before)、後(after)
      -- 觸發對象:表中的每一條記錄(行)

-- 一張表中只能擁有一種觸發時間的一種類型的觸發器:最多一張表能有留個觸發器

-- 創建觸發器,在sql中,沒有大括號,都是用賭贏的字母符號代替的
-- 基本語法
-- 臨時修改語句結束符
delmiter 自定義符號,後續代碼中只有碰到自定義符號纔算結束

create trigger 觸發器名稱 觸發時間 事件類型 on 表名 for each row
begin  --代表左大括號,開始
  --裏面就是觸發器的內容
end -- 代表右大括號, 結束
-- 語句結束符
自定義符號
-- 將臨時修改過來
delmiter ;

create  table my_goods(
id int primary key auto_increment,
name varchar (20) not null,
price decimal (10,2) not null,
inv int comment '庫存數量'
)charset utf8;

insert into my_goods values
(null, 'iphone', 5288, 100),
(null, 'iphoneS6', 6000, 100),
(null, 'vivo', 3000, 100);

create table my_order(
id int primary key auto_increment,
g_id int not null comment '商品id',
g_number int comment '商品數量'
)charset utf8;

-- 觸發器:訂單生成一個,商品庫存減少一個
-- 臨時修改語句結束符
delimiter $$
create trigger after_order after insert on my_order for each row
begin
update my_goods set inv = inv - 1 where id = 2;
end
-- 結束觸發器
$$
-- 修正
delimiter  ;

-- 查看所有觸發器
show triggers [like pattern];
-- 查看觸發器創建語句
show create trigger after_order;

-- 所有的觸發器,都會保存到一張表中:information_schema.triggers
select * from information_schema.triggers;

-- 使用觸發器,不需要手動調用,當某種情況發生時,會自動觸發(訂單裏面插入記錄之後)
mysql> select * from my_goods;
+----+----------+---------+------+
| id | name     | price   | inv  |
+----+----------+---------+------+
|  1 | iphone   | 5288.00 |  100 |
|  2 | iphoneS6 | 6000.00 |  100 |
|  3 | vivo     | 3000.00 |  100 |
+----+----------+---------+------+

mysql> select * from my_order;
Empty set (0.00 sec)

-- 插入訂單
insert into my_order values (null, 1, 2);
-- 查看goods表
mysql> select * from my_goods;
+----+----------+---------+------+
| id | name     | price   | inv  |
+----+----------+---------+------+
|  1 | iphone   | 5288.00 |  100 |
|  2 | iphoneS6 | 6000.00 |   99 |
|  3 | vivo     | 3000.00 |  100 |
+----+----------+---------+------+
-- 說明:觸發器的確工作了,當插入訂單時,商品確實減少了,
--  問題是減少的數量並不是訂單的數量,而是固定死的減少的數量,——》觸發器有問題

-- 觸發器修改:不能修改
-- 觸發器刪除:drop trigger 觸發器名字;
-- 刪除觸發器
drop trigger after_order;

-- 觸發器記錄:不管觸發器是否出發了,只要當某種操作準備執行,系統就會將當前要操作的
          -- 記錄的當前狀態和即將執行之後的新的狀態分別保留下,供觸發器使用;
          -- 其中,要操作的當前狀態保存到old中,操作之後的狀態保留在new之中
          -- old代表的是舊記錄
          -- new代表的是新記錄,假設發生之後的結果
          -- 刪除的時候是沒有new的,插入的時候是沒有old的
      --old和new都是代表記錄本身,任何一條記錄除了有數據,還有字段名字
      -- 使用方式:old.字段名 / new.字段名

delimiter $$
create trigger after_order after insert on my_order for each row
begin
update my_goods set inv = inv - new.g_number where id = new.g_id;
end
-- 結束觸發器
$$
-- 修正
delimiter  ;

-- 插入訂單
insert into my_order values (null, 1, 2);
mysql> select * from my_goods;
+----+----------+---------+------+
| id | name     | price   | inv  |
+----+----------+---------+------+
|  1 | iphone   | 5288.00 |  100 |
|  2 | iphoneS6 | 6000.00 |   99 |
|  3 | vivo     | 3000.00 |  100 |
+----+----------+---------+------+
3 rows in set (0.00 sec)

mysql> select * from my_order;
+----+------+----------+
| id | g_id | g_number |
+----+------+----------+
|  1 |    1 |        2 |
+----+------+----------+
1 row in set (0.00 sec)

mysql> insert into my_order values (null, 1, 2);
Query OK, 1 row affected (0.10 sec)

mysql> select * from my_goods;
+----+----------+---------+------+
| id | name     | price   | inv  |
+----+----------+---------+------+
|  1 | iphone   | 5288.00 |   98 |
|  2 | iphoneS6 | 6000.00 |   99 |
|  3 | vivo     | 3000.00 |  100 |
+----+----------+---------+------+
3 rows in set (0.00 sec)

-- 說明,觸發器正確。

-- ----------------- 代碼執行結構
-- 代碼執行結構有三種:
  -- 順序結構
  -- 分支結構
  -- 循環結構

-- 分支結構:事先準備多個代碼塊,按照條件選擇性執行某段代碼,在sql中,只有if分支
-- 基本語法:
if 條件判斷 then
  -- 滿足條件要執行的代碼
else
  --不滿足條件要執行的代碼
end if;

-- 觸發器結合if分支,判斷商品訂單夠不夠
-- 修改語句結束符
delimiter %%

create trigger before_order before insert on my_order for each row
begin
  -- 判斷商品庫存是否足夠
  -- 獲取商品庫存
  select inv from my_goods where id = new.g_id into @inv_count;
  -- 比較庫存
  if @inv_count < new.g_number then
    -- 庫存不夠,觸發器沒有一個能否阻止時間發生的能力
    insert into XXX values(XXX); -- 暴力報錯
  end if;
end
%%
delimiter ;

-- 插入訂單
insert into my_order values(null, 1, 10000);
-- ERROR 1146 (42S02): Table 'stuinfo.XXX' doesn't exist
mysql> select * from my_order;
+----+------+----------+
| id | g_id | g_number |
+----+------+----------+
|  1 |    1 |        2 |
|  2 |    1 |        2 |
+----+------+----------+

-- 循環結構:某段代碼在指定條件下重複執行
-- while 循環(沒有for循環)
while 條件判斷 do
-- 滿足條件要執行的代碼
-- 變更循環條件
end while;

-- 循環控制:在循環內部進行循環判斷和控制
-- mysql 中沒有對應的continue 、break ,但是有替代品
-- iterate <----> continue
-- leave <---> break

--使用方式:itrate/leave 循環名字
-- 定義循環名字
循環名字:while 條件 domain
    -- 循環體
    -- 循環控制
    leave/itrate 循環名字;
end while;

-- 函數:將一塊代碼塊封裝到一個結構中,在需要執行代碼塊的時候,調用結構即可執行(代碼複用)
-- 函數分爲兩類:系統函數和自定義函數

-- 系統函數:系統定義好的函數,直接調動即可,任何函數都有返回值,因此,函數的調用是
-- 通過select調用

-- mysql中,字符串的基本操作單位(最常見是字符)

-- 定義變量
set @cn = '世界你好';
set @en = 'hello world';

substring
char_length : 字符長度 -- 顯示出來不太對
length : 字節長度
-- 字符串截取
select substring(@cn, 1, 3); -- 函數一定要挨着括號

select char_length(@cn), char_length(@en), length(@cn), length(@en);

instr :判斷字符串是否在某個具體的字符串中存在,存在返回位置

select instr(@cn, '界'), instr(@en, 'll'), instr(@cn, '拜拜');
select instr(@cn, '世'),instr(@cn, '界'),instr(@cn, '你'),instr(@cn, '好');
+------------------+------------------+------------------+------------------+
| instr(@cn, '世') | instr(@cn, '界') | instr(@cn, '你') | instr(@cn, '好') |
+------------------+------------------+------------------+------------------+
|                1 |                3 |                5 |                7 |
+------------------+------------------+------------------+------------------+


lpad : 左填充,將字符串按照某個指定的填充方式,填充到指定的長度
select lpad(@cn, 20, '歡迎'), lpad(@en, 20, 'hey');

insert(str, pos, length, tarstr) 替換,找到目標位置的字符串,替換
select insert (@en, 3, 3, 'y');
strcmp

------------- 自定義函數------------
-- 函數要素:函數名,參數列表(形參和實參),返回值,函數體(作用域)

-- 創建函數:
create function 函數名 (形參列表) returns 數據類型 -- 規定要返回的數據類型
begin
-- 函數體
-- 返回值 return 類型(指定數據類型)
end

-- 簡單函數
create function display1() returns int
return 100;

-- 自定義函數與系統函數的調用方式是一樣的
mysql> create function display1() returns int
    -> return 100;
Query OK, 0 rows affected (0.03 sec)

mysql> select display1();
+------------+
| display1() |
+------------+
|        100 |
+------------+

-- 查看函數
-- 查看所有函數:
show function status [like 'pat'];
mysql> show function status\G
*************************** 1. row ***************************
                  Db: stuinfo             -- 函數屬於數據庫,只有在數據庫下纔可以調用
                Name: display1
                Type: FUNCTION
             Definer: root@localhost
            Modified: 2018-06-25 12:26:16
             Created: 2018-06-25 12:26:16
       Security_type: DEFINER
             Comment:
character_set_client: utf8
collation_connection: utf8_general_ci
  Database Collation: utf8_general_ci

-- 查看函數創建
show create function display1;

-- 修改函數:不能修改
-- 刪除函數
drop function 函數名;

-- 函數參數
-- 參數分爲兩類,定義時的參數叫形參,調用時的參數叫實參
-- 形參,必須指定數據類型
function 函數名 (形參名字 字段類型) returns 函數參數

-- 計算1到指定數值之間的和
delimiter $$
create function display2(int_1 int) returns int
begin

  set @i = 1; -- 定義條件變量,@符號定義的是全局變量
  set @res = 0;
  while @i <= int_1 do
    set @res = @res + @i;
    set @i = @i + 1;
  end while;

  return @res;
end
$$
delimiter ;

mysql> select display2(10);
+--------------+
| display2(10) |
+--------------+
|           55 |
+--------------+

-- 作用域
-- mysql中的作用域與js中的作用域完全一樣,全部變量可以在任何地方使用,局部變量只能在函數內部使用
-- 全局變量:使用set關鍵字定義,使用@符號標誌
-- 局部變量:使用declare關鍵字聲明,沒有@符號,所有的局部變量的聲明,必須在函數體開始之前

-- 求和 1 - n 之間 5的倍數不要
delimiter $$
create function display3(n int) returns int
begin
  declare i int default 1; -- 定義條件變量,@符號定義的是全局變量
  declare res int default 0;

  my_while:while i <= n do
    if i % 5 = 0 then
      set i = i + 1;
      iterate my_while;
    else
      set res = res + i;
      set i = i + 1;
    end if;
  end while;

  return res;
end
$$
delimiter ;

------------ 存儲過程
-- 存儲過程簡稱:過程,procedure,是一種用來處理數據的方式
-- 存儲過程是一種沒有返回值的函數,往往是完成數據的操作

-- 創建過程基本語法
create procedure name (args ...)
begin
  --過程體
end


-- 查看過程
show procedure status [like pattern];
--- 查看創建語句
show create PROCEDURE name;

-- 調用過程,過程沒有返回值,意味着不用select調用,過程有個專門的關鍵字:call

-- 調用
call pro_name();

-- 過程不能修改
-- 過程刪除
drop procedure name();


-- 過程參數,函數的參數需要數據類型指定,過程筆函數更加嚴格
-- 過程還有字節的類型限定:三種類型
  -- in,數據只是從外部傳入給內部使用(值傳遞),可以是數據,也可以是變量
  -- out,只允許過程內部使用(不用外部傳數據),給外部使用的(應用傳遞,外部的數據會被先清空才進去內部,只能是變量)
  -- inout,外部的可以在內部使用,而內部使用也可以給外部使用,典型的引用傳遞

-- 使用
create procedure name(in arg type, out arg2 type, inout arg3 type)

-- 過程參數
delimiter $$
create procedure pro2(in int_1 int, out int_2 int, inout int_3 int)
begin
  select int_1, int_2, int_3; -- int_2 一定是null
end $$
delimiter ;

-- 調用
mysql> set @int_1 := 1;
mysql> set @int_2 := 2;
mysql> set @int_3 := 3;
mysql> call pro2(@int_1, @int_2, @int_3);
+-------+-------+-------+
| int_1 | int_2 | int_3 |
+-------+-------+-------+
|     1 |  NULL |     3 |
+-------+-------+-------+

-- 存儲過程對於變量的操作(返回)是滯後的,是在存儲過程調用結束後纔將內部修改的值修改到變量上

delimiter $$
create procedure pro4(in int_1 int, out int_2 int, inout int_3 int)
begin
  select int_1, int_2, int_3; -- int_2 一定是null(三個變量當前局部變量)
  set int_1 = 10;
  set int_2 = 100;
  set int_3 = 1000;

  -- 查看局部變量
  select int_1, int_2, int_3;

  -- 查看全局變量
  select @int_1, @int_2, @int_3;

  -- 修改全局變量
  set @int_1 = 'a';
  set @int_2 = 'b';
  set @int_3 = 'c';

  -- 查看全局變量
  select @int_1, @int_2, @int_3;

end $$
delimiter ;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章