1. 程式人生 > 資料庫 >《MySQL必知必會》19~23章

《MySQL必知必會》19~23章

第十九~第二十三章

第十九章

INSERT 是用來插入(或新增)行到資料庫表的,有以下幾種方式

· 插入完整的行

· 插入行的一部分

· 插入多行

· 插入某些查詢的結果

可針對每個表或每個使用者利用MySQL的安全機制禁止使用INSERT語句

插入完整的行,要求表名和插入新行的值

INSERT INTO customers
VALUES(NULL,'Pep E. LaPew','100 Main Street','Los Angeles','CA','90046','USA',NULL,NULL);

這種格式下,每個列必須提供一個值,次序也應該與定義列的次序相同,沒有值則填NULL(假定該列允許為空值),若某列由MySQL自動增量,也指定為NULL,但這種語法不安全(表的結構以後可能會變動),應儘量避免使用

更安全的方法

INSERT INTO customers(cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country,cust_contact,cust_email)
VALUES('Pep E. LaPew',NULL);

VALUES中的值以之前指定的次序(不需要按照原有表的次序)填寫,這種方法即使表的結構改變也能正確工作,因為cust_id的值是不必要的,所以可以不用出現

若滿足以下條件之一在INSERT語句中可以省略

· 該列定義為允許NULL值(無值或者空值)

· 在表定義中給出預設值,表示如果不給出值,將使用預設值

若資料檢索更重要,可以在INSERT和INTO之間新增關鍵字LOW_PRIORITY降低INSERT的優先順序

插入多行可以使用多條INSERT語句多次插入,或者,只要每條INSERT語句中的列名和次序相同,可以如下形式

INSERT INTO customers(cust_name,cust_country)
VALUES(
	'Pep E. LaPew','90046'
    'USA'
),(
	'M,Martian','42 Galaxy Way','New York','NY','11213','USA'
);

每組值用一對圓括號括起來,用逗號分隔,該方法比使用多條INSERT語句更快

插入檢索出的資料,由一條INSERT語句和SELECT語句組成,比如我們想從一個表中合併客戶列表到customers表中,不需要每次讀取一行再插入(假設有一個名為custnew的表,結構與customers相同,要注意custnew表中的cust_id不能與customers中的重複或者省略這列值在匯入到另一個表時有MySQL生成)

INSERT INTO customers(cust_id,cust_email,cust_name,cust_country)
SELECT cust_id,cust_country
FROM custnew;

若這個表為空,則不插入,這是合法的,也可以在INSERT和SELECT省略cust_id這一列,有MySQL生成值

INSERT和SELECT中的列名不一定要相同,MySQL使用的是列的位置,SELECT中的第一列將用來填充INSERT指定的第一列……這對於從使用不同列名的表匯入資料是非常有用的

在INSERT SELECT中SELECT語句可包含WHERE子句來過濾插入的資料

第二十章

更新(修改)表中的資料可以使用UPDATE,可以更新特定的行或者更新所有行

UPDATE主要由更新的表、列名和新的值、確定更新行的過濾條件三部分組成,注意WHERE子句的重要性

如客戶10005現在有了電子郵件地址,需要更新

UPDATE customers
SET cust_email = '[email protected]'
WHERE cust_id = 10005;

若沒有WHERE子句,將會更新所有行

更新更多的列

UPDATE customers
SET cust_name = 'The Fudds',cust_email = '[email protected]'
WHERE cust_id = 10005;

在UPDATE中也可以使用子查詢

在更新多行時,為避免一行或多行出現錯誤而取消整個操作(取消後之前更新的行恢復到原有值)可以寫作UPDATE IGNORE customers…

為刪除某個列的值,可以將其設為NULL(前提是允許為NULL)

UPDATE customers
SET cust_email = NULL
WHERE cust_id = 10005;

用DELETE刪除特定行或者所有行,同樣不要省略WHERE子句

DELETE FROM customers
WHERE cust_id = 10006;

若不加WHERE子句,將刪除表中每個客戶

DELETE刪除整行而不是列,刪除列使用UPDATE,但DELETE不刪除表本身

刪除所有行用TRUNCATE TABLE語句,速度更快(實際是刪除原來的表並重新生成一個表,而不是逐行刪除)

更新和刪除的原則

· 除非確實打算修改每行,否則必須使用WHERE子句

· 保證每個表都有主鍵,儘可能像WHERE子句那樣使用

· 可以先使用SELECT來測試使用的WHERE是否過濾正確的行,保證正確性

· 使用強制實施引用完整性的資料庫,這樣MySQL將不允許刪除具有與其他表相關聯資料的行

· MySQL沒有撤銷按鈕,小心使用UPDATE和DELETE

第二十一章

可以用CREATE TABLE來建立表,事實上,使用互動式工具時,介面工具生成相應的MySQL語句並執行

建立一個新表需要給出表的名字,表列的名字和定義,或者可以給出更多資訊

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,custy_email		char(255)	NULL,PRIMARY KEY (cust_id)
)	ENGINE=InnoDB;

縮排的大小和各列之間的空格沒有規定

表名緊跟在CREATE TABLE後,表的定義在圓括號之中,每列的定義以列名開始,後跟列的資料型別,表的主鍵可以在建立表時指定

在建立新表時,指定的表名必須不存在,若要防止意外覆蓋已有的表,先手工刪除該表,可以在表名後給出IF NOT EXISIT,檢查表名是否存在並在表名不存在時建立

如果指定了NOT NULL,則在插入時必須有值,NULL為預設設定,如果不指定NOT NULL,則認為指定的是NULL

要注意NULL不是空串,NULL值是沒有值,如果指定’’(兩個單引號,中間沒有字元),這在NOT NULL列中是允許的

如果主鍵使用多個列,則這些列的組合值必須唯一,在建立表時用逗號分隔各列

CREATE TABLE orderitems
(
	order_num		int			NOT NULL,order_item		int			NOT NULL,prod_id			char(10)	NOT NULL,quantity		int			NOT NULL,item_price		decimal(8,2)NOT NULL,PRIMARY KEY(order_num,order_item)
)	ENGINE=InnoDB;

主鍵也可以在建立表之後定義,只能使用不允許NULL的列

AUTO_INCREMENT告訴MySQL,本列每增加一行時自動增量,這樣給每個行分配一個唯一的cust_id,每個表只允許一個AUTO_INCREMENT列,而且它必須被索引(如:通過使它成為主鍵)

覆蓋AUTO_INCREMENT,可以簡單地在INSERT語句中指定一個值,只要之前沒有出現過即可,該值將代替自動生成的值,後續的增量將開始使用該手工插入的值

SELECT last_insert_id()

該語句返回最後一個AUTO_INCREMENT值

如果插入行時沒有給出值,MySQL允許指定此時使用的預設值

CREATE TABLE orderitems
(
	order_num	int			NOT NULL,order_item	char(10)	NOT NULL,prod_id		char(10)	NOT NULL,quantity	int			NOT NULL DEFAULT 1,item_price	decimal(8,PRIMARY KEY (order_num,order_item)
)	ENGINE=InnoDB; 

在不給出數量的情況下使用數量1,但不允許使用函式作為預設值,只支援常量

MySQL有一個具體管理和處理資料的內部引擎 ,在使用CREATE TABLE或者SELECT等語句時,由該引擎來處理請求,MySQL有多種引擎,具有不同的功能和特性,如果忽略ENGINE=語句,則使用預設引擎(很可能是MyISAM)

· InnoDB是一個可靠的事務處理引擎,不支援全文字搜尋

· MEMORY在功能上等同於MyISAM,但資料儲存在記憶體上(不是磁碟)中,速度很快,適合於臨時表

· MyISAM效能極高,支援全文字搜尋,但不支援事務處理

引擎型別可以混用,如一個表用MyISAM,另一個用InnoDB,但有一個缺陷,外來鍵不能跨引擎,即用一個引擎的表不能引用使用不同引擎的表的外來鍵

可使用ALTER TABLE更新表

給vendors表增加一個名為vend_phone的列,必須明確其資料型別

ALTER TABLE vendors
ADD vend_phone CHAR(20);

刪除剛剛新增的列

ALTER TABLE vendors
DROP COLUMN vend_phone;

還可以定義外來鍵

ALTER TABLE orderitems
ADD CONSTRAINT fk_orderitems_orders
FOREIGN KEY (order_num) 
REFERENCES orders (order_num);

ALTER TABLE orderitems
ADD CONSTRAINT fk_orderitems_products
FOREIGN KEY (prod_id) 
REFERENCES products (prod_id);

ALTER TABLE orders
ADD CONSTRAINT fk_orders_customers
FOREIGN KEY (cust_id) 
REFERENCES customers (cust_id);

ALTER TABLE products
ADD CONSTRAINT fk_products_vendors
FOREIGN KEY (vend_id) 
REFERENCES vendors (vend_id);

如果是對單個表進行多個更改,可以使用單條ALTER TABLE語句,每個更改用逗號分隔

複雜的表結構更改一般需要手動刪除過程,應該在使用ALTER TABLE改動表前做好備份,資料庫表的更改不能撤銷

刪除表(整個表而不是其內容),不能撤銷,永久刪除

DROP TABLE customers2;

重命名錶

RENAME TABLE customers2 TO customers;

對多個表重新命名

RENAME TABLE backup_customers TO customers,backup_vendors TO vendors,backup_products TO products;

第二十二章

檢視是虛擬的表,不同的是檢視只包含使用時動態檢索資料的查詢

SELECT cust_name,cust_contact
FROM customers,orders,orderitems
WHERE customers.cust_id = orders.cust_id
AND orderitems.order_num = orders.order_num
AND prod_id = 'TNT2';

如果把上述整個查詢包裝成一個名為productcustomers的虛擬表就會方便很多

SELECT cust_name,cust_contact
FROM productcustomers
WHERE prod_id = 'TNT2';

作為檢視,不包含表中的任何列或資料,它包含的是一個SQL查詢

可以用與表基本相同的方式利用它們,可以執行SELECT操作,過濾排序資料,聯結到其他表或者檢視,等等

使用檢視可以簡化操作,在編寫查詢後可以多次重用,它僅使用表的一部分,保護資料

檢視的使用規則和限制

· 命名要唯一,但數目沒有限制

· 檢視可以巢狀,可以從其他檢視中檢索資料的查詢來構造一個檢視

· ORDER BY可以用在檢視中,如果該檢視的SELECT語句中也含有ORDER BY,則原有的ORDER BY會被覆蓋

檢視用CREATE VIEW語句來建立

用SHOW CREATE VIEW viewname; 來檢視建立檢視的語句

用DROP VIEW viewname; 刪除檢視

更新檢視時,可以先用DROP再用CREATE,也可以直接用CREATE OR REPLACE VIEW,如果要更新的檢視不存在,則第2條更新語句會建立一個檢視,若存在,則會替換

檢視最常見的應用之一是隱藏複雜的SQL,這通常會涉及聯結

CREATE VIEW productcustomers AS
SELECT cust_name,prod_id
FROM customers,orderitems
WHERE customers.cust_id = orders.cust_id
AND orderitems.order_num = orders.order_num;    

這個檢視返回已訂購了任意產品的所有客戶的列表

執行

SELECT * FROM productcustomers

將列出訂購了任意產品的客戶

為了檢索產品TNT2的客戶

SELECT cust_name,cust_contact
FROM productcustomers
WHERE prod_id = 'TNT2';

可以用檢視重新格式化檢索出的資料

CREATE VIEW vendorlocations AS
SELECT Contact(RTrim(vend_name),' (',RTrim(vend_country),')')
	   AS vend_title
FROM vendors
ORDER BY vend_name;

這條語句使用與以前的SELECT語句相同的查詢建立檢視,為了檢索出以建立所有郵件標籤的資料

SELECT *
FROM vendorlocations;

可以用檢視過濾不想要的資料,比如過濾沒有電子郵件的客戶

CREATE VIEW customeremaillist AS
SELECT cust_id,cust_email
FROM customers
WHERE cust_email IS NOT NULL;

現在可以像使用其他表一樣使用該檢視

SELECT *
FROM customeremaillist;

如果在使用檢視時也有WHERE子句,則和檢視中的WHERE子句會自動組合

檢視對於簡化計算欄位的使用特別有用

CREATE VIEW orderitemsexpanded AS
SELECT order_num,prod_id,quantity,item_price,quantity*item_price AS expanded_price
FROM orderitems;

為檢索訂單20005的詳細內容

SELECT *
FROM orderitemexpanded
WHERE order_num = 20005;

檢視是可更新的,可以使用INSERT、UPDATE和DELETE,更新一個檢視將更新其基表,如果對檢視增加或刪除行,實際是對其基表增加或刪除行,但並非所有檢視都可以更新,如果檢視定義中有如下操作,則不能進行檢視更新

· 分組(GROUP BY和HAVING)

· 聯結

· 子查詢

·

· 聚集函式(Min(),Count(),Sum()等)

· DISTINCT

· 匯出(計算)列

第二十三章

儲存過程簡單來說,就是為以後的使用而儲存的一條或多條MySQL語句的集合

使用儲存過程的優點:簡單、安全、高效能

MySQL把執行儲存過程稱為呼叫,使用CALL接受儲存過程的名字以及需要傳遞給它的任意此引數

CALL productpricing(@pricelow,@pricehigh,@priceaverage);

儲存過程可以顯示結果,也可以不顯示結果

建立一個返回產品平均價格的儲存過程

CREATE PROCEDURE productpricing()
BEGIN
	SELECT Avg(prod_price) AS priceaverage
	FROM products;
END;

如果儲存過程接受引數,可以在()中列出來,BEGIN和END用來限定儲存過程體,這個建立的過程不返回資料,只是為以後使用而建立

在命令列的模式下,由於程式碼裡有兩個分號,會出現錯誤,可以臨時更改分隔符,除了\以外,任何字元都可以用作語句分隔符

DELIMITER //

CREATE PROCEDURE productpricing()
BEGIN
	SELECT Avg(prod_price) AS priceaverage
	FROM products;
END //

DELIMITER ;

使用這個過程

CALL productpricing();

儲存過程實際上是一種函式

儲存過程建立以後,被儲存在伺服器上供使用,直至被刪除,刪除語句如下

DROP PROCEDURE productpricing;

注意最後沒有()

如果不確定要刪除的過程是否存在,使用DROP PROCEDURE IF EXISTS

一般儲存過程並不顯示結果,而是把結果傳給指定的變數

以下是productpricing的修改版(若先前已有,需要先刪除)

CREATE PROCEDURE productpricing(
	OUT pl DECIMAL(8,2),OUT ph DECIMAL(8,OUT pa DECIMAL(8,2)
)
BEGIN
	SELECT Min(prod_price)
	INTO pl
	FROM products;
	SELECT Max(prod_price)
	INTO ph
	FROM products;
	SELECT Avg(prod_price)
	INTO pa
	FROM products;
END;

SELECT語句中,INTO子句和FROM子句順序可調換

此儲存過程接受3個引數,每個引數必須具有指定的型別,這裡使用十進位制,關鍵字OUT指出相應的引數用來從儲存過程中傳出一個值,MySQL還支援IN(傳遞給儲存過程)、INOUT(傳入和傳出),SELECT語句檢索值,並通過INTO儲存到相應變數

呼叫此儲存過程

CALL productpricing(@pricelow,@priceaverage);

為了顯示檢索出的產品的平均價格

SELECT @priceaverage;

獲得三個值

SELECT @pricehigh,@pricelow,@priceaverage;

使用IN和OUT引數,ordertotal接受訂單號並返回該訂單的合計

CREATE PROCEDURE ordertotal(
	IN onumber INT,OUT ototal DECIMAL(8,2)
)
BEGIN
	SELECT Sum(item_price*quantity)
	FROM order_items
	WHERE order_num = onumber
	INTO ototal;
END;

INTO子句可以跟在SELECT之後

呼叫新的儲存過程

CALL ordertotal(20005,@total);

顯示合計

SELECT @total;

為了得到另一個訂單的合計顯示,再次呼叫儲存過程,重新顯示變數

CALL ordertotal(20009,@total);
SELECT @total;

建立智慧儲存過程

CREATE PROCEDURE ordertotal(
	IN onumber INT
	IN taxable BOOLEAN
	OUT ototal DECIMAL(8,2)
)	COMMENT 'Obtain order total,optionally adding tax'
BEGIN

	DECLARE total DECIMAL(8,2);
	DECLARE taxrate INT DEFAULT 6;
	
	SELECT Sum(item_price*quantity)
	FROM orderitems
	WHERE order_num = onumber
	INTO total;
	
	IF taxable THEN
		SELECT total+(total/100*taxrate) INTO total;
	END IF;
	
	SELECT total INTO ototal;
	
END;

IF語句檢查是否需要增加營業稅到區域性變數total,最後用另一個SELECT語句將total儲存到ototal

COMMENT值不是必須的,如果給出將在SHOW PROCEDURE STATUS的結果中顯示

試驗此儲存過程

CALL ordertotal(20005,@total);
SELECT @total;
CALL ordertotal(20005,1,@total);
SELECT @total;

BOOLEAN值非零(包括負數)為真,0為假

IF還支援ELSEIF和ELSE(前者使用THEN子句,後者不使用)

為顯示用來建立一個儲存過程的CREATE語句

SHOW CREATE PROCEDURE ordertotal;

為了獲得包括何時、由誰建立等詳細資訊的儲存過程列表

SHOW PROCEDURE STATUS

上述語句列出所有儲存過程,可以用LIKE過濾

SHOW PROCEDURE STATUS LIKE 'ordertotal';