使用mysql以及連線資料庫
MySQL
Table of Contents
1 安裝與配置
MySQL/MariaDB:
比如,MariaDB 的下載頁面:
下載安裝即可:
- 注意,要注意勾選“將utf8作為預設字符集”
- 安裝過程中需要提供 root 使用者的密碼,需要記住
- 安裝完後,需要將 bin 目錄新增到環境變數中
- 可以選擇將 mysqld 安裝為 windows 服務,如果沒有勾選,那麼也可以手動建立服務
建立服務的方式:
# 如果要刪除相關服務,只需要在“管理員身份”的命令列視窗下面執行:
# sc delete 服務名字
# 將 MySQL 安裝為服務的方式: "C:\Program Files\MariaDB 10.3\bin\mysqld.exe" install MyDB --defaults-file="C:\Program Files\MariaDB 10.3\my.ini"
在連線之前,需要啟動伺服器守護程式,可以通過下面兩種方式之一:
- 啟動相關 windows 服務,比如 MySQL 服務
- 通過命令列視窗,執行
mysqld --defaults-file=my.ini
命令
服務啟動後,就可以通過客戶端訪問了:
mysql -u root -p
mysql -uroot -proot
mysql -h 45.42.32.1 -P 9999 -uroot -p
2 資料庫與賬戶
MySQL 在使用者登入後(預設使用 root 使用者登入),需要切換到一個 database
剛安裝完之後,MySQL 中有若干內建資料庫:
想要切換到某個資料庫下,使用 use
指令:
use mysql
我們可以為自己的業務建立單獨的資料庫,比如,為我們的書店專案我們建立一個單獨的資料庫:
create database bookstore;
create database bookstore character set UTF8; -- 建立時可以指定字符集
use bookstore; -- 切換到資料庫下
雖然 root 使用者強大無比,但是使用 root 使用者進行資料庫操作不是一個好的行為。所以我們需要為資料庫的操作建立相關的使用者:
-- 特別需要注意,在 MySQL 中,賬號由兩部分組成:
-- 1. user
-- 2. host
-- 即使 user 相同,只要 host 不同,也會被認為是不同賬號。
-- 這樣可以非常方便對來自不同 ip 地址的訪問進行精細的許可權控制。
-- 預設情況下,建立的使用者 host 為 '%',這是一個匹配符,跟模糊查詢裡的意思一樣,表示匹配所有 create user vip identified by 'vippp'; -- 所有連線 create user [email protected]'127.0.0.1' identified by 'xxx'; -- 本地連線 create user [email protected]'192.168.%' identified by 'yyy'; -- 192.168 網段的連線
新建立的使用者沒有多少許可權,登入之後,只能訪問操作 test 以及以 test 開頭的資料庫。如果想讓新使用者有更多許可權,就需要用到授權語句(DCL):
grant all on *.* to [email protected]'127.0.0.1'; -- 將所有資料庫上的所有權利都授予通過本機連線的 vip 使用者! grant all privileges on lagou.* to [email protected]'%'; -- 將資料庫 lagou 上的所有權利都授予所有連線的 vip 使用者! grant select on lagou.position to [email protected]'%'; -- 將 lagou 資料庫上的 position 表的訪問許可權開放給所有 vip 使用者。
如果想檢視某個使用者擁有的許可權,只需要:
show grants for [email protected]'%';
3 使用者跟許可權
使用者跟許可權的相關資訊,都是儲存在下面表中:
- mysql.user
- mysql.db/host/table/priv…
mysql.user
是一個非常重要非常特殊的表,它儲存了所有的賬號資訊,以及儲存了賬號的許可權資訊。
如果要操作使用者和許可權,有兩種方式:
- 使用相關的授權語句
- 直接修改 mysql.user 表
第一種方式:
-- 檢視使用者
select current_user(), user();
select * from mysql.user; -- 建立與授權 create user 'xxx'@'host' identified by '密碼'; grant all on ttt.* to 'user'@'host' with grant options; -- 檢視授權情況 show grants for 'user'@'host'; -- 使用者的管理,刪除密碼等 set password for 'user'@'host' = password('新密碼'); drop user vip; -- 授權的相對完整語法為: grant all/alter/create/drop/select/update/delete on *.* -- db.*/db.table to 'user'@'host' identified by '密碼' with max_user_connections 2 max_connections_pser_hour 5;
第二種方式:
-- 增加使用者
insert into mysql.user(host, user, password) values (xx, yy, zz); -- 修改密碼 update mysql.user set password=password('新密碼') where user='vip' and host='%'; -- 修改許可權 update mysql.user set event_priv='Y' where user='vip' and host='%'; -- 注意,使用 sql 語句修改使用者跟許可權之後,需要手動重新整理許可權表 flush privileges;
4 常用命令
基本命令:
-- 查詢當前日期
select now(), current_date, current_time;
-- 查詢當前使用者
select user(), current_user(); -- 資料庫 show databases; use test;
更多命令:
show engines; -- 所有引擎
show plugins; -- 所有外掛
show variables; -- 所有系統變數
show variables like '%char%'; -- 可以使用 like 進行過濾, 注意 % _ 的使用 set names utf8; -- 一個快捷方式,用來快速批量設定字符集 show full processlist; kill 109;
5 表的建立
create table book1 (
bookid int primary key ); create table book2 ( bookid int, primary key (bookid) ); create table book3 ( bookid int auto_increment primary key ); create table book4 ( bookid int auto_increment primary key, name varchar(30) not null, price float check (price >= 0) ); create table book5 ( bookid int auto_increment primary key, name varchar(30) not null, price float check (price >= 0), press varchar(50), authorid int references author (authorid) -- foreign key(author) references author(authorid), ); create table book6 ( bookid int auto_increment, name varchar(30) not null, price float check (price >= 0), press varchar(50), authorid int, primary key (bookid), foreign key (authorid) references author (authorid) ); create table book7 ( bookid int auto_increment primary key, name varchar(30) not null, price float check (price >= 0), press varchar(50), authorid int ); create table author ( authorid int auto_increment, name varchar(30) not null, tel varchar(20), birth date, primary key (authorid) ); -- 實際的匯入情況,首先建立表,先不新增任何外來鍵約束 create table book ( bookid int auto_increment primary key, name varchar(30) not null, price float check (price >= 0), press varchar(50), authorid int, publish_at datetime, created_at timestamp ); create table author ( authorid int auto_increment primary key, name varchar(30) not null, tel varchar(20), birth date ); -- 在這裡,執行所有的資料插入操作 -- insert into book ... -- insert into author ... alter table book add constraint fk_authorid foreign key (authorid) references author (authorid); -- 如果需要新增主鍵的話,語法如下 alter table book add constraint pk_bookid primary key (bookid); -- 如果想刪除掉的話 alter table book drop foreign key fk_authorid;
其他:
create table xxx if not exists (id int); create temporary table yyy (id int); create temporary table if not exists yyy ( id bigint auto_increment, name varchar(20) not null unique, -- 唯一,不重複的 birth datetime, gender enum('f', 'm'), -- Female Male minzu set('漢', '俄羅斯'), created_at timestamp, primary key (id) ); create table aaa (id int auto_increment primary key, name varchar(20)) default charset = gbk engine = InnoDB auto_increment = 7 comment = `這只是一個測試表而已`; create table gbk_test2 (vvv varchar(4)) default charset = gbk; create table gbk_test3 (vvv varchar(4)) default charset = utf8; insert into gbk_test2 values ('中國'); insert into gbk_test3 values ('中國'); select * from gbk_test2; select * from gbk_test3; select length(vvv) from gbk_test2; select length(vvv) from gbk_test3; select char_length(vvv) from gbk_test2; select char_length(vvv) from gbk_test3;
一致性,完整性
id | 名字 | xxx |
---|---|---|
1 | Java | 2 |
2 | 太極劍法 | 1 |
3 | MySQL指南 | 22 |
id | 名字 | 生日 |
---|---|---|
1 | 張三丰 | x |
2 | 我 | y |
3 | 你好 | z |
6 資料型別
大致分為 6 類:
- 整數: bit / bool / tinyint / smallint / mediumint / int / bitint
- 浮點數: float / double / decimal
- 字串: char / varchar / tinytext / text / mediumtext / longtext
- BLOB(Binary Large Object): tinyblob / blob / mediumblob / longblob
- 日期: date(2018-9-6) / time (03:33:49) / datetime(2018-9-6 03:34:08) / timestamp
- 其他: binary / varbinary / enum / set / geometry / point / LineString …
問題:
- char 跟 varchar 有什麼區別?
- varchar 最多儲存的長度是多少?
- varchar 跟 text 型別有什麼區別?
- set/enum 該怎麼去使用?
create table char_demo
(
aaa char(40),
bbb varchar(40),
ccc text(40)
);
insert into char_demo values ('123', '123', '123'); select length(aaa), length(bbb), length(ccc) from char_demo; SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH'; select length(aaa), length(bbb), length(ccc) from char_demo;
總結 Char vs Varchar vs Text:
- char(n) 和 varchar(n) 中間的 n 代表字元的個數,不是位元組的個數
- 如果資料超過 n 的限制,那麼資料將會被截斷
- char 是固定寬度,如果資料長度不滿足 n,那麼將會在右邊用空格補齊;varchar 是變長寬度
- varchar 是標準型別;text 不是標準型別
- varchar 跟 text 都最大儲存 65535 位元組長度的資料。但是 text 還有 mediumtext/longtext 它可以支援儲存更多的內容
- varchar 的儲存格式是 stored-inline, text 的儲存格式是 off-record。一般情況來說,varchar 的速度要比 text 快
BLOB 型別,可以儲存大欄位。
ENUM 跟 SET 是兩種比較特殊的字串型別,他們對欄位進行了一定約束:
create table person (
id int auto_increment primary key, name varchar(30) not null, gender char(2) -- 限制,只有 男、女、未知 ); create table person ( id int auto_increment primary key, name varchar(30) not null, gender enum('男', '女', '未知') -- 限制,只有 男、女、未知 ); create table person2 ( id int auto_increment primary key, name varchar(30) not null, gender enum('男', '女', '未知'), -- 限制,只有 男、女、未知 country set('中國', '美國', '南非') ); insert into person3 (name, gender, country) values ('滅絕', '女', '美國,南非');
7 主鍵約束
主鍵,primary key,從效能和實際出發,應該遵循下面幾點:
- 儘量短,避免佔用太多空間
- 唯一,不能跟其他行的資料重複
- 不會改變
- 代理鍵
所謂代理鍵是指跟業務無關的欄位:
- 因為和業務無關,所以可以使用 int 等型別儘量短
- 因為和業務無關,所以可以避免因為業務變動引起的主鍵變化
- 不要相信自己的直覺,因為在直覺上感覺不會變的東西,往往都是經不起實踐考驗的
8 表的修改
基本語法:
ALTER [ONLINE|OFFLINE] [IGNORE] TABLE tbl_name
[alter_specification [, alter_specification] ...]
[partition_options]
alter_specification:
table_options
| ADD [COLUMN] col_name column_definition
[FIRST | AFTER col_name] | ADD [COLUMN] (col_name column_definition,...) | ADD {INDEX|KEY} [index_name] [index_type] (key_part,...) [index_option] ... | ADD [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (key_part,...) [index_option] ... | ADD [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY] [index_name] [index_type] (key_part,...) [index_option] ... | ADD FULLTEXT [INDEX|KEY] [index_name] (key_part,...) [index_option] ... | ADD SPATIAL [INDEX|KEY] [index_name] (key_part,...) [index_option] ... | ADD [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (col_name,...) reference_definition | ALGORITHM [=] {DEFAULT|INPLACE|COPY} | ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT} | CHANGE [COLUMN] old_col_name new_col_name column_definition [FIRST|AFTER col_name] | [DEFAULT] CHARACTER SET [=] charset_name [COLLATE [=] collation_name] | CONVERT TO CHARACTER SET charset_name [COLLATE collation_name] | {DISABLE|ENABLE} KEYS | {DISCARD|IMPORT} TABLESPACE | DROP [COLUMN] col_name | DROP {INDEX|KEY} index_name | DROP PRIMARY KEY | DROP FOREIGN KEY fk_symbol | FORCE | LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE} | MODIFY [COLUMN] col_name column_definition [FIRST | AFTER col_name] | ORDER BY col_name [, col_name] ... | RENAME [TO|AS] new_tbl_name | ADD PARTITION (partition_definition) | DROP PARTITION partition_names | TRUNCATE PARTITION {partition_names | ALL} | COALESCE PARTITION number | REORGANIZE PARTITION partition_names INTO (partition_definitions) | EXCHANGE PARTITION partition_name WITH TABLE tbl_name | ANALYZE PARTITION {partition_names | ALL} | CHECK PARTITION {partition_names | ALL} | OPTIMIZE PARTITION {partition_names | ALL} | REBUILD PARTITION {partition_names | ALL} | REPAIR PARTITION {partition_names | ALL} | REMOVE PARTITIONING key_part: col_name [(length)] [ASC | DESC] index_type: USING {BTREE | HASH} index_option: KEY_BLOCK_SIZE [=] value | index_type | WITH PARSER parser_name | COMMENT 'string' table_options: table_option [[,] table_option] ... table_option: AUTO_INCREMENT [=] value | AVG_ROW_LENGTH [=] value | [DEFAULT] CHARACTER SET [=] charset_name | CHECKSUM [=] {0 | 1} | [DEFAULT] COLLATE [=] collation_name | COMMENT [=] 'string' | CONNECTION [=] 'connect_string' | {DATA|INDEX} DIRECTORY [=] 'absolute path to directory' | DELAY_KEY_WRITE [=] {0 | 1} | ENGINE [=] engine_name | INSERT_METHOD [=] { NO | FIRST | LAST } | KEY_BLOCK_SIZE [=] value | MAX_ROWS [=] value | MIN_ROWS [=] value | PACK_KEYS [=] {0 | 1 | DEFAULT} | PASSWORD [=] 'string' | ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT} | STATS_AUTO_RECALC [=] {DEFAULT|0|1} | STATS_PERSISTENT [=] {DEFAULT|0|1} | STATS_SAMPLE_PAGES [=] value | TABLESPACE tablespace_name [STORAGE {DISK|MEMORY|DEFAULT}] | UNION [=] (tbl_name[,tbl_name]...)
基本示例:
-- 新增列
alter table person add column qq varchar(20); alter table person add qq varchar(20); -- 可以去掉 column 關鍵詞 alter table person add tel varchar(20) after gender; -- 指定位置 alter table person add (xxx int, yyy int); -- 修改列 ALTER TABLE t2 MODIFY a TINYINT NOT NULL, CHANGE b c CHAR(20); -- 刪除主鍵 alter table person change id id int; -- 需要將 auto_increment 去掉 alter table person drop primary key; -- 增加主鍵 alter table person add primary key (id); -- 增加/刪除外來鍵 alter table book add constraint fk_authorid foreign key (authorid) references author (authorid); -- 增加/刪除索引 ALTER TABLE t2 ADD INDEX (d), ADD UNIQUE (a); -- 重新命名 alter table rename to people;
9 引擎(Engine)
-
MySQL 為了滿足各種需求,提供了不同的引擎實現,比如 MyISAM/InnoDB 等。
show engines
- 可以為資料庫指定預設引擎,只需要在 my.ini 配置檔案中增加
default-storage-engine=InnoDB
就可以了 -
也可以在建立表的時候,手動指定使用的引擎:
create table xxx (id int) engine=MyISAM;
- 在早期(<5.0)MySQL 的預設引擎是 MyISAM,它的特點是快,它的缺點是功能簡單,不支援事務包括外來鍵等功能
- 目前(>5)預設引擎是 InnoDB,它為資料庫提供了事務支援、外來鍵支援、鎖的支援等特性
事務相關的語法有:
start transaction; -- begin;
savepoint xxx; -- 存檔
rollback; -- 回滾 rollback to xxx; -- 回檔到某個儲存點 commit; -- 提交事務 -- 創表 create table xxx_inno (id int) engine=InnoDB; create table xxx_isam (id int) engine=MyISAM; -- 檢視 show create table xxx_inno; show create table xxx_isam; -- 檢視 xxx_inno 的事務支援情況 -- 首先,開啟事務 start transaction; -- 然後,插入資料 insert into xxx_inno values (111); insert into xxx_inno values (222); -- 現在,在其他的連線中,是查詢不到這資料的 select * from xxx_inno; -- 建立儲存點(存檔) savepoint s1; -- 再插入更多資料 insert into xxx_inno values (333); -- 現在,在當前事務中,可以查到3條資料 select * from xxx_inno; -- 使用 rollback 回檔,將回到插入 333 前的狀態 rollback to s1; -- 提交事務 commit; -- 現在,在其他的連線中,就能查詢到這些資料了 select * from xxx_inno; -- 檢視 xxx_isam 的事務支援情況 start transaction; insert into xxx_isam values (666); -- 在沒有 commit 的時候,在其他連線中竟然能查詢這資料 -- 說明 MyISAM 是不支援事務的 select * from xxx_isam;
10 儲存過程
[WARNNING] 儲存過程不建議使用:
- 難以除錯,難以維護
- 業務邏輯跟資料庫耦合度過高,不便於遷移
- 對資料庫的壓力過大,需要分拆
- MySQL 對儲存過程的支援有些弱雞
10.1 基本語句
-- 儲存過程的定義
delimiter // -- 使用 delimiter 指令,將預設的分隔符(;)換為我們自定義的分隔符
create procedure ppx1 (p int) -- 建立儲存過程,名字 ppx1, 一個入參 int 型別的 p begin -- 使用 begin 跟 end 將所有語句包括起來,相當於 {} set @x = 0; -- set 是用來定義或者設定變數的值的,使用者級別的變數,需要使用 @ 作為字首 repeat -- repeat .. until .. end repeat 這是使用迴圈的其中一種方式 set @x = @x + 1; -- 在迴圈體中,我們就可以進行我們不斷重複的業務、事情了 until @x > p end repeat; -- 使用 until 表示迴圈結束的條件。類似於 while 迴圈中的 break end delimiter ; -- 過程定義完成後,需要將分隔符還原為預設(;) -- 呼叫儲存過程 call ppx1(100); -- 檢視使用者定義變數的值 select @x;
求和 1-n:
create procedure sum2 (y int)
begin
declare x int; -- 宣告區域性變數 set x = 0; -- 為區域性變數賦值 set @sum = 0; repeat set x = x + 1; set @sum = @sum + x; until x >= y end repeat; end
10.2 變數定義
變數分類:
- 區域性變數,使用在儲存過程或函式中,這樣的變數使用 declare 進行宣告,使用 set 進行賦值。作用域是過程中。
- 使用者變數,定義 set @xxx = 333; select @xxx := 333; 需要用 @ 符號進行修飾
- 系統變數,系統自帶的,內建的變數
- 可以通過命令列的方式進行設定 mysqld –default_storage_engine=MyISAM
- 可以在 my.ini 配置檔案中進行配置 default_storage_engine=MyISAM
- 可以通過 set 的方式進行動態設定
- 系統變數,分為兩類:
-
全域性的(global)
show global variables; set global default_storage_engine=MyISAM; set @@global.default_storage_engine=MyISAM;
-
會話的(session)
show session variables; show local variables; show variables; set session default_storage_engine=MyISAM; set local default_storage_engine=MyISAM; set @@session.default_storage_engine=MyISAM; set @@local.default_storage_engine=MyISAM; set default_storage_engine=MyISAM; set @@default_storage_engine=MyISAM;
-
10.3 引數與遊標
如果讓儲存過程跟外邊進行資料互動,需要用到引數
第一種,在過程內部使用使用者變數 @xxx
delimiter //
create procedure pp1 ()
begin
declare v1 int default 100; set @aaa = v1*101; end // delimiter ; -- 呼叫 call pp1(); select @aaa;
另外,可以將表中查詢到的資料賦予引數:
delimiter //
drop procedure if exists pp2;
create procedure pp2 () begin select count(*) into @bbb from t_person; select weixin into @ccc from t_person limit 1; end // delimiter ; -- 呼叫 call pp2(); select @bbb, @ccc;
使用過程內部定義使用者變數的方式,容易在呼叫的時候產生衝突、混淆,所以最好使用外部定義,結合 out/inout 型別引數:
delimiter //
drop procedure if exists pp3;
create procedure pp3 (inout bbb varchar(30), inout ccc varchar(20)) -- in out in out begin insert into t_person (name, weixin) value (bbb, ccc); select count(*) into bbb from t_person; select weixin into ccc from t_person limit 1; end // delimiter ; -- 呼叫 set @ddd = '3333'; set @eee = 'sdfsfsd'; call pp3(@ddd, @eee); select @ddd, @eee;
如果需要處理表中的多行資料,就需要用到遊標(Cursor):
delimiter //
drop procedure if exists pp4;
create procedure pp4 (out sum bigint) begin declare iii int; declare done int default 0; declare c_person cursor for select weixin from t_person; -- 1. 遊標的定義 declare continue handler for not found set done = 1; -- 捕獲系統丟擲的 not found 錯誤,如果捕獲到,將 done 設定為 1 set sum = 0; open c_person; -- 2. 開啟遊標 xxx:loop fetch c_person into iii; -- 3. 使用遊標 if done = 1 then -- 4. 設定退出條件 leave xxx; end if; set sum = sum + iii; end loop; close c_person; -- 5. 關閉遊標 end // delimiter ; -- 呼叫 set @sum = 0; call pp4(@sum); select @sum;
10.4 JDBC 操作儲存過程示例
儲存過程示例:
delimiter //
drop procedure if exists pp6;
create procedure pp6 (inout bbb varchar(30), inout ccc varchar(20)) -- in out in out begin insert into t_person (name, weixin) value (bbb, ccc); select count(*) into bbb from t_person; select weixin into ccc from t_person limit 1; select * from lagou_city where province='廣東省'; select * from lagou_pos order by salary_min desc limit 10; end // delimiter ;
JDBC程式碼:
// Statement PrepareStatement CallableStatement
public static void main(String[] args) { Connection connection = null; CallableStatement ps = null; ResultSet rs = null; try { connection = DBUtil.getMySQLConnection(); ps = connection.prepareCall("call pp6(?, ?)"); ps.setString(1, "dfasd"); ps.setString(2, "16463"); ps.registerOutParameter(1, Types.VARCHAR); ps.registerOutParameter(2, Types.VARCHAR); boolean hasMore = ps.execute(); while (hasMore) { rs = ps.getResultSet(); while (rs.next()) { System.out.println(rs.getObject(1) + "/" + rs.getObject(2)); } rs.close(); hasMore = ps.getMoreResults(); } System.out.println(ps.getObject(1)); System.out.println(ps.getObject(2)); } catch (Exception e) { e.printStackTrace(); } finally { DBUtil.tryRelease(connection, ps, rs); } }