MySQL教程

金面佛光發表於2024-08-10

1、初識MySQL#

JavaEE:企業級Java開發 Web

前端(頁面:展示,資料!)

後臺(連線點:連線資料庫JDBC,連線前端(控制,控制檢視跳轉,給前端傳遞資料))

資料庫(存資料,Txt,Excle,word)

學好資料庫:

作業系統,資料結構與演算法。

離散數學,數位電路,體系結構,編譯原理。

1.1 什麼是資料庫#

資料庫(DB,DataBase)

概念:資料倉儲,軟體,安裝在作業系統(window,Linux,mac)之上

作用:儲存資料,管理資料

1.2 資料庫分類#

關係型資料庫: (SQL)

  • MySQL,Oracle,SqlServer,DB2,SQLlite
  • 透過表和表之間,行和列之間的關係進行資料的儲存

非關係型資料庫: (NoSQL) Not Only

  • Redis,MongDB
  • 非關係型資料庫,以物件儲存,透過物件自身的屬性來決定

DBMS(資料庫管理系統)

  • 資料庫的管理軟體,科學有效的管理我們的資料,維護和獲取資料
  • MySQL,本質:資料庫管理系統

1.3 MySQL安裝#

1.下載後得到zip壓縮包.
2.解壓到自己想要安裝到的目錄,本人解壓到的是D:\Environment\mysq-5.7.19
3.添環境變數

1.我的電腦>屬性>高階>環境變數
2.選擇PATH,在其後面新增:你的mysql安裝檔案下面的bin資料夾
3.在D:\Environment\mysql--5.7.19下新建my.ini檔案
4.編輯my.ini檔案,注意替換路徑位置

[mysqld]
basedir=E:\Environment\mysql-8.0.27\
datadir=E:\Environment\mysql-8.0.27\data\ 
port=3306
skip-grant-tables

5.啟動管理員模式下的CMD,並將路徑切換至mysql下的bin目錄,然後輸入mysqld -install(安裝mysql)
6、初始化資料庫檔案
mysqld --initialize-insecure --user=mysql
7.然後啟動mysql,進入mysql管理介面(密碼為空)

net start mysql 啟動MySQL
mysql -u root -p 連線MySQL

8.進入介面後更改root密碼

update mysql.user set authentication_string=password('123456') where user='root' and Host='localhost';

(最後輸入flush privileges;重新整理許可權)
9.修改my.ini檔案,註釋最後一句 #skip-grant-tables
10.重啟mysql即可正常使用

net stop mysql
net start mysql

11.連線上測試出現以下結果就安裝好了

1.4 在安裝MySQL過程中的一些錯誤

sc delete mysql,清空服務

update mysql.user set authentication_string=password('123456') where user='root' and Host='localhost';

當修改密碼出現報錯時可使用下面兩句任意一句話來進行修改

SET PASSWORD = '123456'
ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';

1.5 SQLyog#

Ultimate版本
註冊名:kuangshen
註冊碼:8d8120df-a5c3-4989-8f47-5afc79c56e7c

1、新建一個資料庫

image-20211024212840888

連線資料庫時的一些錯誤:

錯誤:1251 - Client does not support authentication protocol requested by server; consider upgrading MySQL client.

再次安裝MySQL後,使用 Navicat 或者 MySQL yog都會出現下面這個異常。

連線資料庫之後把下面的兩條指令逐條複製貼上到控制檯就可以了 ( *'123456'為你的資料庫登入密碼。* )。

alter user 'root'@'localhost' identified with mysql_native_password by '123456';

flush privileges;

1.6 連線資料庫#

命令列連線

ctrl + c 強行終止

mysql -uroot -p123456	-- 連線資料庫

update mysq1.user set authentication_string=password('123456') where user='root' and Host ='loca1host';	-- 修改使用者密碼

flush privileges; -- 重新整理許可權
--------------------------------------------
--所有的語句都使用;結尾
show databases;-- 檢視所有的資料庫

mysql> use schoo1 -- 切換資料庫use 資料庫名
Database changed

show tables; -- 檢視資料庫中所有的表
describe student; -- 顯示資料庫中所有的表的資訊

create database westos; -- 建立一個資料庫

exit;	-- 退出連線
-- 單行註釋(sQL的本來的註釋)
/*(sq1的多行註釋)
he1lo
world
*/

資料庫語言:CRUD 增刪改查

DDL 定義

DML 操作

DQL 查詢

DCL 控制

2、運算元據庫#

運算元據庫 > 運算元據庫中的表 > 運算元據庫表中的資料

mysql關鍵字不區分大小寫

2.1 運算元據庫#

1、建立資料庫

CREATE DATABASE [IF NOT EXISTS] westos;

2、刪除資料庫

DROP DATABASE [IF EXISTS] westos

3、使用資料庫

-- tab	鍵的上面,如果你的表名或者欄位名是一個特殊字元,就需要帶``
USE`school`

4、檢視資料庫

SHOW DATABASE -- 檢視所有的資料庫,記得+"s"

學習思路:

  • 對照sqlyog視覺化歷史記錄檢視sql
  • 固定的語法或關鍵字必須要強行記住

2.2 資料庫的資料(列)型別#

數值

  • tinyint 1個位元組

  • smallint 2個位元組

  • mediumint 3個位元組

  • int 4個位元組

  • bigint 8個位元組

  • float 4個位元組

  • double 8個位元組(存在精度問題)

  • decimal 字串形式的浮點數(用於金融計算等,不存在精度問題)

字串

  • char 固定大小的字串 0~255
  • varchar 可變字元 0~65535 常用的變數 String
  • tinytext 微型文字 2^8-1
  • text 文字串 2^16-1 儲存大文字

時間日期

  • date YYYY-MM-DD,日期格式
  • time HH : mm : ss 時間格式
  • datetime YYYY-MM-DD HH : mm : ss 最常用的時間格式
  • timestamp 時間戳,1970.1.1到現在的毫秒數
  • year 年份表示

null

  • 沒有值,未知
  • 注意,不要使用null進行運算,結果為null

2.3 資料庫的欄位屬性(重點)#

unsigned:

  • 無符號的整數
  • 宣告該列不能宣告為負數

zerofill:

  • 0填充的

  • 不足的位數用0來填充,int(3),5 , 005

自增:

  • 通常理解為自增,自動在上一條記錄的基礎上 +1(預設)
  • 通常用來設計唯一的主鍵--index,必須是整數型別
  • 可以自定義設計主鍵自增的起始值和步長

非空:

  • 假設設定為 not null ,如果不給它賦值,就會報錯
  • null,如果不填寫值,預設就是null

預設:

  • 設定預設的值
  • sex,預設值為男,如果不指定該列的值,則會有預設的值

int(11)也就是int(M),M指示最大顯示寬度,不是說限制你只能插入規定長度的資料,最大有效顯示寬度是255,顯示寬度與儲存大小或型別包含的值的範圍是無關的

如int(1),你可以插1,也可以插10,100

但是varchar(20)就不一樣,輸入的字元必須在20個字元內

2.4 建立資料庫#

-- 目標:建立一個schoo1資料庫
-- 建立學生表(列,欄位)使用sQL建立
-- 學號int 登入密碼varchar(20)姓名,性別varchar(2),出生日期(datatime),家庭住址, emai1
--注意點,使用英文(),表的名稱和欄位儘量使用``括起來
-- AUTO_INCREMENT 自增
-- 字串使用單引號括起來!
-- 所有的語句後面加  ,(英文的),最後一個不用加
-- PRIMARY KEY主鍵,一般一個表只有一個唯一的主鍵!
CREATE TABLE IF NOT EXISTs student(
	`id` INT(4)NOT NULL AUTO_INCREMENT COMMENT '學號',
	`name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
	`pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT'密碼',
	`sex` VARCHAR(2) NOT NULL DEFAULT '女' COMMENT '性別',
	`birthday` DATETIME DEFAULT NULL COMMENT '出生目期',
	`address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
	`email` VARCHAR(50) DEFAULT NULL COMMENT '郵箱',
	PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

格式

CREATE TABLE [IF NOT EXISTs]`表名`(
	'欄位名’列型別[屬性][索引][註釋],
    '欄位名'列型別[屬性][索引][註釋],
    ......
    '欄位名’列型別[屬性][索引][註釋]
)[表型別][字符集設定][註釋]

常用命令

SHOw CREATE DATABASE school -- 檢視建立資料庫的語句
SHOw CREATE TABLE student -- 檢視student資料表的定義語句
DESC student -- 顯示錶的結構

2.5 資料表的型別#

-- 關於資料庫引擎
/*
INNODB 	預設使用~
MYISAM	早些年使用的
*/
MYISAM INNODB
事務支援 不支援 支援
資料行鎖定 不支援(表鎖定) 支援
外來鍵索引 不支援 支援
全文索引 支援 不支援
表空間的大小 較小 較大,約為2倍

常規使用操作:

  • MYISAM 節約空間,速度較快
  • INNODB 安全性高,事務的處理,多表多使用者操作

在物理空間存在的位置

所有的資料庫檔案都存在data目錄下,一個資料夾就對應一個資料庫

本質還是檔案的儲存

MySQL引擎在物理檔案上的區別

  • INNODB 在資料庫表中只有一個*.frm檔案,以及上級目錄下的ibdata1檔案
  • MYISAM對應檔案
    • *.frm 表結構的定義檔案
    • *.MYD 資料檔案(data)
    • *.MYI 索引檔案(index)

設定資料庫表的字符集編碼

CHARSET=utf8

MySQL的預設編碼是Latin1,不支援中文

可以在my.ini中配置預設的編碼

character-set-server = utf8

2.6 修改刪除表#

修改

-- 修改表名:ALTER TABLE舊錶名 RENAME AS新表名
ALTER TABLE teacher RENAME As teacher1
-- 增加表的欄位: ALTER TABLE 表名 ADD欄位名 列屬性
ALTER TABLE teacher1 ADD age INT(11)
-- 修改表的欄位(重新命名,修改約束!)
-- ALTER TABLE表名 MODIFY 欄位名 列屬[]
ALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改約束
-- ALTER TABLE 表名 CHANGE 舊名字 新名字列 屬性[]
ALTER TABLE teacher1 CHANGE age age1 INT(1)	-- 欄位重名名

-- 刪除表的欄位:ALTER TABLE 表名 DROP 欄位名
ALTER TABLE teacher1 DROP age1

最終結論:

change用來欄位重新命名,不能修改欄位型別和約束。

modify不用來欄位重新命名,只能修改欄位型別和約束。

刪除

-- 刪除表(表存在)
DROP TABLE IF EXISTS teacher1

所有的建立和刪除操作儘量加上判斷,以免報錯

注意點:

  • 欄位名使用 `` 包裹
  • 註釋使用 -- 或者 /**/
  • sql關鍵字不敏感,建議小寫
  • 符號用英文

3、MySQL資料管理#

3.1、外來鍵#

在建立表的時候,增加約束(複雜)

CREATE TABLE grade(
`gradeid` INT(5) AUTO_INCREMENT NOT NULL COMMENT'年級id',
`gradename` VARCHAR(10) NOT NULL COMMENT'年級姓名',
PRIMARY KEY(`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

-- 學生表的外來鍵要去引用年級表的gradeid
-- 定義外來鍵
-- 給這個外來鍵新增約束(執行引用),reference 引用

CREATE TABLE IF NOT EXISTS student(
`id` INT(4)NOT NULL AUTO_INCREMENT COMMENT '學號',
`name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
`pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT'密碼',
`sex` VARCHAR(2) NOT NULL DEFAULT '女' COMMENT '性別',
`birthday` DATETIME DEFAULT NULL COMMENT '出生目期',
`gradeid` INT(10) NOT NULL COMMENT '學生的年級',
`address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
`email` VARCHAR(50) DEFAULT NULL COMMENT '郵箱',
PRIMARY KEY(`id`),
KEY `FK_gradeid`(gradeid),
CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

建立表成功後,新增外來鍵約束

ALTER TABLE `student`
ADD CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`) 
-- ALTER TABLE 表 ADD CONSTRAINT 約束名 FOREIGN KEY(作為外來鍵的列) REFERENCES 哪個表(哪個欄位)

以上操作都是物理外來鍵,都是資料庫級別的外來鍵,不建議使用

最佳實踐

  • 資料庫就是單純的表,只用來存資料,只有行和列
  • 想使用外來鍵時,用程式去實現

3.2、DML語言#

資料庫意義:資料儲存資料管理

DML語言:資料操作語言

  • insert
  • update
  • delete

3.3、新增#

語法:insert into 表名 (欄位1,欄位2,...) values(值1),(值2,...)

-- 插入單行
INSERT INTO `student` (`name`,pwd,sex) VALUES ('趙','xyz','男')

-- 插入多行
INSERT INTO `student`(`name`,pwd,sex) VALUES('錢','asd','男'),('孫','qwe','男')

-- 省略欄位
INSERT INTO `student` VALUES (4,'李','zxc','男','2000-01-01','北京','email')

注意事項:

  1. 欄位和欄位之間使用英文逗號隔開
  2. 欄位是可以省略的,但是後面的值必須要一一對應,不能少
  3. 可以同時插入多條資料,values 後面的值使用英文逗號隔開即可

3.4、修改#

語法:update 表 set 列 = 值 where 條件

UPDATE `student` SET `name` = '李四'  WHERE pwd = 'ls'

-- 不指定條件的情況下
UPDATE `student` SET `name` = '張三'

-- 修改多個屬性
UPDATE `student` SET `name` = '李四', birthday = '2002-02-04', address = '上海' WHERE `pwd` = 'ls' 

條件:where子句 運算子 id等於某個值,大於某個值,在某個區間內修改

運算子會返回布林值

運算子 含義 範圍 結果
= 等於
<>或 != 不等於
>
<
<=
>=
between...and... 在某個範圍內 [2,5]
and 5>1 and 1>2 false
or 5>1 or 1>2 true

注意:

  • colnum_name 是資料庫的列,儘量帶上``
  • 條件,篩選的條件,如果沒有指定,則會修改所有的列
  • value,是一個具體的值,也可以是一個變數
UPDATE `student` SET `birthday` = CURRENT_TIME WHERE `name` = '孫' and sex = '女'

3.5、刪除#

delete 命令

語法:delete from 表名 where 條件

-- 刪除資料(會全刪,一般不這麼寫)
DELETE FROM `student`

-- 刪除指定資料
DELETE FROM `student` WHERE id = 4

TRUNCATE 命令

作用:完全清空一個資料庫表,表的結構和索引約束不會變

-- 清空student表
TRUNCATE `student`

delete 和 TRUNCATE 的區別

  • 相同點:

  • 都能刪除資料,都不會刪除表結構

不同點:

  • TRUNCATE 重新設定自增列,計數器會歸零
  • TRUNCATE 不會影響事務

delete刪除的問題,重啟資料庫,現象

  • INNODB 自增列會從1開始(存在記憶體中,斷電即失)
  • MyISAM 繼續從上一個自增量開始(存在檔案中,不會丟失)

4、DQL查詢資料#

SELECT  [ALL  |  DISTINCT]

{*  |  table.*  |  [table.field1[as  alias1][,table.field2[as  alias2]][, ...]]}

FROM  table_name  [as  table_alias]

        [left  |  right  |  inner  join  table_name2]    -- 聯合查詢

        [WHERE  ...]    -- 指定結果需滿足的條件

        [GROUP  BY  ...]    -- 指定結果按照哪幾個欄位來分組

        [HAVING]    -- 過濾分組的記錄必須滿足的次要條件

        [ORDER  BY  ...]    -- 指定查詢記錄按一個或多個條件排序

        [LIMIT  {[offset,]row_count  |  row_countOFFSET  offset}];   

                                       -- 指定查詢的記錄從哪條至哪條     

4.1查詢指定欄位#

-- 查詢全部學生
SELECT * FROM `student` 

-- 查詢指定欄位
SELECT `studentno`,`studentname` FROM `student`

-- 使用 AS 可以給欄位起別名
SELECT `studentno` AS 學號,`studentname` AS 學生姓名 FROM `student`

-- CONCAT(a,b)
SELECT CONCAT('姓名:',studentname) AS 名字 FROM `student`

去重 distinct

作用:去除SELECT查詢出來的結果中重複的資料,重複的資料只顯示一條

SELECT DISTINCT `studentno` AS '學號' FROM `result`

資料庫的列(表示式)

SELECT VERSION()
-- 學員考試成績 + 1 分檢視
SELECT `studentresult` +1 AS '提分後' FROM `result`

資料庫中的表示式:文字值,列,NULL,函式,計算表示式,系統變數

select 表示式 from 表

4.2、where條件子句#

作用:檢索資料中符合條件的值

邏輯運算子

運算子 語法
and && a and b
or
Not ! not a !a

儘量使用英文字母

SELECT `studentno`,`studentresult` FROM `result`
WHERE `studentresult`>=80 AND `studentresult`<=95

-- 模糊查詢
SELECT `studentno`,`studentresult` FROM `result`
WHERE `studentresult`BETWEEN 80 AND 95

SELECT `studentno`,`studentresult` FROM `result`
WHERE NOT `studentno` = 1001

模糊查詢:比較運算子

運算子 語法 描述
IS NULL a is null 如果a為null,結果為真
IS NOT NULL a is not null a不為null,結果為真
between a between b and c a在b,c中間,為真
Like a like b SQL匹配,如果a匹配b,結果為真
In a in (a1,a2,a3) a在a1,a2...其中某一個值中,結果為真
-- like結合 %(代表0到任意個字元) _(一個字元)
SELECT `studentno`,`studentname` FROM `student` 
WHERE `studentname` LIKE '張%'

SELECT `studentno`,`studentname` FROM `student` 
WHERE `studentname` LIKE '張_'

-- 查詢名字中間有達的
SELECT `studentno`,`studentname` FROM `student` 
WHERE `studentname` LIKE '%達%'

-- 查詢1001,1002號學員 
SELECT `studentno`, `studentname` FROM `student`
WHERE `studentno` IN (1001,1002)

4.4、聯表查詢#

JOIN 對比

-- join(連線的表) on (判斷的條件) 連線查詢
-- where 等值查詢

SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM student s
INNER JOIN `result` r
ON s.studentno = r.studentno

-- right join
SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM student s
RIGHT JOIN `result` r
ON s.studentno = r.studentno

-- left join
SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM student s
LEFT JOIN `result` r
ON s.studentno = r.studentno
操作 描述
inner join 如果表中至少有一個匹配,就返回行
left join 會從左表中返回所有的值,即使右表中沒有匹配
right join 會從右表中返回所有的值,即使左表中沒有匹配
-- 思考題(查詢了參加考試的同學資訊:學號,學生姓名,科目名,分數)
/*思路
1.分析需求,分析查詢的欄位來自哪些表,student.result. subject(連線查詢)
2.確定使用哪種連線查詢? 7種
確定交叉點(這兩個表中哪個資料是相同的)
判斷的條件:學生表的中 studentNo.=成績表studentNo */

SELECT s.`studentno`,`studentname`,`subjectname`,`studentresult`
FROM student s
RIGHT JOIN `result` r
ON s.studentno = r.studentno
INNER JOIN `subject` sub
ON r.subjectno = sub.subjectno

自連線

自己的表和自己的表連線,核心:一張表拆為兩張一樣的表即可

image-20220122095202159

SELECT a.`categoryname` AS '父欄目',b.`categoryname` AS '子欄目'
FROM category a, category b
WHERE a.`categoryid` = b.`pid` 

4.5、分頁和排序#

排序

-- 排序:升序 ASC ,降序 DESC
-- ORDER BY 透過哪個欄位排序

SELECT s.`studentno`,`studentname`,`subjectname`,`studentresult`
FROM student s
INNER JOIN `subject` sub
ON s.`gradeid` = sub.`gradeid`
INNER JOIN result r
ON r.`subjectno` = sub.`subjectno`
WHERE `subjectname` = '高等數學-2'
ORDER BY `studentresult` DESC

分頁

-- 語法:limit 起始值,頁的大小
-- 第N頁 (n-1)* pagesize,pagesize
-- pagesize :頁面大小
-- (n-1)* pagesize:起始值
-- n:當前頁
-- 資料總數/頁面大小 = 總頁數

SELECT s.`studentno`,`studentname`,`subjectname`,`studentresult`
FROM student s
INNER JOIN `subject` sub
ON s.`gradeid` = sub.`gradeid`
INNER JOIN result r
ON r.`subjectno` = sub.`subjectno`
WHERE `subjectname` = '高等數學-2'
ORDER BY `studentresult` DESC
LIMIT 0,5

4.6、子查詢#

-- 查詢科目為高等數學-3且分數> 80分的學生的學號和姓名
SELECT s.`studentno`,`studentname`
FROM student s
INNER JOIN result r
ON s.`studentno` = r.`studentno`
INNER JOIN `subject` sub
ON r.`subjectno` = sub.`subjectno`
WHERE `subjectname` = '高等數學-3'
AND `studentresult` >80

-- 子查詢
SELECT s.`studentno`,`studentname`
FROM student s
INNER JOIN result r
ON s.`studentno` = r.`studentno`
WHERE `studentresult` >80 AND `subjectno` = (
	SELECT `subjectno` FROM `subject`
	WHERE `subjectname` = '高等數學-3'
)

4.7、分組和過濾#

-- 查詢不同課程的平均分,最高分,最低分,平均分大於80
-- 核心:(根據不同的課程分組)
SELECT `subjectname`, AVG(`studentresult`),MAX(`studentresult`),MIN(`studentresult`)
FROM `result` r
INNER JOIN `subject` sub
ON r.`subjectno` = sub.`subjectno`
GROUP BY r.`subjectno`
HAVING AVG(`studentresult`) >80

ANY_VALUE()函式說明

MySQL有any_value(field)函式,它主要的作用就是抑制ONLY_FULL_GROUP_BY值被拒絕。
這樣sql語句不管是在ONLY_FULL_GROUP_BY模式關閉狀態還是在開啟模式都可以正常執行,不被mysql拒絕。
any_value()會選擇被分到同一組的資料裡第一條資料的指定列值作為返回資料。

4.8、select小結#

image-20220122135345657

5、MySQL函式#

https://dev.mysql.com/doc/refman/5.7/en/built-in-function-reference.html

5.1、常用函式#

-- 數學運算
SELECT ABS(-8) -- 絕對值
SELECT CEILING(9.4) -- 向上取整
SELECT FLOOR(9.4) -- 向下取整
SELECT RAND() -- 返回一個0~1之間的隨機數
SELECT SIGN(10) -- 判斷一個數的符號 0-0  負數返回-1  正數返回1

-- 時間和目期函式(記住)
SELECT CURRENT_DATE()--獲取當前日期
SELECT CURDATE() --獲取當前日期
SELECT NOW ()--獲取當前的時間
SELECT LOCALTIME()--本地時間
SELECT SYSDATE() --系統時間
SELECTYEAR(NOW ())
SELECT MONTH(NOW())SELECT DAY(NOW ())
SELECT HOUR (NOW () )
SELECT MINUTE (NOW () )
SELECT SECOND (NOW ())

--系統
SELECT SYSTEMUSER()
SELECT USER()
SELECT VERSION()

5.2、聚合函式#

函式名稱 描述
COUNT() 計數
SUM() 求和
AVG() 平均值
MAX() 最大值
MIN() 最小值
...
count(1):統計所有的記錄(包括null)。
count(*):統計所有的記錄(包括null)。
count(欄位):統計該"欄位"不為null的記錄。
count(distinct 欄位):統計該"欄位"去重且不為null的記錄。

count(1)中的1並不是表示第一個欄位,而是表示一個固定值。其實就可以想成表中有這麼一個欄位,這個欄位就是固定值1,count(1),
就是計算一共有多少個1。count(*),執行時會把星號翻譯成欄位的具體名字,效果也是一樣的,不過多了一個翻譯的動作,比固定值的方式效率稍微低一些。

-- 都能夠統計表中的資料(想查詢一個表中有多少個記錄,就使用這個count( ))
SELECT COUNT(`BornDate`) FROM student; -- Count(欄位),會忽略所有的null值
SELECT COUNT(*) FROM student; -- count(*),不會忽略 null值,本質計算行數
SELECT COUNT(1)FROM result; -- Count(1),不會忽略null值,本質計算行數
-- 當要統計的數量比較大時,發現count(*)花費的時間比較多,相對來說count(1)花費的時間比較少。            
-- 如果你的資料表沒有主鍵,那麼count(1)比count(*)快 ;如果有主鍵的話,那主鍵(聯合主鍵)作為count的條件也比count(*)要快  。

-- 2、如果你的表只有一個欄位的話那count(*)就是最快的。  

-- 3、如果count(1)是聚索引,id,那肯定是count(1)快,但是差的很小的。因為count(*),自動會最佳化指定到那一個欄位。
所以沒必要去count(1),用count(*),sql會幫你完成最佳化。此時count(1)和count(*)基本沒有區別! 

SELECT SUM(`studentResult`) AS 總和 FROM result
SELECT AVG(`studentResult`) AS 平均分 FROM result
SELECT MAX(`studentResult`) AS 最高分 FROM result
SELECT MIN( `studentResult `) AS 最低分 FROM result

5.3、資料庫級別的MD5加密#

MD5不可逆,具體的值的MD5是一樣的

-- 明文密碼
INSERT INTO `grade` VALUES(6,'123456')
-- 插入時加密
INSERT INTO `grade` VALUES(7,MD5('123456'))
-- 插入後加密
UPDATE `grade` SET `gradename` = MD5(gradename) WHERE `gradeid` = 6
UPDATE `grade` SET `gradename` = MD5(gradename) -- 全部加密
WHERE `gradeid` =  6
-- 校驗:將使用者傳入過來的密碼,進行md5加密,比對加密後的值
SELECT * FROM `grade` WHERE `gradeid` = 6 AND `gradename` = MD5('123456')

6、事務#

6.1、什麼是事務#

事務:要麼都成功,要麼都失敗

——————

1、SQL執行 A 給 B 轉賬

2、SQL執行 B 收到 A 的錢

——————

將一組SQL放在一個批次中去執行

事務原則:ACID 原子性 一致性 隔離性 永續性

原子性(Atomicity):

要麼都成功,要麼都失敗

一致性(Consistency):

事務前後資料完整性要保持一致

永續性(Durability):

事務一旦提交則不可逆,被持久化到資料庫中

隔離性(Isolation):

事務的隔離性是多個使用者併發訪問資料庫時,資料庫為每一個使用者開啟的事務,不能被其它事務的運算元據所干擾,事務之間要相互隔離

隔離所導致的一些問題

髒讀:

指一個事務讀取了另一個事務未提交的資料

不可重複讀:

在另一個事務內讀取表中的某一行資料,多次讀取結果不同(這個不一定是錯誤,只是某些場合不對)

虛讀(幻讀):

是指在一個事務內讀取到了別的事務插入的資料,導致前後讀取不一致

執行事務

-- MySQL預設開啟事務自動提交
SET autocommit = 0  -- 關閉
SET autocommit = 1  -- 開啟

-- 手動處理事務
SET autocommit = 0 

-- 事務開啟
START TRANSACTION -- 標記一個事務的開始,從這個之後的sql都在一個事務內
UPDATE XXX
UPDATE XXX
-- 提交,持久化(成功)
COMMIT
-- 回滾(失敗)
ROLLBACK
-- 事務結束
SET autocommit = 1 -- 開啟自動提交

SAVEPOINT 儲存點名 -- 設定一個事務的儲存點

7、索引#

索引是幫助MySQL高效獲取資料的資料結構

7.1、索引的分類#

  • 主鍵索引(PRIMARY KEY)
    • 唯一的標識,主鍵不可重複,只能有一個列作為主鍵
  • 唯一索引(UNIQUE KEY)
    • 避免重複的列出現,可以重複
  • 常規索引(KEY/INDEX)
    • 預設的,index,key關鍵字來設定
  • 全文索引(FullText)
    • 在特定的資料庫引擎下才有
-- 顯示全部索引
SHOW INDEX FROM `student`

-- 建立一個索引可以用
-- CREATE INDEX 索引名 on 表(欄位)

-- 新增一個全文索引 
ALTER TABLE `student` ADD FULLTEXT INDEX `studentname`(`studentname`)

-- EXPLAIN 分析sql執行的情況
EXPLAIN SELECT * FROM student WHERE MATCH(studentname) AGAINST('周')

索引在小資料量的時候,用處不大,但是在大資料量的時候,區別十分明顯

7.2、索引原則#

  • 索引不是越多越好
  • 不要對經常變動的資料加索引
  • 小資料量的表不需要加索引
  • 索引一般加在常用來查詢的欄位上

索引的資料結構

Hash 型別的索引

Btree:InnoDB的預設資料結構

8、許可權管理和備份#

8.1、使用者管理#

SQL yog視覺化管理

image-20220122153940150

SQL 命令操作

使用者表:mysql.user

本質:讀這張表進行增刪改查

-- 建立使用者
CREATE USER xy IDENTIFIED BY '111111'
DROP USER xy	-- 刪除

-- 修改密碼
SET PASSWORD = PASSWORD('123456')	-- 當前使用者
SET PASSWORD FOR xy = PASSWORD('123123')

-- 重新命名
RENAME USER xy TO xy1

-- 使用者授權 ALL PRIVILEGES 全部的許可權,庫.表
-- ALL PRIVILEGES 除了能給別的使用者授權,其它都能幹
GRANT ALL PRIVILEGES ON *.* TO xy

-- 撤銷許可權
REVOKE ALL PRIVILEGES ON *.* FROM xy

8.2、MySQL備份#

MySQL資料庫備份的方式

  • 直接複製物理檔案
  • 在SQLyog 這種視覺化工具中手動匯出
  • 使用命令列匯出 mysqldump 命令列使用
    • 匯出:mysqldump -h 主機 -u使用者名稱 -p 密碼 資料庫 表名 >物理磁碟位置/檔名
    • 匯入:登入的情況下,切換到指定的資料庫,source 備份檔案
#mysq1dump -h主機 -u使用者名稱 -p密碼 資料庫 表名 > 物理磁碟位置/檔名
mysqldump -hlocalhost -uroot -p123456 school student > D:/a.sql
#mysq1dump -h主機 -u使用者名稱 -p密碼 資料庫 表1 表2 表3 > 物理磁碟位置/檔名
mysqldump -hlocalhost -uroot -p123456 school student > D:/b.sql
#mysq1dump -h主機 u使用者名稱 -p密碼 資料庫 > 物理磁碟位置/檔名
mysqldump -hlocalhost -uroot -p123456 school > D:/c.sql

#登入的情況下,匯入
mysql -uroot -p123456
use school
#source備份檔案
source d:/a.sql
#不登入的情況下,匯入
mysq1 -u使用者名稱 -p密碼 庫名<備份檔案

9、規範資料庫設計#

9.1概述#

槽糕的資料庫設計:

  • 資料冗餘,浪費空間
  • 資料庫的插入和刪除麻煩,異常(遮蔽使用物理外來鍵)
  • 程式效能差

良好的資料庫設計:

  • 節省記憶體空間
  • 保證資料庫的完整性
  • 方便開發系統

資料庫的設計:

  • 分析需求,分析業務和需要處理的資料庫的需求
  • 概要設計:設計關係圖E-R圖

9.2、三大正規化#

第一正規化(1NF)

原子性:保證每一列不可再分

第二正規化(2NF)

前提:滿足第一正規化

每張表只描述一件事情

第三正規化(3NF)

前提:滿足第一正規化和第二正規化

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

規範性 和 效能問題

關聯查詢的表不得超過三張表

  • 考慮商業化的需求和目標,(成本,使用者體驗)資料庫的效能更加重要
  • 在規範效能的問題的時候,需要適當的考慮一下規範性
  • 故意給某些表增加一些冗餘的欄位(從多表查詢變為單表查詢)
  • 故意增加一些計算列(從大資料量降低為小資料量的查詢:索引)

目前關聯式資料庫有六種正規化:第一正規化(1NF)、第二正規化(2NF)、第三正規化(3NF)、巴斯-科德正規化(BCNF)、第四正規化(4NF)和第五正規化(5NF,又稱完美正規化)。

而通常我們用的最多的就是第一正規化(1NF)、第二正規化(2NF)、第三正規化(3NF),也就是本文要講的“三大正規化”。

第一正規化(1NF):要求資料庫表的每一列都是不可分割的原子資料項。

舉例說明:

在上面的表中,“家庭資訊”和“學校資訊”列均不滿足原子性的要求,故不滿足第一正規化,調整如下:

可見,調整後的每一列都是不可再分的,因此滿足第一正規化(1NF);

第二正規化(2NF):在1NF的基礎上,非碼屬性必須完全依賴於候選碼(在1NF基礎上消除非主屬性對主碼的部分函式依賴)

第二正規化需要確保資料庫表中的每一列都和主鍵相關,而不能只與主鍵的某一部分相關(主要針對聯合主鍵而言)。

舉例說明:

在上圖所示的情況中,同一個訂單中可能包含不同的產品,因此主鍵必須是“訂單號”和“產品號”聯合組成,

但可以發現,產品數量、產品折扣、產品價格與“訂單號”和“產品號”都相關,但是訂單金額和訂單時間僅與“訂單號”相關,與“產品號”無關,

這樣就不滿足第二正規化的要求,調整如下,需分成兩個表:

第三正規化(3NF):在2NF基礎上,任何非主屬性不依賴於其它非主屬性(在2NF基礎上消除傳遞依賴)

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

舉例說明:

上表中,所有屬性都完全依賴於學號,所以滿足第二正規化,但是“班主任性別”和“班主任年齡”直接依賴的是“班主任姓名”,

而不是主鍵“學號”,所以需做如下調整:

這樣以來,就滿足了第三正規化的要求。

ps:如果把上表中的班主任姓名改成班主任教工號可能更確切,更符合實際情況,不過只要能理解就行。

10、JDBC(重點)#

image-20220123111634959

java.sql

javax.sql

還需要匯入一個資料庫驅動包 mysql-connector-java-版本.jar

10.1、第一個JDBC程式#

建立測試資料庫

CREATE DATABASE `jdbcStudy` CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `jdbcStudy`;
CREATE TABLE `users`(
 `id` INT PRIMARY KEY,
 `NAME` VARCHAR(40),
 `PASSWORD` VARCHAR(40),
 `email` VARCHAR(60),
 birthday DATE
);

 INSERT INTO `users`(`id`,`NAME`,`PASSWORD`,`email`,`birthday`)
VALUES(1,'zhangsan','123456','zs@sina.com','1980-12-04'),
(2,'lisi','123456','lisi@sina.com','1981-12-04'),
(3,'wangwu','123456','wangwu@sina.com','1979-12-04')

1、建立專案

2、匯入資料庫驅動

3、編寫測試程式碼

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 載入驅動
        Class.forName("com.mysql.jdbc.Driver");

        // 使用者資訊和url
        // useUnicode = true    支援中文字符集
        // character=utf8   設定字符集編碼
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8";
        String username = "root";
        String password = "123456";

        // 連線資料庫物件 Connection 代表資料庫
        Connection connection = DriverManager.getConnection(url,username,password);

        // 建立執行sql的物件 Statement
        Statement statement = connection.createStatement();

        // 執行sql,可能存在結果,檢視返回的結果集
        String sql = "select * from users";

        ResultSet resultSet = statement.executeQuery(sql);

        while(resultSet.next()){
            System.out.println("id = " + resultSet.getObject("id"));
            System.out.println("naem = " + resultSet.getObject("NAME"));
            System.out.println("password = " + resultSet.getObject("PASSWORD"));
            System.out.println("email = " + resultSet.getObject("email"));
            System.out.println("birth = " + resultSet.getObject("birthday"));
            System.out.println("============================");
        }

        // 釋放連線
        resultSet.close();
        statement.close();
        connection.close();
    }

步驟總結:

1、載入驅動

2、連線資料庫DriverManager

3、獲得執行sql的物件 Statement

4、獲得返回的結果集

5、釋放連線

DriverManager

Class.forName("com.mysql.jdbc.Driver");	//固定寫法,載入驅動

        Connection connection = DriverManager.getConnection(url,username,password);
 //  Connection 代表資料庫
 //  可以設定自動提交,事務提交,事務回滾
connection.rollback();
connection.commit();
connection.setAutoCommit();

URL

String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8";

// 協議://主機地址:埠號/資料庫名?

//oralce --1521
//jdbc:oracle:thin:@localhost:1521:sid

Statement

//Statement 執行sql的物件
statement.executeQuery();//查詢操作返回 ResultSet
statement.execute(;//執行任何sQL
statement.executeupdate();//更新、插入、刪除。都是用這個,返回一個受影響的行數

ResultSet

Resultset查詢的結果集,封裝了所有的查詢結果

可以獲得指定的資料型別

resultSet.getObject()	//在不知道列的具體型別時使用
// 如果知道就使用指定的型別

遍歷,指標

resultSet.beforeFirst();//移動到最前面
resultSet.afterLast();//移動到最後面
resultSet.next();//移動到下一個資料
resultSet.previous();//移動到前一行
resultSet.absolute(row);//移動到指定行

10.2、statement物件#

jdbc中的statement物件用於向資料庫傳送SQL語句,想完成對資料庫的增刪改查,只需要透過這個物件向資料庫傳送增刪改查語句即可

Statement物件的executeUpdate方法,用於向資料庫傳送增、刪、改的sql語句,executeUpdate執行完後,將會返回一個整數(即增刪改語句導致了資料庫幾行資料發生了變化)。

Statement.executeQuery方法用於向資料庫傳送查詢語句,executeQuery方法返回代表查詢結果的ResultSet物件。

編寫工具類(利用反射機制),建立配置檔案(db.properties裡面存放Driver和URL等資訊)

image-20220202162544906

image-20220215203828517

工具類:

package lesson01;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class jdbcUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;

    static{
        try {
            InputStream in = jdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);

            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //獲取連線
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }
    //釋放連線
    public static void release(Connection coon , Statement st, ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(st!=null){
            try {
                st.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(coon!=null){
            try {
                coon.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

程式碼:

package lesson01;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class ST_insert {
    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            conn = jdbcUtils.getConnection();
            st = conn.createStatement();
            String sql = "insert into users(id,NAME,PASSWORD,email,birthday) values(4,'xy','4654656','2568798778@qq.com','2020-01-01')";
            int i = st.executeUpdate(sql);
            if(i>0){
                System.out.println("插入成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            jdbcUtils.release(conn,st,rs);
        }
    }
}

SQL隱碼攻擊問題

sql存在漏洞,會被攻擊導致資料洩露,SQL會被拼接 or

//登入業務
//當使用者輸入的使用者名稱和密碼為下述情況時,會返回資料庫中所有資料,導致資料洩露
Login("'or'1=1","'or'1=1")

10.3、PreparedStatement物件#

PreparedStatement可以防止SQL隱碼攻擊,效率更高

防注入的本質:把傳遞進來的引數當做字元,假設其中存在跳脫字元,就直接忽略,比如說 ‘ 會被直接轉義

package lesson01;

import java.sql.Connection;
import java.util.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class PST_insert {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null;
        try {
            conn = jdbcUtils.getConnection();
            //區別:使用佔位符代替引數
            String sql = "insert into users(id,NAME,PASSWORD,email,birthday) values(?,?,?,?,?)";

            st = conn.prepareStatement(sql);    //預編譯sql,先寫sql
            //手動給引數賦值
            st.setInt(1,4);
            st.setString(2,"xy");
            st.setString(3,"536365");
            st.setString(4,"467981234@qq.com");

            //注意點: sql.Date 資料庫
            // java.util.Date new Date().getTime()獲得時間戳
            st.setDate(5,new java.sql.Date(new Date().getTime()));

            //執行
            int i = st.executeUpdate();
            if (i>0){
                System.out.println("插入成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            jdbcUtils.release(conn,st,null);
        }
    }
}

10.4、事務#

程式碼實現

1、開啟事務:conn.setAutoCommit(flase);

2、一組業務執行完畢,提交事務

3、可以在catch語句中顯示的定義回滾語句,但預設失敗就會回滾

10.5、資料連線池#

資料庫連線---執行完畢---釋放

連線 -- 釋放 十分浪費資源

池化技術:準備一些預先的資源,過來就連線預先準備好的

最小連線數(一般就是常用連線數)

最大連線數(業務承載能力上限)

等待超時(超出業務承載能力上限後,排隊等待的最長時間)

編寫連線池,實現一個介面 DateSource

開源資料來源實現

DBCP

C3P0

Druid:阿里巴巴

使用了這些資料庫連線池後,我們在專案開發中就不需要編寫連線資料庫的程式碼了

DBCP

需要用到的jar包

commons-dbcp-1.4、 commons-pool-1.6

C3P0

需要用到的jar包

c3p0-0.9.5.5、 mchange-common-java-0.2.19

結論

無論使用什麼資料來源,本質還是一樣的,DataSource介面不會變,方法就不會變

相關文章