1. 程式人生 > >使用mysql以及連線資料庫

使用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" 

在連線之前,需要啟動伺服器守護程式,可以通過下面兩種方式之一:

  1. 啟動相關 windows 服務,比如 MySQL 服務
  2. 通過命令列視窗,執行 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 中有若干內建資料庫:

clip_2018-09-05_07-24-23.png

想要切換到某個資料庫下,使用 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 是一個非常重要非常特殊的表,它儲存了所有的賬號資訊,以及儲存了賬號的許可權資訊。

如果要操作使用者和許可權,有兩種方式:

  1. 使用相關的授權語句
  2. 直接修改 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 類:

  1. 整數: bit / bool / tinyint / smallint / mediumint / int / bitint
  2. 浮點數: float / double / decimal
  3. 字串: char / varchar / tinytext / text / mediumtext / longtext
  4. BLOB(Binary Large Object): tinyblob / blob / mediumblob / longblob
  5. 日期: date(2018-9-6) / time (03:33:49) / datetime(2018-9-6 03:34:08) / timestamp
  6. 其他: binary / varbinary / enum / set / geometry / point / LineString …

問題:

  1. char 跟 varchar 有什麼區別?
  2. varchar 最多儲存的長度是多少?
  3. varchar 跟 text 型別有什麼區別?
  4. 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:

  1. char(n) 和 varchar(n) 中間的 n 代表字元的個數,不是位元組的個數
  2. 如果資料超過 n 的限制,那麼資料將會被截斷
  3. char 是固定寬度,如果資料長度不滿足 n,那麼將會在右邊用空格補齊;varchar 是變長寬度
  4. varchar 是標準型別;text 不是標準型別
  5. varchar 跟 text 都最大儲存 65535 位元組長度的資料。但是 text 還有 mediumtext/longtext 它可以支援儲存更多的內容
  6. 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,從效能和實際出發,應該遵循下面幾點:

  1. 儘量短,避免佔用太多空間
  2. 唯一,不能跟其他行的資料重複
  3. 不會改變
  4. 代理鍵

所謂代理鍵是指跟業務無關的欄位:

  • 因為和業務無關,所以可以使用 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] 儲存過程不建議使用:

  1. 難以除錯,難以維護
  2. 業務邏輯跟資料庫耦合度過高,不便於遷移
  3. 對資料庫的壓力過大,需要分拆
  4. 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 變數定義

變數分類:

  1. 區域性變數,使用在儲存過程或函式中,這樣的變數使用 declare 進行宣告,使用 set 進行賦值。作用域是過程中。
  2. 使用者變數,定義 set @xxx = 333; select @xxx := 333; 需要用 @ 符號進行修飾
  3. 系統變數,系統自帶的,內建的變數
    • 可以通過命令列的方式進行設定 mysqld –default_storage_engine=MyISAM
    • 可以在 my.ini 配置檔案中進行配置 default_storage_engine=MyISAM
    • 可以通過 set 的方式進行動態設定
    • 系統變數,分為兩類:
      1. 全域性的(global)

        show global variables;
        set global default_storage_engine=MyISAM;
        set @@global.default_storage_engine=MyISAM;
        
      2. 會話的(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); } } 

11 優化(Optimization)