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;