1. 程式人生 > 資料庫 >SQL,何必在憶之一(基礎篇)

SQL,何必在憶之一(基礎篇)

還記得那是在2018年的十月的某個日子,雖早已入秋,但夏日的炎熱卻絲毫不減退散。那時的我正捧著一本SQL Server程式設計的白藍皮書與九棟315的狗子們,匆匆的走向j1-402進行了我們人生中第一次SQL資料庫的學習,時光總是戲人,現實總是玩笑。當初的幾個夥伴都走向了各行各業,而唯有我程式設計課,問啥啥不會,寫啥啥就廢的我進入了IT行業。說來實在嘲諷,緬懷那些我錯過的程式設計課,致那些年說過無數次“讓我學SQL,根本不可能”,我承認我打臉了。正如此章的title一般,“SQL語句, 何必在憶?”

很久之前就學了SQL,然而又忘記,今天正式系統的性的回顧一下,溫故而知新。可以為師矣

表屬性

表的屬性

儲存引擎:
InnoDB(預設的)
字符集和排序規則:
utf8
utf8mb4

列的屬性

約束(一般建表時新增):
primary key :主鍵約束
設定為主鍵的列,此列的值必須非空且唯一,主鍵在一個表中只能有一個,但是可以有多個列一起構成。 作為聚簇索引
not null      :非空約束
列值不能為空,也是表設計的規範,儘可能將所有的列設定為非空。可以設定預設值為0
unique key :唯一鍵
列值不能重複
unsigned :無符號
針對數字列,非負數。

其他屬性:
key :索引
可以在某列上建立索引,來優化查詢,一般是根據需要後新增
default           :預設值
列中,沒有錄入值時,會自動使用default的值填充
auto_increment:自增長
針對數字列,順序的自動填充資料(預設是從1開始,將來可以設定起始點和偏移量)
comment : 註釋

sql_mode

作用:影響sql執行行為,規範SQL語句的書寫方式(例如除數不能為0)

可以使用select @sql_mode檢視(各版本有所出入)

字符集(charset)及校對規則(Collation)

字符集:

  • utf8:最大儲存長度,單個字元最多3位元組
  • utf8mb4:最大儲存長度,單個字元最多4位元組

常用於建庫建表時

create database dbname charset utf8mb4;
# 檢視資料庫的字元集合
show create database dbname;

校對規則

每種字符集,有多種校對規則(排序),例如常見的ASCII編碼表

show collation;

作用:影響排序的操作

資料型別

text型別

Number型別

*:這些整數型別擁有額外的選項 UNSIGNED。通常,整數可以是負數或正數。如果新增 UNSIGNED 屬性,那麼範圍將從 0 開始,而不是某個負數。

Date型別

*即便 DATETIME 和 TIMESTAMP 返回相同的格式,它們的工作方式很不同。在 INSERT 或 UPDATE 查詢中,TIMESTAMP 自動把自身設定為當前的日期和時間。TIMESTAMP 也接受不同的格式,比如 YYYYMMDDHHMMSS、YYMMDDHHMMSS、YYYYMMDD 或 YYMMDD。

什麼是SQL語句

SQL語句是結構化查詢語言(Structured Query Language)的簡稱,是一種特殊目的的程式語言,是一種資料庫查詢和程式設計語言,用於存取資料以及查詢、更新和管理關係資料庫系統。

SQL語句的型別

資料查詢語言(:Data Query Language):其語句,也稱為“資料檢索”,用以從表中獲得資料,確定資料怎樣在應用程式給出。保留字是DQL(也是所有SQL)用得最多的動詞,其他DQL常用的保留字有WHERE,ORDER BY,GROUP BY和HAVING。這些DQL保留字常與其它型別的SQL語句一起使用。

(DML:Data Manipulation Language):其語句包括動詞、和。它們分別用於新增、修改和刪除。

事務控制語言(TCL):它的語句能確保被DML語句影響的表的所有行及時得以更新。包括COMMIT(提交)命令、SAVEPOINT(儲存點)命令、ROLLBACK(回滾)命令。

(DCL):它的語句通過GRANT或REVOKE實現許可權控制,確定單個使用者和使用者組對的訪問。某些RDBMS可用GRANT或REVOKE控制對個列的訪問。

資料定義語言():其語句包括動詞CREATE,ALTER和DROP。在資料庫中建立新表或修改、刪除表(CREATE TABLE 或 DROP TABLE);為表加入索引等。

指標控制語言(CCL):它的語句,像DECLARE CURSOR,FETCH INTO和UPDATE WHERE CURRENT用於對一個或多個表單獨行的操作。

比較常用的有DDL(資料定義語言)`DCL(資料控制語言)\DML(資料操作語言)\DQL(資料查詢語言)`

SQL

Client

?         (\?) Synonym for `help'.           # 幫助資訊                                                                                
clear     (\c) Clear the current input statement.  清空此行sql                                                                         
connect   (\r) Reconnect to the server. Optional arguments are db and host.                                                  
delimiter (\d) Set statement delimiter.                                                                                      
edit      (\e) Edit command with $EDITOR.                                                                                    
ego       (\G) Send command to mysql server, display result vertically. 格式化輸出
exit      (\q) Exit mysql. Same as quit.	退出登陸 ctrl(control) + d 
go        (\g) Send command to mysql server.
help      (\h) Display this help.
nopager   (\n) Disable pager, print to stdout.
tee       (\T) Set outfile [to_outfile]. Append everything into given outfile. # 記錄日誌(語句+結果) eg:tee /tmp/mysql.log
notee     (\t) Don't write into outfile. 不記錄日誌
pager     (\P) Set PAGER [to_pager]. Print the query results via PAGER.
print     (\p) Print current command.
prompt    (\R) Change your mysql prompt.
quit      (\q) Quit mysql.
rehash    (\#) Rebuild completion hash.
source    (\.) Execute an SQL script file. Takes a file name as an argument. 匯入腳步,相當於 <
status    (\s) Get status information from the server.
system    (\!) Execute a system shell command.
use       (\u) Use another database. Takes database name as argument.
charset   (\C) Switch to another charset. Might be needed for processing binlog with multi-byte charsets.
warnings  (\W) Show warnings after every statement.
nowarning (\w) Don't show warnings after every statement.
resetconnection(\x) Clean session context.

server

DDL 資料定義語言

在日常中DDL多用於庫、表的管理.

庫名與庫屬性

## 增
create database dbname
eg: create database dbname charset utf8mb4
## 刪
drop database dbname
## 改 (從小到大改, utf8 -> utf8mb4, 嚴格超集)
alter  database dbname 將修改的屬性名 將要修改的屬性值
## 查
show databases;

建庫規範:
1.庫名不能有大寫字母,不能太長(<30字元) 多平臺相容性問題
2.建庫要加字符集
3.庫名不能有數字開頭
4.庫名要和業務相關

# 增加
create table tableName(
列1  屬性(資料型別、約束、其他屬性) ,
列2  屬性,
列3  屬性
)
eg:
CREATE TABLE student(
id      INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '學號',
sname   VARCHAR(255) NOT NULL COMMENT '姓名',
sage    TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '年齡',
sgender ENUM('m','f','n') NOT NULL DEFAULT 'n' COMMENT '性別' ,
sfz     CHAR(18) NOT NULL UNIQUE  COMMENT '身份證',
intime  TIMESTAMP NOT NULL DEFAULT NOW() COMMENT '入學時間'
) ENGINE=INNODB CHARSET=utf8 COMMENT '學生表';

id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '全域性id', 

CREATE TABLE TableName2 LIKE TableName1;

# 刪除表
drop table tableName

# 修改
ALTER TABLE tableName ADD(DROP) 屬性(資料型別、約束、其他屬性)
eg:
# 在stuent表中新增qq列
ALTER TABLE student ADD qq VARCHAR(20) NOT NULL UNIQUE COMMENT 'qq';
# 在sname後加微信列
ALTER TABLE student ADD wechat VARCHAR(64) NOT NULL UNIQUE  COMMENT '微訊號' AFTER sname ;
# 在id列前加一個新列num
ALTER TABLE student ADD num INT NOT NULL COMMENT '數字' FIRST;

# 刪除列 
ALTER TABLE stu DROP num;
ALTER TABLE stu DROP qq;
ALTER TABLE stu DROP wechat;

# 將sgender 改為 sg 資料型別改為 CHAR 型別
ALTER TABLE student change sgender sg CHAR(1) NOT NULL DEFAULT 'n' ;


用的時候,一定要注意:
修改資料型別,修改欄位位置  ---用modify
修改名字 --就用change
# 範圍大用change,小用modify。
# 均需要加入型別,限制
  1. 表名小寫
  2. 不能是數字開頭
  3. 注意字符集和儲存引擎
  4. 表名和業務有關
  5. 選擇合適的資料型別,簡短的、長度合適的資料型別
  6. 每個列都要有註釋
  7. 每個列設定為非空,無法保證非空,用0來填充。
  8. 必須有主鍵

DCL 資料控制語言

控制就是操作許可權,而在DCL之中,主要有兩個語法:GRANT,REVOKE

# 使用者管理
create user xxx@"白名單" indentified by "password"
drop user
alter user
select user,host from mysql.user;
# 許可權
## 檢視所有許可權列表
show privileges;
all 
with grant option

# 檢視使用者許可權
show grant UserName@"白名單"
select * from mysql.user\G; 

DCL

授權

grant 許可權 on 物件 to 使用者 identified by “密碼”
# mysql 8.0+:(中文表示,可自定製)
create user 使用者 identified by “密碼”
grant 許可權1,許可權2,許可權3... on 物件 to 使用者 identified by “密碼”

許可權:
ALL: 管理員(不包含“ Grant option”,給他人授權)
許可權1,許可權2,許可權3…: 普通人員(開發人員)
Grant option

物件範圍: 庫,表

“.”—> chmod -R 755 /管理員
userName.*—> chmod -R 755 userName/普通使用者
userName.t1—> chmod -R 755 userName/t1
# 授權
grant create update, select ... on 庫名.表的範圍[*(所有), 表名1] to userName@“白名單”

Mysql授權表

user.
dbdb.*
Tables_privdb.table
Colums
Procs_priv儲存過程中的許可權

回收許可權

revoke 許可權 on 庫 from 使用者@“白名單”
# 具體eg如上

拓展,忘記root密碼了該怎麼辦?

# 需知
# --skip-grant-tables 跳過授權表
# --skip-network  關閉TCP/IP連線

# 1.關閉資料庫(任選其一)
service mysqld stop
/etc/init.d/mysqld stop
pkill mysqld

# 2.跳過驗證(任選其一)
service mysqld start --skip-grant-tables
service mysqld restart --skip-grant-tables
mysql_safe --skip-grant-tables &

# 3.禁止遠端連線
service mysqld start --skip-grant-tables --skip-network 
service mysqld restart --skip-grant-tables --skip-network

# 4.修改密碼
## 4.1手動載入授權表
flush privileges
## 4.2修改密碼
alter user root@"localhost" indentified by "new Passwd"

# 5.重啟資料庫
service mysqld restart

原理探究

說到這個,那就不得不從mysql的server層說起了,mysql的架構圖如下(僅關鍵部分)

當我們忘記密碼的時候,改怎麼辦呢?

這就對於我們平時對於mysql的模型有所考察了,如果你知道mysql的他內部到底是如何執行的,那麼只需要在合適的地方,按照我們所想的給他“繞道而行”,是否就可以繞過這個密碼驗證了呢?答案是當然可以的。

首先我們介紹一下上面這副圖中執行流程,當我們啟動mysql服務的時候,系統會自動幫我們做一下這些事兒

  1. 首先提供可連線的協議,也就是提供服務
  2. 開啟使用者與密碼校驗,以處理將要連線的客戶
  3. 驗證成功,分配獨立的連線執行緒

如果我們需要跳過密碼校驗,那麼只需要做以下幾件事。

  1. 讓系統重啟
  2. 在重啟的過程中停掉使用者與密碼校驗

這樣我們就可以連線了,但是還不夠。盡然已經停掉了。此時的我們無法修改密碼.(跳過驗證,而不是把驗證功能移除了)

那麼我們此時還需要把驗證功能載入進來,然後對驗證的表進行修改。

DML 資料操作語言

這個也是我們日常中用的最多的地方,應為建庫表,改許可權,改密碼。修改等等什麼的並不是每次都要嘛.這個也很好理解

DML 資料操作語言 對錶中的資料行進行增、刪、改

insert

# 語法
單行資料
INSERT INTO tableName(key1, key2, key3..) VALUES(value1, value2,value3...) [SELECT * FROM tableName]

# 多行資料
INSERT INTO tableName(key1, key2, key3..) VALUES \
(value1, value2,value3...)
(value1, value2,value3...)
(value1, value2,value3...);
...
[SELECT * FROM tableName]

插入時, key1,key2,key3 必須與value1,value2, value3 數量一致

插入對應欄位

INSERT INTO tableName(key1, key3..) VALUES(value1, value3...) [SELECT * FROM tableName]

update

# 更新前我們一般都會先查表內資料
# 查詢出對應表已存在所有行
DESC tableName;
# 查詢對應表已存在資料
SELECT * FROM tableName;	# * 可替換成欄位名,查對應欄位

# 更新資料
UPDATE student SET 欄位名='新值' [WHERE 限定條件];

Eg:

建立一張新的student表

# 建表
CREATE TABLE `student` (
 `id` int NOT NULL AUTO_INCREMENT COMMENT '學號',
 `sname` varchar(255) CHARACTER SET utf8 NOT NULL COMMENT '姓名',
 `sage` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '年齡',
 `intime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入學時間',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='學生表'

# 插入演示資料
INSERT INTO student(sname,sage) VALUES("趙一", 1),
("王二", 2),
("張三", 3),
("李四", 4);

資料庫

需求一:

李四改名為“里斯”;

UPDATE student SET sname="里斯" WHERE sname = "李四";

# 或者
UPDATE student SET sname="里斯" WHERE id=4;
UPDATE student SET sname="里斯" WHERE sage=4;

修改後,如下所示

需求二:

將所有表內成員的年齡+10;

UPDATE student SET sage=sage + 10
# UPDATE student SET sage+=10(錯誤寫法,開發時候用的什麼sage ++, sage +=,在這裡都不允許)

需求三:將所有表內成員的年齡+10,除了里斯

UPDATE student SET sage=sage + 10 WHERE sname != "里斯";
# 當然也可以這樣寫;
UPDATE student SET sage=sage + 10 WHERE sname = "趙一" OR sname = "王二" OR sname="張三" ;

AND: 執行均滿足

OR: 滿足其一執行

where 見下文

delete

# 刪除指定資料
DELETE FROM tableName  [WHERE id=1];

# 清空標中所有資料
DELETE FROM student;
truncate table student;

區別:
delete: DML操作, 是邏輯性質刪除,逐行進行刪除,速度慢.
truncate: DDL操作,對與表段中的資料頁進行清空,速度快.

偽刪除:用update來替代delete,最終保證業務中查不到(select)

1.新增狀態列
ALTER TABLE stuent ADD state TINYINT NOT NULL DEFAULT 1 ;
SELECT * FROM stuent;
2. UPDATE 替代 DELETE
UPDATE stuent SET state=0 WHERE id=6;
3. 業務語句查詢
SELECT * FROM stu WHERE state=1;

拓展

DELETE FROM student;
DROP TABLE student;
truncate table student;

以上三條刪除語句有何區別?

同:三者都是刪除語句,均可刪除

異:

DELETE FROM student:

邏輯上逐行刪除,資料過多,操作很慢

並沒有真正的從磁碟上刪除,知識在磁碟上打上標記,磁碟空間不立即釋放。HWM高位線不會降低

DROP TABLE student;

將表結構(元資料)和資料行,物理層次刪除

truncate truncate table student;

清空表段中的所有資料頁,物理層次刪除全表資料,磁碟空間立即釋放。HWM高位線降低

DQL 資料查詢語言

show類

show databases;
show CREATE DATABASE databaseName;

show table;
show CREATE TABLE table;

desc tableName;

select類

獲取表中的資料行

# select @@xxx 檢視系統引數
SELECT @@port;
SELECT @@basedir;
SELECT @@datadir;
SELECT @@socket;
SELECT @@server_id;

# select 函式
SELECT NOW();
SELECT DATABASE();
SELECT USER();
SELECT CONCAT("hello world");
SELECT CONCAT(USER,"@",HOST) FROM mysql.user;
SELECT GROUP_CONCAT(USER,"@",HOST) FROM mysql.user;

手冊 https://dev.mysql.com/doc/refman/8.0/en/sql-function-reference.html

select配合子句

select 
FROM 表1,表2...,
WHERE 過濾條件1,過濾條件2,過濾條件3 ...
GROUP BY 條件列1,條件列2,條件列3 ...
	# selct_list 列名
HAVING 過濾條件1,過濾條件2,過濾條件3 ...
ORDER BY 條件列1,條件列2,條件列3 ...
LIMIT 限制條件;

單表子句-from

SELECT 列1,列2 FROM 表
SELECT  *  FROM 表

# EG
# 查詢student中所有的資料(不要對大表進行操作)
SELECT * FROM stu ;
# 查詢stu表中,學生姓名和入學時間
SELECT sname , intime FROM stuent;

單表子句-where

SELECT col1,col2 FROM TABLE WHERE colN 條件;
# where 操作符(>、<、>=、 <=、 <>、in、like、and、or)
SELECT col1,col2 FROM TABLE WHERE = 條件;

# where 模糊查詢
SELECT * FROM city WHERE district LIKE 'guang%';    
%  : 表示任意0個或多個字元。可匹配任意型別和長度的字元,有些情況下若是中文,請使用兩個百分號(%%)表示。
_  : 表示任意單個字元。匹配單個任意字元,它常用來限制表示式的字元長度語句
[] : 表示括號內所列字元中的一個(類似正則表示式)。指定一個字元、字串或範圍,要求所匹配物件為它們中的任一個。

# where配合between...and...
SELECT * FROM city  WHERE population >1000000 AND population <2000000;
SELECT * FROM city  WHERE population BETWEEN 1000000 AND 2000000;

group by

根據 by後面的條件進行分組,方便統計,by後面跟一個列或多個列

未分組分組列,使用聚合函式

聚合函式

**max()**      :最大值
**min()**      :最小值
**avg()**      :平均值
**sum()**      :總和
**count()**    :個數
group_concat() : 列轉行

HAVING

需要在group by 之後,在做判斷過濾使用(類似於where)

order by

實現先排序,by後新增條件列(預設從小到大)

逆序:後加DESC

distinct:去重複

SELECT countrycode FROM city ;
SELECT DISTINCT(countrycode) FROM city  ;

聯合查詢- union all

-- 中國或美國城市資訊

SELECT * FROM city 
WHERE countrycode IN ('CHN' ,'USA');

SELECT * FROM city WHERE countrycode='CHN'
UNION ALL
SELECT * FROM city WHERE countrycode='USA'

說明:一般情況下,我們會將 IN 或者 OR 語句 改寫成 UNION ALL,來提高效能
UNION     去重複
UNION ALL 不去重複

LIMIT 限制條件

限制查詢

select * FROM 表名 LIMIT 限制條件
eg:
--- 只輸出前1000條
select * FROM 表名 LIMIT 1000
--- 只輸出前1000-2000條
select * FROM 表名 LIMIT 1000, 2000

select * FROM 表名 LIMIT 1000, 2000
相當於
select * FROM 表名 LIMIT 2000 OFFSET 1000

join 多表連線查詢

內連線

查詢li4家的地址

SELECT A.name,B.address FROM
A JOIN  B
ON A.id=B.id	--- 關聯列
WHERE A.name='li4'


--- 相當於

SELECT A.name,B.address FROM
A JOIN  B
ON A.id=B.id
WHERE A.name='li4'

外連線

驅動表建議使用 資料少的表 為驅動表

SELECT A.name,B.address FROM
A JOIN  B
ON A.id=B.id	--- 關聯列
WHERE A.name='li4'


--- 相當於

SELECT A.name,B.address FROM
A left JOIN  B
ON A.id=B.id
WHERE A.name='li4'

---  A left JOIN  B 其中a位驅動表