1. 程式人生 > >MySQL 部分小結

MySQL 部分小結

資料庫概述

資料(Data)

對於計算機來說,資料就是計算機儲存的(有用的)資訊。既然是計算機儲存的,那麼實質上它就是一串二進位制資料(0、1),但是人始終還是要用這個資訊的,所以資料儲存不能是毫無章法的,它必須遵循一定的規則,資料格式定義了這種規則。

我們之前說過的資料型別就是一種資料格式,比如它規定了整型的數在記憶體中應該是什麼的,浮點型的應該是什麼樣子。

我們講作業系統的時候,作業系統的檔案系統也是一種資料格式。作業系統在處理讀寫磁碟檔案的時候,肯定要先知道這個檔案是什麼大概什麼格式的,比如word文件、文字文件等。

對於資料來說,最重要的就是它的格式,如果格式定義不清晰,資料就沒法儲存和處理。

但是即使我們定義清晰了,也還是會有問題。比如,Windows作業系統下的檔案,複製到Linux作業系統下,就可能識別不出來。也就是說,資料格式還一個系統相容性問題。對於檔案系統這一類資料表示形式來說,它是高度依賴於作業系統的。如果我們寫了一個軟體,這個軟體是把資料儲存到了比如Windows上,那麼即使這個軟體我們是使用的Java(跨平臺)語言開發的,遷移到Linux上,也不一定能夠使用。因為Java跨平臺,它解決的只是程式設計語言的跨平臺,對於資料,它無能為力。所以就要有一種機制,來實現資料格式跨平臺相容。

資料庫(DataBase)

資料庫,顧名思義,就是遵循一定資料格式的資料集合,可以認為它是對檔案系統的改進。它解決了不同作業系統之前,資料格式的相容性問題。也就是說,只要是同一個資料庫的資料檔案,即使是從Windows遷移到了Linux上,也可以正常處理的。

資料庫管理系統(DataBaseManagementSystem)

最靠近我們使用者(開發人員)的其實不是資料庫(這個概念要更接近硬體,更底層),我們通常說資料庫,其實是說資料庫管理系統(DBMS)。

DB是DBMS的一個組成部分(底層實現),對於一個DBMS來說,除了底層資料(DB),還有其他組成部分。比如儲存引擎,比如伺服器元件,比如UI(命令列,圖形化介面),還有其他的一些類似SQL直譯器,連線認證系統等。

一些應用廣泛的DBMS

 

畫紅框的是我們重點關注的。

Oracle,這是一種關係型資料庫。它的特點是閉源收費,但是功能強大穩定,而且有一支專業的技術支撐團隊。使用這種資料庫的一般是大型企業、銀行業、金融業。

MySQL,它也是關係型資料庫。它的特點是開源免費,功能還是不錯的,也比較穩定。通常使用這種資料庫的,是中小企業等。因為它是開源的,所以我們有些企業可以對它做定製化、二次開發,以支援自己特殊的業務。比如阿里。說它是目前應用最廣泛的DBMS是不誇張的。

Memcached、Redis,這2者是非關係型資料庫(Not Only SQL)。它們通常用K-V鍵值對的形式儲存資料,使用場景為快取(可做為關係型資料庫的補充)。它倆都是基於記憶體儲存的,它們的資料都是在記憶體中,所以訪問速度要快。Redis還提供了定期向磁碟中進行序列化的機制,它的IO效能也是非常高的。

MongoDB,這個也是NOSQL資料庫。它使用文件形式儲存資料。使用場景,通常是大資料量高併發訪問。它的效能也非常高。

 

 

DDL Data Definition Language,資料定義語言

use -

use test1; -- 使用庫

desc

desc hello; -- 查看錶結構

show

show tables; -- 查詢當前庫的所有表

show databases; -- 查詢當前連線的資料庫伺服器的所有資料庫

show create table student --檢視建表語句

show create database test1 --檢視建庫語句

select - 方法

select version();-- 展示已連線資料庫版本

select database();-- 展示當前使用的資料庫,可用use切換

create - 建庫、表

create database test1; -- 建庫

create table hello( -- 建表

age int,

`name` varchar(30),

money decimal(18,2)

)ENGINE=InnoDNB DEFAULT CHARSET=utf8;

create index idx_sc_c on student_score(cname); 建索引

 

drop - 刪,庫、表、欄位 慎重刪除!!!

drop database test; -- 刪庫

drop table ss; -- 刪表

alter table teacher drop birthday;

drop index idx_sc_c on student_score; 刪索引

 

alter - 修改 慎重!!!

rename

alter table hello

rename hello_info; -- 改表名

change

alter table hello_info

change money money_copy varchar(20); -- 改表的欄位名,順帶改型類

add

alter table hello_info

add address varchar(500); -- 給表加欄位,在最後加

alter table hello_info

add address varchar(500) after teacher_name; -- 少用

alter table student_score add key idx_sc_s_c(sname,cname)

ALTER table student

add PRIMARY KEY(teacher_id,id,name);  --加主鍵

ALTER table student

add FOREIGN KEY(teacher_id)references teacher(teacher_id); --加外來鍵

drop

alter table teacher drop birthday;      --刪欄位

alter table student_score drop key idx_sc_s_c; 刪除索引

modify

alter table hello_info

modify address varchar(600) comment'家庭住址'; -- 改型別,改大小

type

alter table teacher type=InnoDB; -- 修改表的儲存引擎

 

注意事項

通常,對於SQL語法中的關鍵字比如CREATE等,使用大寫,其他可以用小寫。

改型別的時候要注意,儘量把型別擴大(比如varchar(100)改成varchar(500)),不要反過來改。儘量不要跨型別改(比如把一個decimal改成了int)。

反引號(`):它的作用是為了區分開表名/欄位名與SQL語法關鍵字的。

primary key -主鍵 實體完整性

表建立時添主鍵:

create table teacher(

id int not null default 0,

name varchar(20) not null default ' ',

teacher_id int not null default 0,

PRIMARY KEY(teacher_id)

);

表建完時添主鍵:

create table student(

id int not null default 0,

`name` varchar(100) not null default '',

teacher_id int not null default 0

);

ALTER table student

add PRIMARY KEY(teacher_id,id,name);

 

foreign key - 外來鍵 關聯完整性

表建立時添外來鍵:

create table student_2(

id int not null default 0,

`name` varchar(100) not null default ' ',

student_id int not null default 0,

teacher_id int not null default 0,

PRIMARY KEY(student_id),

foreign key(teacher_id)references teacher(teacher_id)

);

表建完時添外來鍵:

create table student(

id int not null default 0,

`name` varchar(100) not null default ' ',

teacher_id int not null default 0,

PRIMARY KEY(id)

);

ALTER table student

add FOREIGN KEY(teacher_id)references teacher(teacher_id);

 

DML Data Manipulation Language資料操縱語言

 

insert

insert into student_2(id,name,student_id,teacher_id)

values(1,'東邪',1,2);

insert into student -- 不建議使用

values(103,'陸軍','男','1974-06-03','95031');

update

update person -- 更新資料,一定要加where限制!!否則會全表刪除

set id = 19,

`name` = '南帝',

salary = 99.0,

address = '大理'

where `name` = '南海';

delete

delete from teacher

where name='老王'; -- 刪除資料,一定要加where限制!!否則會全表刪除

 

 

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

查表的某欄位

union / union all

它倆的作用是把兩張表或者更多表合併成一張表

前者會去重(去重的依據是,UNION時SELECT出來的欄位如果對應相等則認為是同一條記錄,這的邏輯我們可以參考Java equals)

後者則不會去重,它會保留兩張表中的所有記錄,但是它效能高(因為去重操作要花時間),

儘量使用union all,把去重這個工作交給程式碼去完成,這樣可以減少MYSQL伺服器的壓力

 

使用union / union all的時候要注意:

1.參與合併的表,它們SELECT出來的欄位數量必須一致(強制規則)

2.參與合併的表,它們SELECT出來的欄位的型別建議一一對應(非強制,但是最好遵循這條規則)

3.參與合併的表,它們SELECT出來的欄位的順序建議一致(非強制,但是最好遵循這條規則)

 

單表查詢

常用的where條件

查詢語句select  *  from  student  where  A  and  B;篩選student表中既滿足條件A又滿足條件B的記錄。

而select  *  from  student  where  A  or  B;篩選的則是隻要滿足A或者B其中一個條件即可的記錄。

命令select  *  from  student  where  score  >  80;篩選的是student表中score欄位值大於80的記錄。

大於等於使用  >=。小於等於使用  <=。小於使用  <。等於使用  =。不等於使用  <>。

命令select  *  from  student  where  score>=60  and  score<=80;等價於

命令select  *  from  student  where  score  between  60  and  80;

命令select  *  from  student  where  score=10  or  score=20;等價於

命令select  *  from  student  where  score  in  (10,  20);

對score  in  (10,  20);的邏輯否是  score  not  in  (10,  20);

命令select  *  from  student  where  score  is  null;篩選student表中score欄位值為null的記錄。對is  null的邏輯否是 is  not  null。

 

去重distinct

select distinct teacher_id from student; - - 單一去重

select distinct teacher_id,id,name - - 聯合去重

from student;

排序order   by  欄位名   [desc|asc]

select * from  student order by score desc;按score欄位降序排序。升序則使用 asc。

select * from  student order by teacher_id asc, score desc;先按teacher_id升序排序,如果teacher_id相同,再按score降序排序。

限制條數limit 條數。通常與order by 配合使用

select * from  student order by score desc limit 3;求成績在前三名的學生

統計函式/聚合函式/分組查詢

MYSQL中有一類特殊的函式,用於統計,或者分組統計。常用的有求數量的count(1)(等價於count(*)),求最大值的max(欄位名),求最小值的min(欄位名),求平均值的avg(欄位名),求總和的sum(欄位名)。

如select  count(*)  from   student;用於計算student表中一共有多少條記錄。

而select  count(*)  from  student  where  A;用於計算student表中滿足條件A的記錄的條數

子查詢

子查詢又叫巢狀查詢。它通常可以位於SELECT後面 FROM後面 WHERE後面。3種使用場景。

select巢狀

當位於SELECT後面時,要注意

1.一定要在兩個表之間找好對應關係(teacher.id必須是主鍵或者必須保證teacher.id在teacher表中是唯一的)

2.子查詢中只能有一個欄位(子查詢的結果必須是一行一列)

 

使用子查詢的時候,建議大家養成使用別名的好習慣,這樣可以讓我們的查詢語句更加清晰。別名可以用來命令新欄位,也可以用來命名新表。

from巢狀

當位於FROM後面時,要注意

1.我們可以把子查詢當成一張表

2.必須要有別名

where巢狀

當位於WHERE後面時,要注意

1.用in而不要用=,可使用exists優化

2.子查詢中的SELECT後面只能有一個欄位(多個欄位的話會報錯,但可以使用條件限制其在特點條件只有一條)

 

表連線查詢

交叉連線(完全笛卡兒積)

    from t1,t2

    from t1 join t2

內連線

    隱式 from t1,t2 where

    顯示 from t1 innerjoin t2 on t1.xx=t2.xx

外連線

左外連線  left join table on

右外連線  right join table on

 

子查詢和連線查詢效能比較

連線查詢優於子查詢(因為資料庫對連線查詢有優化),子查詢和連線查詢都能實現二點情況下使用連線查詢

避免查詢語句中多個子查詢,最好一個,且子查詢結果最最好一個欄位(記錄越少越好)

不得已必須使用子查詢還得用子查詢

 

MySQL常用系統函式

系統資訊類

select version();顯示當前MySQL軟體的版本

SELECT DATABASE();顯示當前所處資料庫是哪個。(可以使用use  <資料庫名>切換所在庫)

字元類

如select  char_length('中國');返回字元個數。

如select  length('中國');返回字元所佔位元組數。MySQL中,一個UTF8編碼的漢字佔3個位元組。

如select  concat(  'a',  'b',  'c',  'd');返回  'abcd'。字串拼接函式(可變元引數)。

如select  concat_ws(  '=',  'a',  'b',  'c');返回  'a=b=c'。字串拼接函式(可變元引數,但是第一個引數必須是拼接間隔符)。但是可以指定拼接間隔符。

如select   upper('abcd');返回ABCD。將引數中所有小寫字母轉換為大寫。

如select  lower('ABCD');返回abcd。將引數中所有大寫字母轉換為小寫。

如select  substring(  '系統資訊類',  1,  3  );返回  系統信。第2個引數代表從1開始的第幾個字元,第3個引數代表擷取字元個數。

如select  trim('  abc  ');返回 abc。用於刪去引數左右的所有空格。

日期時間類

select  curdate();返回當前日期

select  curtime();返回當前時間

select CURRENT_TIME();

select CURRENT_DATE();

select  now();返回當前日期時間

select  unix_timestamp();返回當前日期時間對應的時間戳(單位秒)

select  unix_timestamp('2018-05-24 20:00:00');返回引數指定的日期時間對應的時間戳(單位秒)

select  from_unixtime(1527163397);返回引數指定時間戳(單位秒)對應的日期時間

select  datediff(  '2018-05-23',  now()  );返回兩個引數對應日期相差的天數(用第一個引數減第二個引數)

select  adddate( now(), -2 );返回指定天數前/後的日期時間(第一個引數是日期時間,第二個引數是天數,向後加是正數,向前減是負數)

條件判斷類

select  if(  <判斷條件>,  <條件為真時的返回值>,  <條件為假時的返回值>  );相當於Java中的三目運算子<判斷條件>  ?  <條件為真的返回值>  :  <條件為假的返回值>。

如select  if(1=1,  2,  3);返回2。

select  ifnull(<表示式或者欄位>,  <表示式或者欄位為NULL時的返回值>);通常用於給有可能有NULL的情況下的提供預設值。

 

MySQL擴充套件

sql注入

所謂SQL注入,就是通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字串,最終達到欺騙伺服器執行惡意的SQL命令。具體來說,它是利用現有應用程式,將(惡意的)SQL命令注入到後臺資料庫引擎執行的能力,它可以通過在Web表單中輸入(惡意)SQL語句得到一個存在安全漏洞的網站上的資料庫,而不是按照設計者意圖去執行SQL語句。

效能優化

慢查詢原因(通常DBMS都有自己的慢查詢日誌):

外部原因:記憶體太小,本地I/O瓶頸,網路I/O瓶頸;

內部原因:程式本身DB設計不合理,SQL語句使用不合理,無索引或者有索引但未充分利用。

學會使用explain分析簡單的查詢。

資料庫設計層面的優化

遵循資料庫設計三正規化(通俗地講就是每個欄位不可再拆分,儘量減少冗餘欄位);

欄位型別設計上,能使用數值就不要使用字串,能使用日期時間就不要使用字串,最好把欄位宣告為not null default 預設值;

為了避免表連線查詢,必要的冗餘欄位是可以設定的;

能提前建立的索引要提前建好(經常用在where、group by、order by中的欄位最好建索引)。

建立索引方式一:alter table student_score add key idx_sc_s_c(sname,cname);

刪除索引方式一:alter table student_score drop key idx_sc_s_c;

建立索引方式二:create index idx_sc_c on student_score(cname);

刪除索引方式二:drop index idx_sc_c on student_score;

如下圖,聯合索引,idx_sc_s_c,在紅框中的用法,是使用不到聯合索引的。

要想使用聯合索引,要麼把聯合索引中的欄位按順序全都用上(sname= and cname= ),要麼就使用最左邊的欄位(sname=)

 

 

SQL語句使用層面的優化

儘量不使用 select *,而是要具體指定欄位,比如select id, name...;

儘量不使用不等於<>;

不使用is null/is not null(雖然也會使用索引,但是效能損耗是由於default null的欄位要比not null的欄位多出額外的儲存空間來標識這個欄位的值是不是null);

不使用or連線不同的欄位;

不使用not in;

不在條件欄位上使用函式;

不使用前置模糊查詢(like '%a');等。

因為上面的使用方式都會產生全表掃描(當然,如果實在沒辦法優化,全表掃描就掃描吧)

索引優化

建立索引的欄位從內容上要有差異要有區分度。

索引提升的是讀效能,如果一張表的寫操作更多,則儘量不建或者少建索引。

使用where、group by、order by時,儘量充分利用建立索引的欄位。

資料匯入和匯出

先將老表資料匯出為本地磁碟檔案:

select * from  student into outfile 'stu_bak';

匯出時,如果outfile使用相對路徑,則基準目錄是/var/lib/mysql/庫名

如果是windows 7,則是C:/ProgramData/MySQL/MySQL Server 5.5/data/庫名

再建立一個和老表結構相同的新表:

create table student4 select * from student where 1=2; 建立表結構但不復制資料

或者新建立一個庫

create database aaa;

use aaa;

create table student_score select * from  lhl_test.student_score where false;

修改表的儲存引擎alter table student_score type=innodb;

後將匯出的本地磁碟檔案匯入新表:

load data local infile '/var/lib/mysql/lhl_test/stu_bak' into table student4;

如果是新建立庫的則是

load data local infile '/var/lib/mysql/lhl_test/student_score_bak' into table student_score;

匯入時,如果infile使用相對路徑,則基準目錄是家目錄。

如果是windows 7,則infile後面必須使用絕對路徑,\使用/代替

PS:

create table student4 select * from student where 1=1; 建立表結構同時複製資料

行轉列

建立一張表,用於練習:

CREATE TABLE `student_score` (

  `id` int(11) NOT NULL DEFAULT '0',

  `sname` varchar(100) NOT NULL DEFAULT '',

  `cname` varchar(100) NOT NULL DEFAULT '',

  `score` decimal(18,2) NOT NULL DEFAULT '0.00',

  PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

初始化資料後如下:

 

 

場景一(多行轉為一行多列):

 

 

可以使用下面的SQL語句(group by 與 case when結合使用即可實現):

select sname,

max(

case cname

when 'Java' then score

else 0

end

) Java,

max(

case cname

when 'MySQL' then score

else 0

end

) MySQL

from student_score

group by sname;

 

 ---------------------------------------------------

select sname,

max(case cname

when 'Java' then score

end) Java,

max(case cname

when 'MySQL' then score

end) MySQL

from student_score

group by sname;

就得到最終結果啦。

 

場景二(多行轉為一行一列):

 

 

第一步:拆分問題,先按分組的思路,只處理一個學員,如小李

select sname, cname, score

from student_score

where sname = '小李';

 

 

第二步:將課程名與成績拼接成一列

select sname,

concat(cname,'=',score) '各科成績'

from student_score

where sname = '小李';

 

 

第三步:利用group_concat函式將多行壓扁到一行

select sname,

group_concat(cname,'=',score) '各科成績'

from student_score

where sname = '小李';

 

 

第四步:修改分隔符(預設是逗號)

select sname,

group_concat(cname,'=',score  separator ' | ') '各科成績'

from student_score

where sname = '小李';

 

 

第五步:按課程名稱排序

select sname,

group_concat(cname,'=',score order by cname asc  separator ' | ') '各科成績'

from student_score

where sname = '小李';

 

 

最後一步:對全表應用group by

select sname,

group_concat(cname,'=',score order by cname asc  separator ' | ') '各科成績'

from student_score

group by sname;