第21章 創建和操縱表
21.1 創建表
MySQL不僅用于表數據操縱,而且還可以用來執行數據庫和表的所有操作,包括表本身的創建和處理.
一般有兩種創建表的方法:
- 使用具有交互式創建和管理表的工具(如第2章討論的工具);
- 表也可以直接用MySQL語句操縱.
為了用程序創建表,可使用SQL的CREATE TABLE語句.值得注意的是,在使用交互式工具時,實際上使用的是MySQL語句.但是,這些語句不是用戶編寫的,界面工具會自動生成并執行相應的MySQL語句
21.1.1 表創建基礎
為利用CREATE TABLE創建表,必須給出下列信息:
- 新表的名字,在關鍵字CREATE TABLE之后給出;
- 表列的名字和定義,用逗號分隔.
CREATE TABLE語句也可能會包括其他關鍵字或選項,但至少要包括表的 名字和列的細節.
語句格式化
CREATE TABLE語句就是語句格式化的一個很好的例子,它被安排在多個行上,其中的列定義進行了恰當的縮進,以便閱讀和編輯.以何種縮進格式安排SQL語句沒有規定,但我強烈推薦采用某種縮進格式.
處理現有的表
在創建新表時,指定的表名必須不存在,否則將出錯.如果要防止意外覆蓋已有的表,SQL要求首先手工刪除該表(請參閱后面的小節),然后再重建它,而不是簡單地用創建表語句覆蓋它.
如果你僅想在一個表不存在時創建它,應該在表名后給出IF NOT EXISTS.這樣做不檢查已有表的模式是否與你打算創建的表模式相匹配.它只是查看表名是否存在,并且僅在表名不存在時創建它.
NULL和空串
理解NULL
不要把NULL值與空串相混淆.NULL值是沒有值,它不是空串.如果指定''(兩個單引號,其間沒有字符),這在NOT NULL列中是允許的.空串是一個有效的值,它不是無值.NULL值用關鍵字NULL而不是空串指定.
例子:
CREATE TABLE customers(
cust_id int NOT NULL AUTO_INCREMENT,
cust_name char(50) NOT NULL,
cust_address char(50) NULL,
cust_city char(50) NULL,
cust_state char(5) NULL,
cust_zip char(10) NULL,
cust_country char(50) NULL,
cust_contact char(50) NULL,
cust_email char(255) NULL,
PRIMARY KEY(cust_id)
)ENGINE=InnoDB;
21.1.2 使用NULL值
NULL值就是沒有值或缺值.允許NULL值的列也允許在插入行時不給出該列的值.
不允許NULL值的列不接受該列沒有值的行,換句話說,在插入或更新行時,該列必須有值.
每個表列或者是NULL列,或者是NOT NULL列,這種狀態在創建時由表的定義規定.
例子:
CREATE TABLE order(
order_num int NOT NULL AUTO_INCREMENT,
order_date datetime NOT NULL,
cust_id int NOT NULL,
PRIMARY KEY(order_num)
)ENGINE=InnoDB;
CREATE TABLE vendors(
vend_id int NOT NULL AUTO_INCREMENT,
vend_name char(50) NOT NULL,
vend_address char(50) NULL,
vend_city char(50) NULL,
vend_state char(5) NULL,
vend_zip char(10) NULL,
vend_country char(50) NULL,
PRIMARY KEY(vend_id)
)ENGINE=InnoDB;
21.1.3 主鍵再介紹
主鍵值必須唯一.即,表中的每個行必須具有唯一的主鍵值.如果主鍵使用單個列,則它的值必須唯一.如果使用多個列,則這些列的組合值必須唯一.
主鍵可以在創建表時定義,或者在創建表之后定義.
主鍵和NULL值
第1章介紹過,主鍵為其值唯一標識表中每個行的列.主鍵中只能使用不允許NULL值的列.允許NULL值的列不能作為唯一標識.
21.1.4 使用AUTO_INCREMENT
AUTO_INCREMENT告訴MySQL,本列每當增加一行時自動增量.每次執行一個INSERT操作時,MySQL自動對該列增量(從而才有這個關鍵字AUTO_INCREMENT),給該列賦予下一個可用的值.這樣給每個行分配一個唯一的cust_id,從而可以用作主鍵值.
每個表只允許一個AUTO_INCREMENT列,而且它必須被索引(如,通過使它成為主鍵).
覆蓋AUTO_INCREMENT
如果一個列被指定為AUTO_INCREMENT,則它需要使用特殊的值嗎?你可以簡單地在INSERT語句中指定一個值,只要它是唯一的(至今尚未使用過)即可,該值將被用來替代自動生成的值.后續的增量將開始使用該手工插入的值.
確定AUTO_INCREMENT值讓MySQL生成(通過自動增量)主 鍵的一個缺點是你不知道這些值都是誰.
考慮這個場景:你正在增加一個新訂單.這要求在orders表中創建一行,然后在orderitms表中對訂購的每項物品創建一行.order_num在orderitems表中與訂單細節一起存儲.這就是為什么orders表和orderitems表為相互關聯的表的原因.這顯然要求你在插入orders行之后,插入orderitems行之前知道生成的order_num.
那么,如何在使用AUTO_INCREMENT列時獲得這個值呢?可使用last_insert_id()函數獲得這個值,如下所示:
SELECT last_insert_id()
此語句返回最后一個AUTO_INCREMENT值,然后可以將它用于后續的MySQL語句.
21.1.5 指定默認值
如果在插入行時沒有給出值,MySQL允許指定此時使用的默認值.默認值用CREATE TABLE語句的列定義中的DEFAULT關鍵字指定.
不允許函數 與大多數DBMS不一樣,MySQL不允許使用函數作為默認值,它只支持常量.
使用默認值而不是NULL值 許多數據庫開發人員使用默認值而不是NULL列,特別是對用于計算或數據分組的列更是如此.
例子:
CREATE TABLE orderitems(
order_num int NOT NULL,
order_item int NOT NULL,
prod_id char(10) NOT NULL,
quantity int NOT NULL DEFAULT 1,
item_price decimal(8,2) NOT NULL,
PRIMARY KEY(order_num, order_item)
)ENGINE=InnoDB;
21.1.6 引擎類型
MySQL與其他DBMS不一樣,它具有多種引擎.它打包多個引擎,這些引擎都隱藏在MySQL服務器內,全都能執行CREATE TABLE和SELECT等命令.
為什么要發行多種引擎呢?因為它們具有各自不同的功能和特性,為不同的任務選擇正確的引擎能獲得良好的功能和靈活性.
當然,你完全可以忽略這些數據庫引擎.如果省略ENGINE=語句,則使用默認引擎(很可能是InnoDB),多數SQL語句都會默認使用它.但并不是所有語句都默認使用它,這就是為什么ENGINE=語句很重要的原因.
以下是幾個需要知道的引擎:
- InnoDB是一個可靠的事務處理引擎,它不支持全文本搜索;
- MEMORY在功能等同于MyISAM,但由于數據存儲在內存(不是磁盤) 中,速度很快(特別適合于臨時表);
- MyISAM是一個性能極高的引擎,它支持全文本搜索, 但不支持事務處理.
外鍵不能跨引擎
混用引擎類型有一個大缺陷.外鍵不能跨引擎,即使用一個引擎的表不能引用具有使用不同引擎的表的外鍵.
那么,你應該使用哪個引擎?這有賴于你需要什么樣的特性.MyISAM由于其性能和特性可能是最受歡迎的引擎.但如果你不需要可靠的事務處理,可以使用其他引擎.
21.2 更新表
為更新表定義,可使用ALTER TABLE語句.但是,理想狀態下,當表中存儲數據以后,該表就不應該再被更新.在表的設計過程中需要花費大量時間來考慮,以便后期不對該表進行大的改動.
為了使用ALTER TABLE更改表結構,必須給出下面的信息:
- 在ALTER TABLE之后給出要更改的表名(該表必須存在,否則將出錯);
- 所做更改的列表.
ALTER TABLE的一種常見用途是定義外鍵.
復雜的表結構更改一般需要手動刪除過程,它涉及以下步驟:
- 用新的列布局創建一個新表;
- 使用INSERT SELECT語句從舊表復制數據到新表.如果有必要,可使用轉換函數和計算字段;
- 檢驗包含所需數據的新表;
- 重命名舊表(如果確定,可以刪除它);
- 用舊表原來的名字重命名新表;
- 根據需要,重新創建觸發器、存儲過程、索引和外鍵.
小心使用ALTER TABLE
使用ALTER TABLE要極為小心,應該在進行改動前做一個完整的備份(模式和數據的備份).數據庫表的更改不能撤銷,如果增加了不需要的列,可能不能刪除它們.類似地,如果刪除了不應該刪除的列,可能會丟失該列中的所有數據.
例子:
給表添加一個列
ALTER TABLE vendors ADD vend_phone CHAR(20);
刪除表中的一列
ALTER TABLE vendors DROP COLUME vend_phone;
21.3 刪除表
刪除表(刪除整個表而不是其內容)非常簡單,使用DROP TABLE語句即可
刪除表沒有確認,也不能撤銷,執行這條語句將永久刪除該表.
例子:
刪除customers2表
DROP TABLE customers2;
21.4 重命名表
使用RENAME TABLE語句可以重命名一個表
例子:
將表customers2重命名為customers
RENAME TABLE customers2 TO customers;
對多個表進行重命名
RENAME TABLE backup_customers TO customers,
backup_vendors TO vendors,
backup_products TO products;
21.5 小結
第22章 使用視圖
22.1 視圖
視圖是虛擬的表.與包含數據的表不一樣,視圖只包含使用時動態檢索數據的查詢.
例子:
SELECT cust_name, cust_contact FROM customers, orders, orderitems WHERE customers.cust_id=order_cust_id AND orderitems.order_num=order.order_num AND proid='TNT2';
22.1.1 為什么使用視圖
視圖的一些常見應用.
- 重用SQL語句.
- 簡化復雜的SQL操作.在編寫查詢后,可以方便地重用它而不必知道它的基本查詢細節.
- 使用表的組成部分而不是整個表.
- 保護數據.可以給用戶授予表的特定部分的訪問權限而不是整個表的訪問權限.
- 更改數據格式和表示.視圖可返回與底層表的表示和格式不同的數據.
在視圖創建之后,可以用與表基本相同的方式利用它們.可以對視圖執行SELECT操作,過濾和排序數據,將視圖聯結到其他視圖或表,甚至能添加和更新數據.
重要的是知道視圖僅僅是用來查看存儲在別處的數據的一種設施. 視圖本身不包含數據,因此它們返回的數據是從其他表中檢索出來的. 在添加或更改這些表中的數據時,視圖將返回改變過的數據.
性能問題
因為視圖不包含數據,所以每次使用視圖時,都必須處理查詢執行時所需的任一個檢索.如果你用多個聯結和過濾創建了復雜的視圖或者嵌套了視圖,可能會發現性能下降得很厲害.因此,在部署使用了大量視圖的應用前,應該進行測試.
22.1.2 視圖的規則和限制
關于視圖創建和使用的一些最常見的規則和限制.
- 與表一樣,視圖必須唯一命名(不能給視圖取與別的視圖或表相 同的名字).
- 對于可以創建的視圖數目沒有限制.
- 為了創建視圖,必須具有足夠的訪問權限.這些限制通常由數據庫管理人員授予.
- 視圖可以嵌套,即可以利用從其他視圖中檢索數據的查詢來構造一個視圖.
- ORDER BY可以用在視圖中,但如果從該視圖檢索數據SELECT中也含有ORDER BY,那么該視圖中的ORDER BY將被覆蓋.
- 視圖不能索引,也不能有關聯的觸發器或默認值.
- 視圖可以和表一起使用.例如,編寫一條聯結表和視圖的SELECT語句.
22.2 使用視圖
在理解什么是視圖(以及管理它們的規則及約束)后,我們來看一下視圖的創建.
- 視圖用CREATE VIEW語句來創建.
- 使用SHOW CREATE VIEW viewname;來查看創建視圖的語句.
- 用DROP刪除視圖,其語法為DROP VIEW viewname;.
- 更新視圖時,可以先用DROP再用CREATE,也可以直接用CREATE OR REPLACE VIEW.如果要更新的視圖不存在,則第2條更新語句會創建一個視圖;如果要更新的視圖存在,則第2條更新語句會替換原有視圖.
22.2.1 利用視圖簡化復雜的聯結
視圖的最常見的應用之一是隱藏復雜的SQL,這通常都會涉及聯結.
視圖極大地簡化了復雜SQL語句的使用.利用視圖,可一次性編寫基礎的SQL,然后根據需要多次使用.
創建可重用的視圖 創建不受特定數據限制的視圖是一種好辦法.
例子:
返回已訂購了任意產品的所有客戶的列表
CREATE VIEW productcusomers AS
SELECT cust_name, cust_contact, prod_id
FROM customers, orders, orderitems
WHERE customers.cust_id=orders.cust_id
AND orderitems.order_num = orders.order_num;
檢索訂購了產品TNT2的客戶
SELECT cust_name, cust_contact
FROM productcustomers
WHERE prod_id='TNT2';
22.2.2 用視圖重新格式化檢索出的數據
視圖的另一常見用途是重新格式化檢索出的數據.
例子:
搜索結果
SELECT Concat(RTrim(vend_name), '(', RTrim(vend_country), ')')
AS vend_title
FROM vendors
ORDER BY vend_name;
創建視圖
CREATE VIEW vendorlocations AS
SELECT Concat(RTrim(vend_name), '(', RTrim(vend_country), ')')
AS vend_title
FROM vendors
ORDER BY vend_name;
從視圖中查詢數據
SELECT * FROM vendorlocations;
22.2.3 用視圖過濾不想要的數據
視圖對于應用普通的WHERE子句也很有用.
HERE子句與WHERE子句
如果從視圖檢索數據時使用了一條WHERE子句,則兩組子句(一組在視圖中,另一組是傳遞給視圖的)將自動組合.
例子:
創建視圖
CREATE VIEW customeremaillist AS
SELECT cust_id, cust_name, cust_email
FROM customers
WHERE cust_email IS NOT NULL;
從視圖中查詢數據
SELECT * FROM customeremaillist;
22.2.4 使用視圖與計算字段
視圖非常容易創建,而且很好使用.正確使用,視圖可極大地簡化復雜的數據處理.
例子:
檢索某個特定訂單中的物品,計算每種物品的總價格:
SELECT prod_id, quantity, item_price, quantity*item_price AS expanded_price
FROM orderitems WHERE order_num=20005;
創建視圖
create VIEW orderitemsexpanded AS
SELECT prod_id, quantity, item_price, quantity*item_price AS expanded_price
FROM orderitems;
從視圖中檢索數據
SELECT * FROM orderitemsexpanded WHERE order_num=20005;
22.2.5 更新視圖
迄今為止的所有視圖都是和SELECT語句使用的.然而,視圖的數據能否更新?答案視情況而定.
通常,視圖是可更新的(即,可以對它們使用INSERT、UPDATE和 DELETE).
更新一個視圖將更新其基表.如果你對視圖增加或刪除行,實際上是對其基表增加或刪除行.
但是,并非所有視圖都是可更新的.基本上可以說,如果MySQL不能正確地確定被更新的基數據,則不允許更新(包括插入和刪除).這實際上意味著,如果視圖定義中有以下操作,則不能進行視圖的更新:
- 分組(使用GROUP BY和HAVING);
- 聯結;
- 子查詢;
- 并;
- 聚集函數(Min()、Count()、Sum()等);
- DISTINCT;
- 導出(計算)列.
視圖主要用于數據檢索.
將視圖用于檢索
一般,應該將視圖用于檢索(SELECT語句)而不用于更新(INSERT、UPDATE和DELETE).
22.3 小結
第23章 使用存儲過程
23.1 存儲過程
迄今為止,使用的大多數SQL語句都是針對一個或多個表的單條語句.并非所有操作都這么簡單,經常會有一個完整的操作需要多條語句才能完成.
這時可以創建存儲過程.存儲過程簡單來說,就是為以后的使用而保存的一條或多條MySQL語句的集合.可將其視為批文件,雖然它們的作用不僅限于批處理.
23.2 為什么要使用存儲過程
既然我們知道了什么是存儲過程,那么為什么要使用它們呢?有許多理由,下面列出一些主要的理由.
- 通過把處理封裝在容易使用的單元中,簡化復雜的操作.
- 由于不要求反復建立一系列處理步驟,這保證了數據的完整性. 如果所有開發人員和應用程序都使用同一存儲過程,則所使用的代碼都是相同的.這一點的延伸就是防止錯誤.需要執行的步驟越多,出錯的可能性就越大.防止錯誤保證了數據的一致性.
- 簡化對變動的管理.如果表名、列名或業務邏輯(或別的內容)有變化,只需要更改存儲過程的代碼.使用它的人員甚至不需要知道這些變化.這一點的延伸就是安全性.通過存儲過程限制對基礎數據的訪問減少了數據訛誤(無意識的或別的原因所導致的數據訛誤)的機會.
- 提高性能.因為使用存儲過程比使用單獨的SQL語句要快.
- 存在一些只能用在單個請求中的MySQL元素和特性,存儲過程可 以使用它們來編寫功能更強更靈活的代碼
換句話說,使用存儲過程有3個主要的好處,即·簡單、安全、高性能.· 顯然,它們都很重要.
不過,在將SQL代碼轉換為存儲過程前,也必須知道它的一些缺陷.
- 一般來說,存儲過程的編寫比基本SQL語句復雜,編寫存儲過程需要更高的技能,更豐富的經驗.
- 你可能沒有創建存儲過程的安全訪問權限.許多數據庫管理員限制存儲過程的創建權限,允許用戶使用存儲過程,但不允許他們創建存儲過程.
盡管有這些缺陷,存儲過程還是非常有用的,并且應該盡可能地使用.
不能編寫存儲過程?
你依然可以使用 MySQL將編寫存儲過程的安全和訪問與執行存儲過程的安全和訪問區分開來.這是好事情.即使你不能(或不想)編寫自己的存儲過程,也仍然可以在適當的時候執行別的存儲過程.
23.3 使用存儲過程
23.3.1 執行存儲過程
MySQL稱存儲過程的執行為調用,因此MySQL執行存儲過程的語句為CALL.CALL接受存儲過程的名字以及需要傳遞給它的任意參數.
例子:
CALL productpricing(@pricelow, @pricehigh, @priceaverage);
解決辦法是臨時更改命令行實用程序的語句分隔符://
其中,DELIMITER //告訴命令行實用程序使用//作為新的語 句結束分隔符,可以看到標志存儲過程結束的END定義為END //而不是END;.這樣,存儲過程體內的;仍然保持不動,并且 正確地傳遞給數據庫引擎.最后,為恢復為原來的語句分隔符,可使用DELIMITER ;. 除\符號外,任何字符都可以用作語句分隔符.
23.3.2 創建存儲過程
例子:
創建存儲過程
CREATE PROCEDURE productpricing()
BEGIN
SELECT Avg(prod_price) AS priceaverage FROM products;
END;
調用存儲過程
CALL productpricing();
23.3.3 刪除存儲過程
存儲過程在創建之后,被保存在服務器上以供使用,直至被刪除.
刪除命令從服務器中刪除存儲過程.
例子:
DROP PROCEDURE productpricing;
這條語句刪除剛創建的存儲過程.請注意沒有使用后面的(),只給出存儲過程名.
僅當存在時刪除 如果指定的過程不存在,則DROPPROCEDURE將產生一個錯誤.當過程存在想刪除它時(如果過程不存在也不產生錯誤)可使用DROP PROCEDURE IF EXISTS.
23.3.4 使用參數
變量(variable)內存中一個特定的位置,用來臨時存儲數據.
變量名 所有MySQL變量都必須以@開始.
一般,存儲過程并不顯示結果,而是把結果返回給你指定的變量.
例子:
CREATE PRODURE productpricing(
OUT p1 DECIMAL(8,2),
OUT ph DECIMAL(8,2),
OUT pa DECIMAL(8,2)
)
BEGIN
SELECT MIN(prod_price) INTO p1 FROM products;
SELECT MAX(prod_price) INTO ph FROM products;
SELECT Avg(prod_price) INTO pa FROM pruductsl
END;
此存儲過程接受3個參數:pl存儲產品最低價格,ph存儲產品最高價格,pa存儲產品平均價格.每個參數必須具有指定的類型,這里使用十進制值.關鍵字OUT指出相應的參數用來從存儲過程傳出一個值(返回給調用者).MySQL支持IN(傳遞給存儲過程)、OUT(從存儲過程傳出,如這里所用)和INOUT(對存儲過程傳入和傳出)類型的參數.存儲過程的代碼位于BEGIN和END語句內,如前所見,它們是一系列SELECT語句,用來檢索值,然后保存到相應的變量(通過指定INTO關鍵字).
調用存儲過程
CALL productpricing(@pricelow, @pricehigh, @priceaverage);
在調用時,這條語句并不顯示任何數據.它返回以后可以顯示(或在其他處理中使用)的變量.
顯示檢索出的變量價格
SELECT @pricelow, @pricehigh, @priceaverage;
使用OUT和IN參數創建存儲過程
CREATE PROCEDURE ordertotal(
IN onumber INT,
OUT ototal DECIMAl(8,2)
)
BEGIN
SELECT Sum(item_price*quantity) FROM orderitems
WHERE order_num=onumber INTO ototal;
END;
調用存儲過程:
CALL ordertotal(2005, @total);
SELECT @total;
23.3.5 建立智能存儲過程
迄今為止使用的所有存儲過程基本上都是封裝MySQL簡單的SELECT 語句.雖然它們全都是有效的存儲過程例子,但它們所能完成的工作你 直接用這些被封裝的語句就能完成.只有在存儲過程內包含業務規則和智能處理時,它們的威力才真正顯現出來.
COMMENT關鍵字
本例子中的存儲過程在CREATEPROCEDURE語句中包含了一個COMMENT值.它不是必需的,但如果給出,將在SHOW PROCEDURE STATUS的結果中顯示.
IF語句
這個例子給出了MySQL的IF語句的基本用法.IF語 句還支持ELSEIF和ELSE子句
例子:
創建復雜的存儲過程
-- Name:ordertotal
-- Parameters:
-- onumber=order number
-- taxable=0 if not taxable, 1 if taxable
-- ototal=order total variable
CREATE PRODURE ordertotal(
IN onumber INT,
IN taxable BOOLEAM;
OUT ototal DECIMAL(8,2)
) COMMENT 'Obtain order total, optionally adding tax'
BEGIN
-- Declare variable for total
DECLARE total DECIMAL(8,2);
-- Declare tax percentage
DECLARE taxrate INT DEFAULT 6;
-- Get the order total
SELECT Sum(item_price*quantity)
FROM orderitems
WHERE order_num=onumber
INTO total;
-- Is this taxable?
IF taxable THEN
-- Yes, so add taxrate to the total
SELECT total+(total/100*taxrate) INTO total;
END IF;
-- And finally, save to out variable
SELECT total INTO ototal;
END;
23.3.6 檢查存儲過程
為顯示用來創建一個存儲過程的CREATE語句,使用SHOW CREATE PROCEDURE語句
為了獲得包括何時、由誰創建等詳細信息的存儲過程列表,使用SHOW PROCEDURE STATUS.
例子:
SHOW CREATE PRODURE ordertotal;
限制過程狀態結果
SHOWPROCEDURESTATUS列出所有存儲過程.為限制其輸出,可使用LIKE指定一個過濾模式
如:SHOW PRODURE STATUS LIKE 'ordertotal';
23.4 小結
第24章 使用游標
24.1 游標
游標(cursor)是一個存儲在MySQL服務器上的數據庫查詢,它不是一條SELECT語句,而是被該語句檢索出來的結果集.在存儲了游標之后,應用程序可以根據需要滾動或瀏覽其中的數據.
游標主要用于交互式應用,其中用戶需要滾動屏幕上的數據,并對數據進行瀏覽或做出更改.
只能用于存儲過程 不像多數DBMS,MySQL游標只能用于存儲過程(和函數).
24.2 使用游標
使用游標涉及幾個明確的步驟.
- 在能夠使用游標前,必須聲明(定義)它.這個過程實際上沒有 檢索數據,它只是定義要使用的SELECT語句.
- 一旦聲明后,必須打開游標以供使用.這個過程用前面定義的 SELECT語句把數據實際檢索出來.
- 對于填有數據的游標,根據需要取出(檢索)各行.
- 在結束游標使用時,必須關閉游標.
在聲明游標后,可根據需要頻繁地打開和關閉游標.在游標打開后, 可根據需要頻繁地執行取操作.
24.2.1 創建游標
游標用DECLARE語句創建.DECLARE命名游標,并定義 相應的SELECT語句,根據需要帶WHERE和其他子句.
在定義游標之后,可以打開它.
例子:
CREATE PRODURE processorders()
BEGIN
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
END;
24.2.2 打開和關閉游標
游標用OPEN CURSOR語句來打開
在處理OPEN語句時執行查詢,存儲檢索出的數據以供瀏覽和滾動.
游標處理完成后,應當使用如下語句關閉游標: CLOSE CURSOR
CLOSE釋放游標使用的所有內部內存和資源,因此在每個游標不再需要時都應該關閉.
在一個游標關閉后,如果沒有重新打開,則不能使用它.但是,使用聲明過的游標不需要再次聲明,用OPEN語句打開它就可以了
隱含關閉 如果你不明確關閉游標,MySQL將會在到達END語句時自動關閉它.
例子:
CREATE PRODURE processorders()
BEGIN
-- Declare the cursor
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
-- Open the cursor
OPEN ordernumbers;
-- Close the cursor
CLOSE ordernumbers;
END;
這個存儲過程聲明、打開和關閉一個游標.但對檢索出的數據什么也沒做.
24.2.3 使用游標數據
在一個游標被打開后,可以使用FETCH語句分別訪問它的每一行.FETCH指定檢索什么數據(所需的列),檢索出來的數據存儲在什么地方.它還向前移動游標中的內部行指針,使下一條FETCH語句檢索下一行(不重復讀取同一行).
例子:
從游標中檢索單個行
CREATE PRODURE processorders()
BEGIN
-- Declare local variables
DECLARE o INT;
-- Declare the cursor
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
-- Open the cursor
OPEN ordernumbers INTO o;
-- Close the cursor
CLOSE ordernumbers;
END;
循環檢索數據,從第一行到最后一行
CREATE PRODURE processorders()
BEGIN
-- Declare local variables
DECLARE done BOOLEAN DEFAULT 0;
DECLARE o INT;
-- Declare the cursor
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
-- Declare continue handler
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
-- Open the cursor
OPEN ordernumbers;
-- Loop through all rows
REPEAT
-- Get order number
FETCH ordernumbers INTO o;
-- End of loop
UNTIL done END REPEAT;
-- Close the cursor
CLOSE ordernumbers;
END;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
這條語句定義了一個CONTINUE HANDLER,它是在條件出現時被執行的代碼.這里,它指出當SQLSTATE '02000'出現時,SET done=1.SQLSTATE '02000'是一個未找到條件,當REPEAT由于沒有更多的行供循環而不能繼續時,出現這個條件.
重復或循環?
除這里使用的REPEAT語句外,MySQL還支持循環語句,它可用來重復執行代碼,直到使用LEAVE語句手動退出為止.通常REPEAT語句的語法使它更適合于對游標進行循環.
CREATE PRODURE processorders()
BEGIN
-- Declare local variables
DECLARE done BOOLEAN DEFAULT 0;
DECLARE o INT;
DECLARE t DECIMAL(8,2);
-- Declare the cursor
DECLARE ordernumbers CURSOR
FOR
SELECT CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
-- Create a table to store the results
CREATE TABLE IF NOT EXISTS ordertotals
(order_num INT, total DECIMAL(8,2));
-- Open the cursor
OPEN ordernumbers;
-- Loop through all rows
REPEAT
-- Get order number
FETCH ordernumbers INTO o;
-- Get the total for this order
CALL ordertotal(o, 1, t);
-- Insert order and total into ordertotals
INSERT INTO ordertotals(order_num, total) VALUES(o,t);
-- End of loop
UNTIL done END REPEAR;
-- Close the cursor
CLOSE ordernumbers;
END;
使用
SELECT * FROM ordertotals;
24.3 小結
第25章 使用觸發器
25.1 觸發器
觸發器是MySQL響應以下任意語句而自動執行的一條MySQL語句(或位于BEGIN和END語句之間的一組語句):
- DELETE;
- INSERT;
- UPDATE.
其他MySQL語句不支持觸發器.
25.2 創建觸發器
在創建觸發器時,需要給出4條信息:
- 唯一的觸發器名;
- 觸發器關聯的表;
- 觸發器應該響應的活動(DELETE、INSERT或UPDATE);
- 觸發器何時執行(處理之前或之后).
保持每個數據庫的觸發器名唯一
在MySQL5中,觸發器名必須在每個表中唯一,但不是在每個數據庫中唯一.這表示同一數據庫中的兩個表可具有相同名字的觸發器.這在其他每個數據庫觸發器名必須唯一的DBMS中是不允許的,而且以后的MySQL版本很可能會使命名規則更為嚴格.因此,現在最好是在數據庫范圍內使用唯一的觸發器名.
僅支持表 只有表才支持觸發器,視圖不支持(臨時表也不支持).
觸發器按每個表每個事件每次地定義,每個表每個事件每次只允許一個觸發器.因此,每個表最多支持6個觸發器(每條INSERT、UPDATE和DELETE的之前和之后).單一觸發器不能與多個事件或多個表關聯,所以,如果你需要一個對INSERT和UPDATE操作執行的觸發器,則應該定義兩個觸發器.
觸發器失敗
如果BEFORE觸發器失敗,則MySQL將不執行請求的操作.此外,如果BEFORE觸發器或語句本身失敗,MySQL將不執行AFTER觸發器(如果有的話).
例子:
CREATE TRIGGER newproduct AFTER ON products FOR EACH ROW SELECT 'Product added';
25.3 刪除觸發器
觸發器不能更新或覆蓋.為了修改一個觸發器,必須先刪除它, 然后再重新創建.
現在,刪除觸發器的語法應該很明顯了.為了刪除一個觸發器,可使用DROP TRIGGER語句
例子:DROP TRIGGER newproduct;
25.4 使用觸發器
INSERT觸發器在INSERT語句執行之前或之后執行.需要知道以下幾點:
- 在INSERT觸發器代碼內,可引用一個名為NEW的虛擬表,訪問被插入的行;
- 在BEFORE INSERT觸發器中,NEW中的值也可以被更新(允許更改被插入的值);
- 對于AUTO_INCREMENT列,NEW在INSERT執行之前包含0,在INSERT執行之后包含新的自動生成值.
BEFORE或AFTER?
通常,將BEFORE用于數據驗證和凈化(目的是保證插入表中的數據確實是需要的數據).本提示也適用于UPDATE觸發器.
例子:
創建一個名為neworder的觸發器,它按照AFTER INSERT ON orders執行.在插入一個新訂單到orders表時,MySQL生 成一個新訂單號并保存到order_num中.觸發器從NEW. order_num取得 這個值并返回它.此觸發器必須按照AFTER INSERT執行,因為在BEFORE INSERT語句執行之前,新order_num還沒有生成.對于orders的每次插 入使用這個觸發器將總是返回新的訂單號.
CREATE TROGGER neworder AFTER INSERT ON orders FOR EACH ROW SELECT new.order_num;
25.4.2 DELETE觸發器
DELETE觸發器在DELETE語句執行之前或之后執行.需要知道以下兩點:
- 在DELETE觸發器代碼內,你可以引用一個名為OLD的虛擬表,訪問被刪除的行;
- OLD中的值全都是只讀的,不能更新.
多語句觸發器
正如所見,觸發器deleteorder使用BEGIN和END語句標記觸發器體.這在此例子中并不是必需的,不過也沒有害處.使用BEGIN END塊的好處是觸發器能容納多條SQL語句(在BEGIN END塊中一條挨著一條).
例子:
使用OLD保存將要被刪除的行到一個存檔表中:
CREATE TRIGGER deleteorder BEFORE DELETE ON orders
FOR EACH ROW
BEGIN
INSERT INTO archive_orders(order_num, order_date, cust_id)
VALUES(OLD.order_num, OLD.order_date, OLD.cust_id)
END;
在任意訂單被刪除前將執行此觸發器.它使用一條INSERT語句將OLD中的值(要被刪除的訂單)保存到一個名為archive_ orders的存檔表中(為實際使用這個例子,你需要用與orders相同的列 創建一個名為archive_orders的表).
25.4.3 UPDATE觸發器
UPDATE觸發器在UPDATE語句執行之前或之后執行.需要知道以下幾點:
- 在UPDATE觸發器代碼中,你可以引用一個名為OLD的虛擬表訪問以前(UPDATE語句前)的值,引用一個名為NEW的虛擬表訪問新更新的值;
- 在BEFORE UPDATE觸發器中,NEW中的值可能也被更新(允許更改將要用于UPDATE語句中的值);
- OLD中的值全都是只讀的,不能更新.
例子:
CREATE TRIGGER updatevendor BEFORE UPDATE ON vendors
FOR EACH ROW SET NEW.vend_state=Upper(NEW.vend_state);
25.4.4 關于觸發器的進一步介紹
在結束本章之前,我們再介紹一些使用觸發器時需要記住的重點.
- 與其他DBMS相比,MySQL 5中支持的觸發器相當初級.未來的MySQL版本中有一些改進和增強觸發器支持的計劃.
- 創建觸發器可能需要特殊的安全訪問權限,但是,觸發器的執行是自動的.如果INSERT、UPDATE或DELETE語句能夠執行,則相關的觸發器也能執行.
- 應該用觸發器來保證數據的一致性(大小寫、格式等).在觸發器中執行這種類型的處理的優點是它總是進行這種處理,而且是透明地進行,與客戶機應用無關.
- 觸發器的一種非常有意義的使用是創建審計跟蹤.使用觸發器,把更改(如果需要,甚至還有之前和之后的狀態)記錄到另一個表非常容易.
- 遺憾的是,MySQL觸發器中不支持CALL語句.這表示不能從觸發器內調用存儲過程.所需的存儲過程代碼需要復制到觸發器內.
25.5 小結
第26章 管理事務處理
26.1 事務處理
事務處理(transaction processing)可以用來維護數據庫的完整性,它保證成批的MySQL操作要么完全執行,要么完全不執行.
事務處理是一種機制,用來管理必須成批執行的MySQL操作,以保證數據庫不包含不完整的操作結果.利用事務處理,可以保證一組操作不會中途停止,它們或者作為整體執行,或者完全不執行(除非明確指示).如果沒有錯誤發生,整組語句提交給(寫到)數據庫表.如果發生錯誤,則進行回退(撤銷)以恢復數據庫到某個已知且安全的狀態.
在使用事務和事務處理時,有幾個關鍵詞匯反復出現.下面是關于 事務處理需要知道的幾個術語:
- 事務(transaction)指一組SQL語句;
- 回退(rollback)指撤銷指定SQL語句的過程;
- 提交(commit)指將未存儲的SQL語句結果寫入數據庫表;
- 保留點(savepoint)指事務處理中設置的臨時占位符(place-holder),你可以對它發布回退(與回退整個事務處理不同).
26.2 控制事務處理
管理事務處理的關鍵在于將SQL語句組分解為邏輯塊,并明確規定數據何時應該回退,何時不應該回退.
MySQL使用下面的語句來標識事務的開始:START TRANSACTION
26.2.1 使用ROLLBACK
ROLLBACK命令用來回退(撤銷)MySQL語句
ROLLBACK只能在一個事務處理內使用(在執行一條START TRANSACTION命令之后).
哪些語句可以回退?
事務處理用來管理INSERT、UPDATE和 DELETE語句.你不能回退SELECT語句.
不能回退CREATE或DROP操作.事務處理塊中可以使用這兩條語句,但如果你執行回退,它們不會被撤銷.
例子:
SELECT * FROM ordertotals;
START TRANSACTION;
DELETE FROM ordertotals;
SELECT * FROM ordertotals;
ROLLBACK;
SELECT * FROM ordertotals;
首先執行一條SELECT以顯示該表不為空.
然后開始一 個事務處理,用一條DELETE語句刪除ordertotals中的所有行.
另一條SELECT語句驗證ordertotals確實為空.
這時用一條ROLLBACK語句回退START TRANSACTION之后的所有語句,
最后一條SELECT語句顯示該表不為空.
26.2.2 使用COMMIT
一般的MySQL語句都是直接針對數據庫表執行和編寫的.這就是所謂的隱含提交(implicit commit),即提交(寫或保存)操作是自動進行的.
但是,在事務處理塊中,提交不會隱含地進行.為進行明確的提交,使用COMMIT語句.
隱含事務關閉
當COMMIT或ROLLBACK語句執行后,事務會自動關閉(將來的更改會隱含提交).
例子:
START TRANCTION
DELETE FROM orderitems WHERE order_num=20010;
DELETE FROM orders WHERE order_num=20010;
COMMIT;
26.2.3 使用保留點
簡單的ROLLBACK和COMMIT語句就可以寫入或撤銷整個事務處理.但是,只是對簡單的事務處理才能這樣做,更復雜的事務處理可能需要部分提交或回退.
為了支持回退部分事務處理,必須能在事務處理塊中合適的位置放置占位符.這樣,如果需要回退,可以回退到某個占位符.
這些占位符稱為保留點.為了創建占位符,可如下使用SAVEPOINT 語句: SAVEPOINT delete1;
每個保留點都取標識它的唯一名字,以便在回退時,MySQL知道要 回退到何處.
為了回退到本例給出的保留點,可如下進行:ROLLBACK TO delete1;
保留點越多越好
可以在MySQL代碼中設置任意多的保留點,越多越好.為什么呢?因為保留點越多,你就越能按自己的意愿靈活地進行回退.
釋放保留點
保留點在事務處理完成(執行一條ROLLBACK或 COMMIT)后自動釋放.自MySQL 5以來,也可以用RELEASE SAVEPOINT明確地釋放保留點.
26.2.4 更改默認的提交行為
正如所述,默認的MySQL行為是自動提交所有更改.換句話說,任何時候你執行一條MySQL語句,該語句實際上都是針對表執行的,而且所做的更改立即生效.為指示MySQL不自動提交更改,需要使用以下語句:SET autocommit=0;
autocommit標志決定是否自動提交更改,不管有沒有COMMIT語句.
設置autocommit為0(假)指示MySQL不自動提交更改(直到autocommit被設置為真為止).
標志為連接專用 autocommit標志是針對每個連接而不是服務器的.
26.3 小結
第27章 全球化和本地化
27.1 字符集和校對順序
數據庫表被用來存儲和檢索數據.不同的語言和字符集需要以不同的方式存儲和檢索.
因此,MySQL需要適應不同的字符集(不同的字母 和字符),適應不同的排序和檢索數據的方法.
在討論多種語言和字符集時,將會遇到以下重要術語:
- 字符集為字母和符號的集合;
- 編碼為某個字符集成員的內部表示;
- 校對為規定字符如何比較的指令.
在MySQL的正常數據庫活動(SELECT、INSERT等)中,不需要操心太多的東西.使用何種字符集和校對的決定在服務器、數據庫和表級進行.
27.2 使用字符集和校對順序
MySQL支持眾多的字符集.
為查看所支持的字符集完整列表,使用以下語句:SHOW CHARACTER SET;
這條語句顯示所有可用的字符集以及每個字符集的描述和默認校對.
為了查看所支持校對的完整列表,使用以下語句:SHOW COLLATION;
此語句顯示所有可用的校對,以及它們適用的字符集.可以看到有的字符集具有不止一種校對.
通常系統管理在安裝時定義一個默認的字符集和校對.此外,也可以在創建數據庫時,指定默認的字符集和校對.
為了確定所用的字符集和校對,可以使用以下語句:
SHOW VARIABLES LIKE 'character%';
SHOW VARIAVLES LIKE 'collation%';
實際上,字符集很少是服務器范圍(甚至數據庫范圍)的設置.不同的表,甚至不同的列都可能需要不同的字符集,而且兩者都 可以在創建表時指定.
一般,MySQL如下確定使用什么樣的字符集和校對.
- 如果指定CHARACTER SET和COLLATE兩者,則使用這些值.
- 如果只指定CHARACTER SET,則使用此字符集及其默認的校對(如SHOW CHARACTER SET的結果中所示).
- 如果既不指定CHARACTER SET,也不指定COLLATE,則使用數據庫默認.
除了能指定字符集和校對的表范圍外,MySQL還允許對每個列設置.
臨時區分大小寫
上面的SELECT語句演示了在通常不區分大小寫的表上進行區分大小寫搜索的一種技術.當然,反過來也是可以的.
SELECT的其他COLLATE子句
除了這里看到的在ORDERBY子句中使用以外,COLLATE還可以用于GROUP BY、HAVING、聚集函數、別名等.
最后,值得注意的是,如果絕對需要,串可以在字符集之間進行轉 換.為此,使用Cast()或Convert()函數.
例子:
為了給表指定字符集和校對
CREATE TABLE mytable(
column1 INT,
column2 VARCHAR(10)
)DEFAULT CHARACTER SET hebrew
COLLATE hebrew_general_ci;
此語句創建一個包含兩列的表,并且指定一個字符集和一個校 對順序.
允許對每個列設置
CREATE TABLE mytable(
column1 INT,
column2 VARCHAR(10),
column3 VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_general_ci
)DEFAULT CHARACTER SET hebrew
COLLATE hebrew_general_ci;
對整個表以及一個特定的列指定了CHARACTER SET和COLLATE.
查詢字符集
SELECT * FROM customers ORDER BY lastname, firstname COLLATE latin1_general_ci;
27.3 小結
第28章 安全管理
28.1 訪問控制
MySQL服務器的安全基礎是:用戶應該對他們需要的數據具有適當的訪問權,既不能多也不能少.
換句話說,用戶不能對過多的數據具有過多的訪問權.
防止無意的錯誤
重要的是注意到,訪問控制的目的不僅僅是防止用戶的惡意企圖.數據夢魘更為常見的是無意識錯誤的結果,如錯打MySQL語句,在不合適的數據庫中操作或其他一些用戶錯誤.通過保證用戶不能執行他們不應該執行的語句,訪問控制有助于避免這些情況的發生.
不要使用root
應該嚴肅對待root登錄的使用.僅在絕對需要時使用它(或許在你不能登錄其他管理賬號時使用).不應該在日常的MySQL操作中使用root.
28.2 管理用戶
MySQL用戶賬號和信息存儲在名為mysql的MySQL數據庫中.一般不需要直接訪問mysql數據庫和表,但有時需要直接訪問.需要直接訪問它的時機之一是在需要獲得所有用戶賬號列表時.
用多個客戶機進行試驗 試驗對用戶賬號和權限進行更改的最好辦法是打開多個數據庫客戶機(如mysql命令行實用程序的多個副本),一個作為管理登錄,其他作為被測試的用戶登錄.
例如:select user from user;
28.2.1 創建用戶賬號
為了創建一個新用戶賬號,使用CREATE USER語句
指定散列口令
IDENTIFIEDBY指定的口令為純文本,MySQL將在保存到user表之前對其進行加密.為了作為散列值指定口令,使用IDENTIFIED BY PASSWORD.
使用GRANT或INSERT
GRANT語句(稍后介紹)也可以創建用戶賬號,但一般來說CREATE USER是最清楚和最簡單的句子.此外,也可以通過直接插入行到user表來增加用戶,不過為安全起見,一般不建議這樣做.MySQL用來存儲用戶賬號信息的表(以及表模式等)極為重要,對它們的任何毀壞都可能嚴重地傷害到MySQL服務器.因此,相對于直接處理來說,最好是用標記和函數來處理這些表.
為重新命名一個用戶賬號,使用RENAME USER語句
MySQL5之前
僅MySQL5或之后的版本支持RENAME USER.為了在以前的MySQL中重命名一個用戶,可使用UPDATE直接更新user表.
例子:
CREATE USER ben IDENTIFY BY 'p@wOrd'給出了 一個口令.如果你再次列出用戶賬號,將會在輸出中看到新賬號.
RENAME USER ben TO bforta;
28.2.2 刪除用戶賬號
為了刪除一個用戶賬號(以及相關的權限),使用DROP USER語句
MySQL5之前
自MySQL5以來,DROPUSER刪除用戶賬號和所有相關的賬號權限.在MySQL 5以前,DROP USER只能用來刪除用戶賬號,不能刪除相關的權限.因此,如果使用舊版本的MySQL,需要先用REVOKE刪除與賬號相關的權限,然后再用DROP USER刪除賬號.
例子:
DROP USER bforta;
28.2.3 設置訪問權限
在創建用戶賬號后,必須接著分配訪問權限.新創建的用戶賬號沒有訪問權限.它們能登錄MySQL,但不能看到數據,不能執行任何數據庫操作.
為看到賦予用戶賬號的權限,使用SHOW GRANTS FOR username;
用戶定義為user@host
MySQL的權限用用戶名和主機名結合定義.如果不指定主機名,則使用默認的主機名%(授予用戶訪問權限而不管主機名).
為設置權限,使用GRANT語句.
GRANT要求你至少給出以下信息:
- 要授予的權限;
- 被授予訪問權限的數據庫或表;
- 用戶名.
每個GRANT添加(或更新)用戶的一個權限.MySQL讀取所有授權,并根據它們確定權限.
GRANT的反操作為REVOKE,用它來撤銷特定的權限.
被撤銷的訪問權限必須存在,否則會出錯.
GRANT和REVOKE可在幾個層次上控制訪問權限:
- 整個服務器,使用GRANT ALL和REVOKE ALL;
- 整個數據庫,使用ON database.*;
- 特定的表,使用ON database.table;
- 特定的列;
- 特定的存儲過程.
未來的授權
在使用GRANT和REVOKE時,用戶賬號必須存在,但對所涉及的對象沒有這個要求.這允許管理員在創建數據庫和表之前設計和實現安全措施.
這樣做的副作用是,當某個數據庫或表被刪除時(用DROP語句),相關的訪問權限仍然存在.而且,如果將來重新創建該數據庫或表,這些權限仍然起作用.
簡化多次授權
可通過列出各權限并用逗號分隔,將多條 GRANT語句串在一起,如下所示:
GRANT SELECT, INSERT ON crashcourse.* TO bforta;
權限
---|---
ALL|除GRANT OPTION外的所有權限
ALTER|使用ALTER TABLE
ALTER ROUTINE|使用ALTER PROCEDURE和DROP PROCEDURE
CREATE|使用CREATE TABLE
CREATE ROUTINE|使用CREATE PROCEDURE
CREATE TEMPORARY TABLE|使用CREATE TEMPORARY TABLE
CREATE USER|使用CREATE USER、DROP USER、RENAME USER和REVOKE ALL PRIVILEGES
CREATE VIEW|使用CREATE VIEW
DELETE|使用DELETE
DROP|使用DROP TABLE
EXECUTE|使用CALL和存儲過程
FILE|使用SELECT INTO OUTFILE和LOAD DATA INFILE
GRANT OPTION|使用GRANT和REVOKE
INDEX|使用CREATE INDEX和DROP INDEX
INSERT使用INSERT
LOCK TABLES|使用LOCK TABLES
PROCESS|使用SHOW FULL PROCESSLIST
RELOAD|使用FLUSH
REPLICATION CLIENT|服務器位置的訪問
REPLICATION SLAVE|由復制從屬使用
SELECT|使用SELECT
SHOW DATABASES|使用SHOW DATABASES
SHOW VIEW|使用SHOW CREATE VIEW
SHUTDOWN|使用mysqladmin shutdown(用來關閉MySQL)
SUPER|使用CHANGE MASTER、KILL、LOGS、PURGE、MASTER 和SET GLOBAL.還允許mysqladmin調試登錄
UPDATE|使用UPDATE
USAGE|無訪問權限
例子:
SHOW GRANTS FOR bforta;
GRANT SELECT ON crashcourse.* TO bforta;
允許用戶在crashcourse.*(crashcourse數據庫的所有表)上使用SELECT.
查看一下權限
SHOW GRANTS FOR bforta;
REVOKE SELECT ON crashcourse.* TO bforta;
REVOKE語句取消剛賦予用戶bforta的SELECT訪問權限.
28.2.4 更改口令
為了更改用戶口令,可使用SET PASSWORD語句.
新口令必須如下加密:SET PASSWORD FOR bforta=Password('123456');
SET PASSWORD更新用戶口令.新口令必須傳遞到Password()函數進行加密.
設置自己的口令:SET PASSWORD=Password('123456')
在不指定用戶名時,SET PASSWORD更新當前登錄用戶的口令.
28.3 小結
第29章 數據庫維護
29.1 備份數據
像所有數據一樣,MySQL的數據也必須經常備份.由于MySQL數據庫是基于磁盤的文件,普通的備份系統和例程就能備份MySQL的數據.但是,由于這些文件總是處于打開和使用狀態,普通的文件副本備份不一定總是有效.
下面列出這個問題的可能解決方案.
- 使用命令行實用程序mysqldump轉儲所有數據庫內容到某個外部文件.在進行常規備份前這個實用程序應該正常運行,以便能正確地備份轉儲文件.
- 可用命令行實用程序mysqlhotcopy從一個數據庫復制所有數據(并非所有數據庫引擎都支持這個實用程序).
- 可以使用MySQL的BACKUP TABLE或SELECT INTO OUTFILE轉儲所有數據到某個外部文件.這兩條語句都接受將要創建的系統文件名,此系統文件必須不存在,否則會出錯.數據可以用RESTORE TABLE來復原.
首先刷新未寫數據
為了保證所有數據被寫到磁盤(包括索引數據),可能需要在進行備份前使用FLUSH TABLES語句.
29.2 進行數據庫維護
MySQL提供了一系列的語句,可以(應該)用來保證數據庫正確和正常運行.
以下是你應該知道的一些語句.
- ANALYZE TABLE,用來檢查表鍵是否正確.
- CHECK TABLE用來針對許多問題對表進行檢查.在MyISAM表上還對索引進行檢查.CHECK TABLE支持一系列的用于MyISAM表的方式. CHANGED檢查自最后一次檢查以來改動過的表.EXTENDED執行最徹底的檢查,FAST只檢查未正常關閉的表,MEDIUM檢查所有被刪除的鏈接并進行鍵檢驗,QUICK只進行快速掃描.
- 如果MyISAM表訪問產生不正確和不一致的結果,可能需要用REPAIR TABLE來修復相應的表.這條語句不應該經常使用,如果需要經常使用,可能會有更大的問題要解決.
- 如果從一個表中刪除大量數據,應該使用OPTIMIZE TABLE來收回所用的空間,從而優化表的性能.
例子:
analyze table orders;
check table orders, oderitems;
repair table orders;
optimize table orders;
29.3 診斷啟動問題
服務器啟動問題通常在對MySQL配置或服務器本身進行更改時出 現.MySQL在這個問題發生時報告錯誤,但由于多數MySQL服務器是作為系統進程或服務自動啟動的,這些消息可能看不到.
在排除系統啟動問題時,首先應該盡量用手動啟動服務器.MySQL服務器自身通過在命令行上執行mysqld啟動.
下面是幾個重要的mysqld 命令行選項:
- --help顯示幫助——一個選項列表;
- --safe-mode裝載減去某些最佳配置的服務器;
- --verbose顯示全文本消息(為獲得更詳細的幫助消息與--help聯合使用);
- --version顯示版本信息然后退出.
幾個另外的命令行選項(與日志文件的使用有關)在下一節列出.
29.4 查看日志文件
MySQL維護管理員依賴的一系列日志文件.主要的日志文件有以下幾種.
- 錯誤日志.它包含啟動和關閉問題以及任意關鍵錯誤的細節.此日志通常名為hostname.err,位于data目錄中.此日志名可用--log-error命令行選項更改.
- 查詢日志.它記錄所有MySQL活動,在診斷問題時非常有用.此日志文件可能會很快地變得非常大,因此不應該長期使用它.此日志通常名為hostname.log,位于data目錄中.此名字可以用--log命令行選項更改.
- 二進制日志.它記錄更新過數據(或者可能更新過數據)的所有語句.此日志通常名為hostname-bin,位于data目錄內.此名字可以用--log-bin命令行選項更改.注意,這個日志文件是MySQL 5中添加的,以前的MySQL版本中使用的是更新日志.
- 緩慢查詢日志.顧名思義,此日志記錄執行緩慢的任何查詢.這個日志在確定數據庫何處需要優化很有用.此日志通常名為 hostname-slow.log,位于data目錄中.此名字可以用--log-slow-queries命令行選項更改.
在使用日志時,可用FLUSH LOGS語句來刷新和重新開始所有日志文件.
29.5 小結
第30章 改善性能
30.1 改善性能
數據庫管理員把他們生命中的相當一部份時間花在了調整、試驗以改善DBMS性能之上.在診斷應用的滯緩現象和性能問題時,性能不良的數據庫(以及數據庫查詢)通常是最常見的禍因.
可以看出,下面的內容并不能完全決定MySQL的性能.我們只是想回顧一下前面各章的重點,提供進行性能優化探討和分析的一個出發點.
- 首先,MySQL(與所有DBMS一樣)具有特定的硬件建議.在學習和研究MySQL時,使用任何舊的計算機作為服務器都可以.但對用于生產的服務器來說,應該堅持遵循這些硬件建議.
- 一般來說,關鍵的生產DBMS應該運行在自己的專用服務器上.
- MySQL是用一系列的默認設置預先配置的,從這些設置開始通常是很好的.但過一段時間后你可能需要調整內存分配、緩沖區大小等.(為查看當前設置,可使用SHOW VARIABLES;和SHOW STATUS;.)
- MySQL一個多用戶多線程的DBMS,換言之,它經常同時執行多個任務.如果這些任務中的某一個執行緩慢,則所有請求都會執行緩慢.如果你遇到顯著的性能不良,可使用SHOW PROCESSLIST顯示所有活動進程(以及它們的線程ID和執行時間).你還可以用KILL命令終結某個特定的進程(使用這個命令需要作為管理員登錄).
- 總是有不止一種方法編寫同一條SELECT語句.應該試驗聯結、并、子查詢等,找出最佳的方法.
- 使用EXPLAIN語句讓MySQL解釋它將如何執行一條SELECT語句.
- 一般來說,存儲過程執行得比一條一條地執行其中的各條MySQL語句快.
- 應該總是使用正確的數據類型.
- 決不要檢索比需求還要多的數據.換言之,不要用SELECT *(除非你真正需要每個列).
- 有的操作(包括INSERT)支持一個可選的DELAYED關鍵字,如果使用它,將把控制立即返回給調用程序,并且一旦有可能就實際執行該操作.
- 在導入數據時,應該關閉自動提交.你可能還想刪除索引(包括FULLTEXT索引),然后在導入完成后再重建它們.
- 必須索引數據庫表以改善數據檢索的性能.確定索引什么不是一件微不足道的任務,需要分析使用的SELECT語句以找出重復的WHERE和ORDER BY子句.如果一個簡單的WHERE子句返回結果所花的時間太長,則可以斷定其中使用的列(或幾個列)就是需要索引的對象.
- 你的SELECT語句中有一系列復雜的OR條件嗎?通過使用多條SELECT語句和連接它們的UNION語句,你能看到極大的性能改進.
- 索引改善數據檢索的性能,但損害數據插入、刪除和更新的性能.如果你有一些表,它們收集數據且不經常被搜索,則在有必要之前不要索引它們.(索引可根據需要添加和刪除.)
- LIKE很慢.一般來說,最好是使用FULLTEXT而不是LIKE.
- 數據庫是不斷變化的實體.一組優化良好的表一會兒后可能就面目全非了.由于表的使用和內容的更改,理想的優化和配置也會改變.
- 最重要的規則就是,每條規則在某些條件下都會被打破.
30.2 小結
MySQL語句的語法
C.1 ALTERTABLE:ALTER TABLE用來更新已存在表的模式.
C.2 COMMIT:COMMIT用來將事務處理寫到數據庫.
C.3 CREATEINDEX:CREATE INDEX用于在一個或多個列上創建索引.
C.4 CREATEPROCEDURE:CREATE PROCEDURE用于創建存儲過程.
C.5 CREATETABLE:CREATE TABLE用于創建新數據庫表.
C.6 CREATEUSER:CREATE USER 用于向系統中添加新的用戶賬戶.
C.7 CREATEVIEW:CREATE VIEW用來創建一個或多個表上的新視圖.
C.8 DELETE:DELETE從表中刪除一行或多行.
C.9 DROP:DROP永久地刪除數據庫對象(表、視圖、索引等).
C.10 INSERT:INSERT給表增加一行.
C.11 INSERTSELECT:INSERT SELECT插入SELECT的結果到一個表.
C.12 ROLLBACK:ROLLBACK用于撤銷一個事務處理塊.
C.13 SAVEPOINT:SAVEPOINT為使用ROLLBACK語句設立保留點.
C.14 SELECT:SELECT用于從一個或多個表(視圖)中檢索數據.
C.15 STARTTRANSACTION:START TRANSACTION表示一個新的事務處理塊的開始.
C.16 UPDATE:UPDATE更新表中一行或多行.
MySQL數據類型
數據類型是定義列中可以存儲什么數據以及該數據 實際怎樣存儲的基本規則.
數據類型用于以下目的.
- 數據類型允許限制可存儲在列中的數據.例如,數值數據類型列只能接受數值.
- 數據類型允許在內部更有效地存儲數據.可以用一種比文本串更簡潔的格式存儲數值和日期時間值.
- 數據類型允許變換排序順序.如果所有數據都作為串處理,則1位于10之前,而10又位于2之前(串以字典順序排序,從左邊開始比較,一次一個字符).作為數值數據類型,數值才能正確排序.
在設計表時,應該特別重視所用的數據類型.使用錯誤的數據類型 可能會嚴重地影響應用程序的功能和性能.更改包含數據的列不是一件 小事(而且這樣做可能會導致數據丟失).
D.1 串數據類型
最常用的數據類型是串數據類型.它們存儲串,如名字、地址、電 話號碼、郵政編碼等.有兩種基本的串類型,分別為定長串和變長串.
定長串接受長度固定的字符串,其長度是在創建表時指定的.CHAR屬于定長串類型.
變長串存儲可變長度的文本.有些變長數據類型具有最大的定長,而有些則是完全變長的.不管是哪種,只有指定的數據得到保存(額外的數據不保存)TEXT屬于變長串類型.
既然變長數據類型這樣靈活,為什么還要使用定長數據類型?回答是因為性能.
MySQL處理定長列遠比處理變長列快得多.此外,MySQL不允許對變長列(或一個列的可變部分)進行索引.這也會極大地影響性能.
串數據類型
數據類型 | 說明 |
---|---|
CHAR | 1~255個字符的定長串.它的長度必須在創建時指定,否則MySQL假定為CHAR(1) |
ENUM | 接受最多64K個串組成的一個預定義集合的某個串 |
LONGTEXT | 與TEXT相同,但最大長度為4GB |
MEDIUMTEXT | 與TEXT相同,但最大長度為16K |
SET | 接受最多64個串組成的一個預定義集合的零個或多個串 |
TEXT | 最大長度為64K的變長文本 |
TINYTEXT | 與TEXT相同,但最大長度為255字節 |
VARCHAR | 長度可變,最多不超過255字節.如果在創建時指定為VARCHAR(n),則可存儲0到n個字符的變長串(其中n≤255) |
使用引號 不管使用何種形式的串數據類型,串值都必須括在 引號內(通常單引號更好).
當數值不是數值時
你可能會認為電話號碼和郵政編碼應該存儲在數值字段中(數值字段只存儲數值數據),但是,這樣做卻是不可取的.如果在數值字段中存儲郵政編碼01234,則保存的將是數值1234,實際上丟失了一位數字. 需要遵守的基本規則是:如果數值是計算(求和、平均等)中使用的數值,則應該存儲在數值數據類型列中.如果作為字符串(可能只包含數字)使用,則應該保存在串數據類型列中.
D.2 數值數據類型
數值數據類型存儲數值.MySQL支持多種數值數據類型,每種存儲的數值具有不同的取值范圍.顯然,支持的取值范圍越大,所需存儲空間越多.此外,有的數值數據類型支持使用十進制小數點(和小數),而有的則只支持整數.
有符號或無符號
所有數值數據類型(除BIT和BOOLEAN外)都可以有符號或無符號.有符號數值列可以存儲正或負的數值,無符號數值列只能存儲正數.默認情況為有符號,但如果你知道自己不需要存儲負值,可以使用UNSIGNED關鍵字,這樣做將允許你存儲兩倍大小的值.
數值數據類型
數據類型 | 說明 |
---|---|
BIT | 位字段,1~64位.(在MySQL 5之前,BIT在功能上等價于TINYINT |
BIGINT | 整數值,支持?92233720368547758089223372036854775807(如果是UNSIGNED,為018446744073709551615)的數 |
BOOLEAN(或BOOL) | 布爾標志,或者為0或者為1,主要用于開/關(on/off)標志 |
DECIMAL(或DEC) | 精度可變的浮點值 |
DOUBLE | 雙精度浮點值 |
FLOAT | 單精度浮點值 |
INT(或INTEGER) | 整數值,支持?2147483648~2147483647(如果是UNSIGNED, 為0~4294967295)的數 |
MEDIUMINT | 整數值,支持-83886088388607(如果是UNSIGNED,為016777215)的數 |
REAL | 4字節的浮點值 |
SMALLINT | 整數值,支持-3276832767(如果是UNSIGNED,為065535)的數 |
TINYINT | 整數值,支持-128127(如果為UNSIGNED,為0255)的數 |
不使用引號 與串不一樣,數值不應該括在引號內.
存儲貨幣數據類型 MySQL中沒有專門存儲貨幣的數據類型,一般情況下使用DECIMAL(8, 2)
D.3 日期和時間數據類型
MySQL使用專門的數據類型來存儲日期和時間值.
日期和時間數據類型
數據類型 | 說明 |
---|---|
DATE | 表示1000-01-01~9999-12-31的日期,格式為 YYYY-MM-DD |
DATETIME | DATE和TIME的組合 |
TIMESTAMP | 功能和DATETIME相同(但范圍較小) |
TIME | 格式為HH:MM:SS |
YEAR | 用2位數字表示,范圍是70(1970年)69(2069年),用4位數字表示,范圍是1901年2155年 |
D.4 二進制數據類型
二進制數據類型可存儲任何數據(甚至包括二進制信息),如圖像、多媒體、字處理文檔等.
二進制數據類型
數據類型 | 說明 |
---|---|
BLOB | Blob最大長度為64KB |
MEDIUMBLOB | Blob最大長度為16MB |
LONGBLOB | Blob最大長度為4GB |
TINYBLOB | Blob最大長度為255字節 |