20. 认识进阶的 T-SQL
INSERT 陈述式
UPDATE 陈述式
DELETE 陈述式
程序设计关键词
本章总结
本章将详细讨论前面章节所使用过用来修改数据的T-SQL陈述式,加上一些可用来控制程序设计流程的T-SQL关键词。这些陈述式及关键词可用于任何可以使用T-SQL的地方,如使用在指令行、指令码、预存程序、批次文件和应用程序中。我们会特别学习有关数据处理的陈述式,如INSERT、UPDATE和DELETE(这些陈述式在 第十三章 曾经提到过),以及程序结构,如IF、WHILE和CASE。
首先我们为范例建立一个数据表,命名为items(请将该数据表建立在MyDB数据库内),以下为建立items数据表的T-SQL命令:
USE MyDB
GO
CREATE TABLE items
(
item_category CHAR(20) NOT NULL,
item_id SMALLINT NOT NULL,
price SMALLMONEY NULL,
item_desc VARCHAR(30) DEFAULT 'No desc'
)
GO
item_id数据行适合设成IDENTITY属性(请参阅 第十章的〈增加IDENTITY属性〉 。但由于识别数据行不允许直接插入值,因此这里不使用IDENTITY;这样才可以更有弹性的使用包含INSERT陈述式的范例。
INSERT陈述式

在 十三章 曾介绍过INSERT陈述式,可以用来新增数据列至数据表或检视表。以下为INSERT陈述式的基本语法:
INSERT [INTO] table_name [(column_list)] VALUES
expression | derived_table
INTO关键词和 数据行清单(column_list) 参数可依需要而设定。 数据行清单 参数指定了要插入值的数据行,这些值和表达式中所列的值按顺序对应(也可以只包含值的清单)。
插入资料列

下面范例示范如何插入数据列至items数据表。
INSERT INTO items
(item_category, item_id, price, item_desc)
VALUES ('health food', 1, 4.00, 'tofu 6 oz.')
GO
由于已经替数据表中的每个数据行都指定了一个值,而且依照数据列在数据表中定义的顺序列出对应的值,因此我们也可以不必使用 数据行清单 。但是如果值不是依数据行的顺序排列,输入的数据就可能不正确,或是收到错误讯息。举例来说,试着执行以下的陈述式,就会收到错误讯息:
INSERT INTO items
VALUES (1, 'health food', 4.00, 'tofu 6 oz.')
GO
服务器:讯息245,层级16,状态1,行1
将varchar数值 'health food' 转换成数据型别为smallint的数据行语法错
误。
由于值所放置的顺序不正确,因此数据列并没有被插入,并传回错误讯息。我们尝试把item_id插入item_category数据行,item_category插入item_id数据行(两个数据输入的顺序颠倒),两个不同数据型别的数据并不兼容,当然,如果两个数据行的型别是兼容的,就算数值不同,数据也会被插入,不过数据虽然被插入,却是不正确的数据。
要看看插入的列在数据表中如何显示,使用下面的SELECT陈述式查询资料表,以选择所有的列。
SELECT * from items
GO
会看到以下的结果集:
item_category item_id price item_desc
-------------- ------------- ----------
health food 1 4.00 tofu 6 oz.
当建立items数据表时,price数据行定义为允许NULL值,item_desc(描述)数据行的默认值为No desc。如果INSERT陈述式中没有替price数据行指定一个值,新数据列中将插入NULL;如果item_desc数据行没有指定一个值,将插入默认值No desc。
省略资料行值

在前面INSERT陈述式范例中,由于price数据行和item_desc数据行设有默认值,所以可以省略数据行的值和名称。如果要省略数据行的值,必须为其它在 数据行清单 的数据行指定参数,否则,列出的值将依照数据表中数据行定义的顺序,分配给各数据行。
举例来说,我们照下面这样输入:在 数据行清单 中少输入了一行价钱,也没有为 数据行清单 指定值。
INSERT INTO items
VALUES ('junk food', 2, 'fried pork skins')
GO
SQL Server会试着把item_desc(值清单中的第三个值)提供的值(fried pork skins)插入price数据行(数据表中的第三字段)。由于price数据行为smallmoney数据型别,fried pork skins是char数据型别,两个不兼容的数据型别,就会产生如下的错误讯息:
服务器:讯息213,层级16,状态4,行1
插入错误:数据行名称或提供的数值数量与数据表定义不相符。
试想如果fried pork skins的数据型别和price数据行兼容,数据就会被不正确的插入,也影响了整个数据表的一致性与正确性。
记住插入数据表或检视表的值的数据型别必须和字段定义的数据型别兼容。还有,如果插入的数据列违反了规则或条件约束,会产生错误讯息,数据列也不会被插入。
为了避免不兼容数据型别的错误发生,指定 数据行清单 以符合应当被插入的值,如下所示:
INSERT INTO items
(item_category, item_id, item_desc)
VALUES ('junk food', 2, 'fried pork skins')
GO
由于没有指定价格,price数据行将插入NULL值。执行下面的SELECT陈述式:
SELECT * FROM items
应该会出现下述的结果集,结果集中包含已插入的两个数据列,注意price数据列中的NULL。
item_category item_id price item_desc
-------------- -------------- -----------------
health food 1 4.00 tofu 6 oz.
junk food 2 NULL fried pork skins
现在增加另一数据列,不要指定price数据行或item_desc数据行的值,如下所示:
INSERT INTO items
(item_category, item_id)
VALUES ('toy', 3)
GO
使用下面的查询就可以得到该列的结果集:
SELECT * FROM items WHERE item_id = 3
结果集如下所示:
item_category item_id price item_desc
-------------- -------------- ------------
toys 3 NULL No desc
注意price数据行中的NULL和item_desc资料列中的No desc。这些值可用稍后本章将介绍的UPDATE陈述式变更。
有四种类型的数据行,在没有指定值时,SQL Server会自动提供值给该数据行:分别是 允许NULL值 、 设定默认值 、 IDENTITY属性 和 时间戳记(timestamp) 的数据行。之前讨论过允许NULL值的数据行和含默认值的数据行,若没有指定值会产生的情形(在 第十章 讨论过)。大部分的情况下,是不能手动插入数据值到这两种类型的数据行中。
________________________________________
说明
在数据表中执行INSERT操作时要谨慎。请确定插入的数据正确地放入应当插入的数据行。确定测试了所有的T-SQL程序代码后,再存取或修改数据。
________________________________________
从另一数据表中新增数据列

您可以从数据表插入数据列到另一个资料表中。这可以透过在INSERT陈述式中使用衍生资料表,或在传回数据列的预存程序中使用EXECUTE子句来完成。
________________________________________
说明
衍生数据表(Derived Table)即为来自另一个T-SQL陈述式FROM子句中的SELECT陈述式结果集,在 第十四章 中会讨论到衍生数据表。
________________________________________
现在来示范如何使用衍生数据表插入数据。首先建立数据表,命名为two_newest_items,并将item资料表中的两笔数据列插入two_newest_items,以下是建立新数据表所使用的CREATE TABLE陈述式:
CREATE TABLE two_newest_items
(
item_id SMALLINT NOT NULL,
item_desc VARCHAR(30) DEFAULT 'no desc'
)
GO
要从items数据表将item_id和item_desc资料行中最新的值插入two_newest_items数据表,请使用下面的INSERT陈述式:
INSERT INTO two_newest_items
(item_id, item_desc)
SELECT TOP 2 item_id, item_desc FROM items
ORDER BY item_id DESC
GO
请注意这里使用的是SELECT陈述式,而不是列在INSERT陈述式的值清单。SELECT陈述式从一个已存在的数据表中传回数据,传回的数据当成值清单。另外,注意在SELECT陈述式上并没有加括号,因为括号将造成传回语法错误。
要查询新数据表中所有数据列,可使用下列程序代码:
SELECT * FROM two_newest_items
所显示的结果集如下:
item_id item_desc
-------------------------
3 No desc
2 fried pork skins
请注意,我们在这里的INSERT陈述式中使用ORDER BY子句,使item_id的数据列递减排序。
如果我们将前面的SELECT陈述式建立为预存程序,使用EXECUTE陈述式加上预存程序名称,也可以得到同样的结果(预存程序将会在 第二十一章 介绍)。操作的方法为,首先使用DELETE陈述式删除two_newest_items数据表中现存的所有数据列。(详细细节请参阅本章稍后的 〈DELETE陈述式〉 。)然后建立一个预存程序,命名top_two,并用EXECUTE陈述式插入两个新数据列到two_newest_items数据表。操作的T-SQL陈述式为:
DELETE FROM two_newest_items
GO
CREATE PROCEDURE top_two
AS
SELECT TOP 2 item_id, item_desc FROM items
ORDER BY item_id DESC
GO
INSERT INTO two_newest_items
(item_id, item_desc)
EXECUTE top_two
GO
现在可以看到,我们已经使用top_two预存程序成功地插入两个新的数据列。
________________________________________
相关信息
INSERT陈述式可以用于指定数据表提示(Table Hints)。要详细了解可以和INSERT陈述式一起使用的数据表提示,请使用《在线丛书》,在搜寻标签页中输入 锁定提示 ,选择 锁定提示 主题。
________________________________________
UPDATE陈述式

UPDATE陈述式用于修改或更新存在的数据,基本语法如下:
UPDATEtable_nameSETcolumn_name = expression
[FROMtable_source] WHERE search_condition
更新数据列

本范例将使用之前的items范例数据表,首先更新之前没有输入价格的junk food数据列。要识别数据列,将搜寻条件指定为fried pork skins,用下列陈述式更新价格至 $2元:
UPDATE items SET price = 2.00
WHERE item_desc = 'fried pork skins'
GO
然后使用查询选出junk food数据列:
SELECT * FROM items
WHERE item_desc = 'fried pork skins'
GO
junk food数据列输出显示如下,原来的NULL值被2.00所替代:
item_category item_id price item_desc
-------------- ----------------------- ------------
junk food 2 2.00 fried pork skins
您也可以使用以下的陈述式将价格增加10%:
UPDATE items SET price = price * 1.10
WHERE item_desc = 'fried pork skins'
GO
现在如果选择junk food数据列,价格已变更至 $2.20($2乘以1.10的结果);其它项目的价格则维持不变。
使用UPDATE陈述式可以更新一个以上的资料列。举例来说,可以将所有数据列的价格都增加10%,其陈述式如下:
UPDATE items SET price = price * 1.10
GO
得到的输出结果如下:
item_category item_id price item_desc
--------------- --------------- -----------------
health food 1 4.40 tofu 6 oz.
junk food 2 2.42 fried pork skins
toys 3 NULL No desc
价格设为NULL的数据列不会变动(因为NULL* 1.10还是等于NULL),这样的执行方式是可以接受的,不会产生错误讯息。
使用FROM子句

UPDATE陈述式可利用FROM子句指定更新操作中的数据来源,数据来源清单可以包含数据表名称、检视表名称、数据列及函数、衍生数据表和联结数据表,甚至更新中的数据表也可以当成数据表来源。以下是范例,首先利用CREATE TABLE陈述式建立一个新的数据表,命名为tax,再用INSERT陈述式插入新的数据列,在tax_percent数据行中将值设为5.25。
CREATE TABLE tax
(
tax_percent real NOT NULL,
change_date smalldatetime DEFAULT getdate( )
)
GO
INSERT INTO tax
(tax_percent) VALUES (5.25)
GO
由于没有设定日期和时间,change_date数据行会使用默认值,由GETDATE函数得到目前的日期和时间。
现在在item数据表中新增一个新的数据行,命名为price_with_tax,并允许NULL值,陈述式如下:
ALTER TABLE items
ADD price_with_tax smallmoney NULL
GO
接下来要更新所有price_with_tax数据行,这个数据行的数据是由items.price * tax.tax_percent运算而来。使用UPDATE陈述式和FROM子句如下:
UPDATE items
SET price_with_tax = i.price +
(i.price * t.tax_percent / 100)
FROM items i, tax t
GO
在price数据行有数值的数据列,在price_with_tax数据行也会出现运算后的数值;如果原price数据行的数据为NULL,则相对应的price_with_tax也会出现NULL(NULL乘以任何值还是NULL),运算后的结果集如下(结果也包含所有先前对数据所做的修改):
item_category item_id price item_desc price_with_tax
-------------- ------- ------ --------------- ------------------
health food 1 4.40 tofu 6 oz 4.63
junk food 2 2.42 fried pork skin 2.55
toys 3 NULL No desc NULL
如果在数据表中新增记录,可以考虑执行之前的UPDATE陈述式,但这样会再次更新price_with_tax数据行,浪费程序时间。要避免这种情况,可以使用UPDATE陈述式的WHERE子句,让SQL Server更新price_with_tax中只含有NULL值的数据行。
UPDATE items
SET price_with_tax = i.price +
(i.price * t.tax_percent / 100)
FROM items i, tax t
WHERE i.price_with_tax IS NULL
GO
UPDATE陈述式非常适合当作触发程序,当插入一个值到price数据行时,便会执行。触发程序是一种特殊的预存程序,在某一特定的条件下会自动执行。触发程序将在 第二十二章 中加以讨论。
使用衍生数据表

使用UPDATE陈述式的另一个方法是在FROM子句中附带一个衍生数据表或子查询。然后衍生数据表就可以当成外部UPDATE的输入。使用范例,我们将在子查询中使用two_newest_items数据表,在外部UPDATE陈述式中使用items数据表。我们想要更新items数据表中的两列数据,以使price_with_tax数据行储存NULL值,可以利用查询two_newest_items数据表,即可找出数据列中需要更新的item_id值:
UPDATE items
SET price_with_tax = NULL
FROM (SELECT item_id FROM two_newest_items) AS t1
WHERE items.item_id = t1.item_id
GO
SELECT陈述式当成子查询,其结果会放入命名为t1的暂存衍生数据表中,此数据表会被WHERE子句当成搜寻条件。子查询的结果产生item_id的2和3两个值,只要在item_id数据行中有2或3这两个值,就会被影响。在item_id值为3的资料列,因为对应的price_with_tax数据行已有NULL值,故不会再改变;而在item_id值为2的数据列则更新为NULL值。结果集如下:
item_category item_id price item_desc price_with_tax
-------------- ------- ----- --------------- --------------
health food 1 4.40 tofu 6 oz. 2.30
junk food 2 2.42 fried pork skins NULL
toys 3 NULL No desc NULL
DELETE陈述式

DELETE陈述式用于从数据表或检视表中移除(删除)一个或多个数据列。DELETE不影响数据表定义;只是从数据表中删除数据列。DELETE陈述式的基本语法如下:
DELETE [FROM]table_name|view_name
[FROMtable_sources] WHEREsearch_condition
第一个和第二个FROM关键词可视需要使用。在第二个FROM子句中,数据表来源中的数据列不会被删除,但会从DELETE后指定的table_name或view_name中被删除。
删除个别数据列

在DELETE中使用WHERE子句,可以指定从资料表中删除特定的数据列。举例来说,要从items数据表中删除item_category中toy这个数据列,执行下面的叙述:
DELETE FROM items
WHERE item_category = 'toy'
GO
该陈述式从items资料表中删掉了一列。
使用一个或多个数据表来源的第二个FROM子句,可指定其它数据表或检视表,供WHERE搜寻条件使用。例如,要从items数据表中删除对应于two_newest_items数据表中的数据列,可执行下面的陈述式:
DELETE items
FROM two_newest_items
WHERE items.item_id = two_newest_items.item_id
GO
请注意,在这个陈述式中,two_newest_items数据表中的item_id数据列含有2和3的值,而items数据表中的item_id数据列则含有1和2的值,所以2就被删除(因为符合搜寻条件)。在two_newest_items数据表(数据来源)的两个数据列则不受影响。
删除所有数据列

要删除数据表中的所有数据列,可利用DELETE陈述式。下面的DELETE陈述式将删除two_newest_items数据表中的所有数据列:
DELETE FROM two_newest_items
GO
现在two_newest_items数据表是一个空的数据表,即不包含数据列的数据表。如果想删除数据表定义,请使用 第十五章 说明的DROP TABLE指令:
DROP TABLE two_newest_items
GO
________________________________________
相关信息
在《在线丛书》中可找到更多使用DELETE的方法,例如利用联结数据表作为数据表来源,以及使用数据表和查询提示,请在《在线丛书》索引中查阅DELETE并选择相关的次主题。
________________________________________
程序设计关键词

T-SQL陈述式中有几个有用的程序设计结构关键词,能用来控制程序流程。这些结构可用于批处理(一次执行一组T-SQL陈述式)、预存程序、指令码和特别的查询(本节范例会使用pubs数据库)。
IF...ELSE

IF...ELSE结构利用条件以决定将执行那一条T-SQL陈述式,语法如下:
IFBoolean_expression
T-SQL_statement | block_of_statements
[ELSET-SQL_statement | block_of_statements]
布尔表达式(Boolean Expression)指的是传回TRUE或FALSE的表达式。如果IF子句中的表达式传回TRUE,陈述式将执行,ELSE子句及其叙述则不执行;如果表达式传回FALSE,就会只执行ELSE关键词后的叙述。 陈述式区块(block_of_statements) 代表一个以上的T-SQL陈述式。使用陈述式区块时,无论该陈述式区块在IF子句中、ELSE子句中或是两者都有,都必须用关键词BEGIN和END来指定区块的开始和结束。
您也可以使用没有ELSE子句的IF子句。现在我们先来看看只使用IF子句的范例,下面的程序代码将检查表达式,如果表达式传回TRUE,将执行接下来的PRINT陈述式:
IF (SELECT ytd_sales FROM titles
WHERE title_id = 'PC1035') > 5000
PRINT 'year-to-date sales are
greater than $5,000 for PC1035.'
GO
因为title_id = 'PC1035'数据列ytd_sales的值是8780(大于5000),因此IF表达式将计算为TRUE,并执行PRINT陈述式,将「Year-to-date sales are greater than $5,000 for PC1035」打印在屏幕上。
现在将一个ELSE子句新增到上面的范例中,并将> 5000变为> 9000。IF...ELSE显示如下:
IF (SELECT ytd_sales FROM titles
WHERE title_id = 'PC1035') > 9000
PRINT 'yeartodate sales are
greater than $9,000 for PC1035.'
ELSE
PRINT 'yeartodate sales are
less than $9,000 for PC1035.'
GO
在本范例中,由于IF表达式传回FALSE。ELSE子句后的打印陈述式将被执行。
让我们更进一步扩展以上的范例,并在IF和ELSE子句中新增陈述式区块。根据IF条件传回的结果,打印讯息后执行查询,下面是范例程序代码:
IF (SELECT ytd_sales FROM titles WHERE title_id = 'PC1035') > 9000
BEGIN
PRINT 'year-to-date sales are
greater than $9,000 for PC1035.'
SELECT ytd_sales FROM titles
WHERE title_id = 'PC1035'
END
ELSE -- ytd_sales必须小与或等于9000.
BEGIN
PRINT 'year-to-date sales are
less than or equal to $9,000 for PC1035.'
SELECT price FROM titles
WHERE title_id = 'PC1035'
END
GO
IF计算式结果是FALSE,所以会执行ELSE子句中BEGIN和END之间的叙述:执行PRINT后接着执行SELECT陈述式,显示书价为 $22.95。
您也可以在IF子句或ELSE子句后使用巢状的IF陈述式。举例来说,可执行下面的程序代码,使用巢状的IF...ELSE陈述式找出ytd_sales平均值的范围:
IF (SELECT avg(ytd_sales) FROM titles) < 10000
IF (SELECT avg(ytd_sales) FROM titles) < 5000
IF (SELECT avg(ytd_sales) FROM titles) < 2000
PRINT 'Average year-to-date sales are
less than $2,000.'
ELSE
PRINT 'Average year-to-date_ sales are
between $2,000 and $4,999.'
ELSE
PRINT 'Average year-to-date sales are
between $5,000 and $9,999.'
ELSE
PRINT 'Average year-to-date sales are greater
than $9,999.'
GO
当您执行该程序代码时,将会看到两次这样的讯息:「警告:汇总(aggregate)或其它SET操作已消除Null值。」该讯息表示数据表计算平均数时,在ytd_sales数据行的NULL值未被使用,也未被当成值来计算。由于平均值是 $6,090,该程序代码所得到最后结果是「Average year-to-date sales are between $5,000 and $9,999.」。请注意,当使用IF陈述式时,很容易就把IF应该和哪个ELSE为一组混淆,或者有了IF但忘了指示ELSE,所以用TAB键(如上所示)是不错的办法。
WHILE

该条件判断式是设定SQL陈述式或陈述式区块重复执行的条件。只要指定条件为TRUE,陈述式会一直重复执行。这就是所谓的WHILE循环(Loop),因为WHILE结构中的程序代码在循环中重复。语法显示如下:
WHILEBoolean_expression
SQL_statement | block_of_statements
[BREAK]SQL_statement | block_of_statements
[CONTINUE]
同上述的IF...ELSE,使用BEGIN和END来指定WHILE循环中陈述式的起始和结束。BREAK关键词则将导致从WHILE循环中跳出,然后接着执行WHILE循环结束后的陈述式。如果一个WHILE循环与另一个WHILE循环是巢状结构,BREAK关键词只会从被包含的WHILE循环中跳出,继续执行该循环外所有的的陈述式和循环。CONTINUE关键词指定WHILE循环重新执行BEGIN和END之间的陈述式,忽略CONTINUE之后的其它陈述式。
现在使用简单的WHILE循环重复执行UPDATE陈述式。WHILE循环的检测条件为:royalty资料行的平均值是否少于20。如果检测传回TRUE,所有royalty数据列的值都会增加5%。接着再次检测WHILE条件,重复更新直到royalty字段的平均值为20或更多。程序代码如下:
WHILE (SELECT AVG(royalty) FROM roysched) < 20
UPDATE roysched SET royalty = royalty * 1.05
GO
由于royalty数据行原来的平均值是15,因此在平均值达到20前,WHILE循环共执行了21次,直到检测条件回传值为FALSE时才停止。
现在看一个在WHILE循环中使用BREAK、CONTINUE、BEGIN和END的范例。我们将执行循环直到royalty的平均值达到25。但如果在循环中,royalty的最大值超过27,将中断循环,并在WHILE循环结束后加一个SELECT陈述式。下面是T-SQL程序代码:
WHILE (SELECT AVG(royalty) FROM roysched) < 25
BEGIN
UPDATE roysched SET royalty = royalty * 1.05
IF (SELECT MAX(royalty) FROM roysched) > 27
BREAK
ELSE
CONTINUE
END
SELECT MAX(royalty) AS "MAX royalty" FROM roysched
GO
数据表中已经存在的royalty值大于27,因此只执行一次循环,而royalty的平均值小于25%,因此UPDATE执行一次;接着IF陈述式检测并传回TRUE,所以执行BREAK,跳出WHILE循环;最后执行END关键词后的陈述式,也就是SELECT陈述式。
您还能使用巢状的WHILE循环,但记得BREAK或CONTINUE关键词只应用于呼叫它们的循环,而不是外部的WHILE循环。
CASE

CASE关键词用来评估各种条件的清单,并且传回多种可能的结果表达式其中之一,最常见的用法是用来取代程序代码,或是使用具可读性的数值(缩写值),或者用来分类值,本节的范例将加以介绍。CASE被分为两种格式:简单(simple)格式和搜寻(searched)格式。简单格式会使用CASE之后的表达式,与一组在WHEN之后的表达式做比较,以决定其结果;搜寻格式则评估一组布尔表达式为TRUE或FALSE,以决定其结果。以下为简单格式的语法:
CASE input_expression
WHEN when_expression THEN result_expression
[WHEN when_expression THEN result_expression...n]
[ELSE else_result_expression]
END
when_expression和input_expression会依其在CASE子句中的排列顺序进行比较,如果相对应的when_expression等于input_expression,将传回result_expression值。如果没有对应的值,将传回指定的else_result_expression的值。如果没有指定else_result_expression,则传回NULL。注意在简单格式CASE子句中,input_expression值和when_expression值的数据型别必须相同,或是数据型别之间允许隐含转换。
下面的范例在SELECT陈述式中使用简单格式的CASE子句。Sales数据表的payterms数据行包含了Net 30、Net 60、On invoice或None。这个T-SQL陈述式允许使用替代值(易于了解)以显示payterms数据行:
SELECT 'Payment Terms' =
CASE payterms
WHEN 'Net 30' THEN 'Payable 30 days
after invoice'
WHEN 'Net 60' THEN 'Payable 60 days
after invoice'
WHEN 'On invoice' THEN 'Payable upon
receipt of invoice'
ELSE 'None'
END,
title_id
FROM sales
ORDER BY payterms
GO
CASE子句会检测payterms数据行中,被SELECT陈述式中所指定每个数据列的值。当when_expression值等于payterms值,传回result_expression的值。来自CASE子句的结果显示在结果集中的Payment Terms资料行中,如下所示:
Payment Terms title_id
------------------------------- -----------
Payable 30 days after invoice PC8888
Payable 30 days after invoice TC3218
Payable 30 days after invoice TC4203
Payable 30 days after invoice TC7777
Payable 30 days after invoice PS2091
Payable 30 days after invoice MC3021
Payable 30 days after invoice BU1111
Payable 30 days after invoice PC1035
Payable 60 days after invoice BU1032
Payable 60 days after invoice PS2091
Payable 60 days after invoice PS2091
Payable 60 days after invoice PS1372
Payable 60 days after invoice PS2106
Payable 60 days after invoice PS3333
Payable 60 days after invoice PS7777
Payable 60 days after invoice BU7832
Payable 60 days after invoice MC2222
Payable upon receipt of invoice PS2091
Payable upon receipt of invoice BU2075
Payable upon receipt of invoice MC3021
Payable upon receipt of invoice BU1032

(影响21个数据列)
现在我们来看看搜寻格式的CASE子句,语法如下:
CASE
WHENBoolean_expressionTHENresult_expression
[WHENBoolean_expressionTHENresult_expression...n]
[ELSEelse_result_expression]
END
简单格式与搜寻格式的CASE子句其区别在于:搜寻格式的CASE子句在CASE关键词后没有input_expression值,而在WHEN关键词后以Boolean表达式检测TRUE或FALSE,并不像简单格式比较表达式之间是否相等。搜寻格式的CASE子句依照各个表达式排列的顺序检测Boolean_expression值,并传回评估为TRUE的第一个result_expression值。
举例来说,下面的SELECT陈述式中的CASE子句将检测每一个数据列的price值,并且以书的价格范围为标准,比较数据列中的price值,传回一个字符串:
SELECT 'Price Range' =
CASE
WHEN price BETWEEN .01 AND 10.00
THEN 'Inexpensive: $10.00 or less'
WHEN price BETWEEN 10.01 AND 20.00
THEN 'Moderate: $10.01 to $20.00'
WHEN price BETWEEN 20.01 AND 30.00
THEN 'Semi-expensive: $20.01 to $30.00'
WHEN price BETWEEN 30.01 AND 50.00
THEN 'Expensive: $30.01 to $50.00'
WHEN price IS NULL
THEN 'No price listed'
ELSE 'Very expensive!'
END,
title_id
FROM titles
ORDER BY price
GO
结果集显示如下:
Price Range title_id
------------------------------- ----------
No price listed MC3026
No price listed PC9999
Inexpensive: $10 or less MC3021
Inexpensive: $10 or less BU2075
Inexpensive: $10 or less PS2106
Inexpensive: $10 or less PS7777
Moderate: $10.01 to $20 PS2091
Moderate: $10.01 to $20 BU1111
Moderate: $10.01 to $20 TC4203
Moderate: $10.01 to $20 TC7777
Moderate: $10.01 to $20 BU1032
Moderate: $10.01 to $20 BU7832
Moderate: $10.01 to $20 MC2222
Moderate: $10.01 to $20 PS3333
Moderate: $10.01 to $20 PC8888
Semiexpensive: $20.01 to $30 TC3218
Semiexpensive: $20.01 to $30 PS1372
Semiexpensive: $20.01 to $30 PC1035

(影响18个数据列)
________________________________________
说明
以上两个CASE子句范例中,由于整个CASE子句是SELECT子句中 数据行清单 的一部份,所以我们在END关键词后插入了逗号后,才加入title_id数据行。换句话说,整个CASE子句在数据行清单中只是一个项目。这是CASE关键词最常被使用的方法。
________________________________________
其它关键词

以下为其它可用于控制程序流程的T-SQL关键词:
GOTO label:当GOTO中定义了某label时,导向处理程序直接由该label后开始继续处理。

RETURN :无条件从查询或程序中退出。

WAITFOR :为陈述式的执行设定延迟或指定时间。

________________________________________
相关信息
要详细了解如何使用这些关键词,可在《在线丛书》索引中查阅GOTO、RETURN和WAITFOR,检视对话框中所列的主题。
________________________________________
本章总结

在本章中,您学到了使用INSERT、UPDATE和DELETE的T-SQL陈述式,以及用来控制程序流程的关键词IF、ELSE、WHILE、BEGIN、END和CASE。 第二十一章 将学习如何建立预存程序,配合使用这些陈述式和结构。