表格是数据库中储存资料的基本架构。在绝大部份的情况下,数据库厂商不可能知道您需要如何储存您的资料,所以通常您会需要自己在数据库中建立表格。虽然许多数据库工具可以让您在不需用到 SQL 的情况下建立表格,不过由于表格是一个最基本的架构,我们决定包括 CREATE TABLE 的语法在这个网站中。
在我们跳入 CREATE TABLE 的语法之前,我们最好先对表格这个东西有些多一点的了解。表格被分为栏位 (column) 及列位 (row)。每一列代表一笔资料,而每一栏代表一笔资料的一部份。举例来说,如果我们有一个记载顾客资料的表格,那栏位就有可能包括姓、名、地址、城市、 国家、生日...等等。当我们对表格下定义时,我们需要注明栏位的标题,以及那个栏位的资料种类。
那,资料种类是什么呢?资料可能是以许多不同的形式存在的。它可能是一个整数 (例如 1),、一个实数(例如 0.55)、一个字串 (例如 'sql')、一个日期/时间 (例如 '2000-JAN-25 03:22:22')、或甚至是 以二进法 (binary) 的状态存在。当我们在对一个表格下定义时,我们需要对每一个栏位的资料种类下定义。(例如 '姓' 这个栏位的资料种类是 char(50)━━代表这是一个 50 个字符的字串)。我们需要注意的一点是不同的数据库有不同的资料种类,所以在对表格做出定义之前最好先参考一下数据库本身的说明。
CREATE TABLE 的语法是:
("栏位 1" "栏位 1 资料种类",
"栏位 2" "栏位 2 资料种类",
... );
若我们要建立我们上面提过的顾客表格,我们就打入以下的 SQL:
(First_Name char(50),
Last_Name char(50),
Address char(50),
City char(50),
Country char(25),
Birth_Date datetime);
视观表 (View) 可以被当作是虚拟表格。它跟表格的不同是,表格中有实际储存资料,而视观表是建立在表格之上的一个架构,它本身并不实际储存资料。
建立一个视观表的语法如下:
"SQL 语句" 可以是任何一个我们在这个教材中有提到的 SQL。
来看一个例子。假设我们有以下的表格:
Customer 表格
栏位名称 | 资料种类 |
First_Name | char(50) |
Last_Name | char(50) |
Address | char(50) |
City | char(50) |
Country | char(25) |
Birth_Date | datetime |
若要在这个表格上建立一个包括 First_Name, Last_Name, 和 Country 这三个栏位的视观表,我们就打入,
AS SELECT First_Name, Last_Name, Country
FROM Customer;
现在,我们就有一个叫做 V_Customer 的视观表:
V_Customer 视观表
栏位名称 | 资料种类 |
First_Name | char(50) |
Last_Name | char(50) |
Country | char(25) |
我们也可以用视观表来连接两个表格。在这个情况下,使用者就可以直接由一个视观表中找出她要的资讯,而不需要由两个不同的表格中去做一次连接的动作。假设有以下的两个表格:
Store_Information表格
Store_Name | Sales | Txn_Date |
Los Angeles | 1500 | 05-Jan-1999 |
San Diego | 250 | 07-Jan-1999 |
Los Angeles | 300 | 08-Jan-1999 |
Boston | 700 | 08-Jan-1999 |
Geography 表格
Region_Name | Store_Name |
East | Boston |
East | New York |
West | Los Angeles |
West | San Diego |
我们就可以用以下的指令来建一个包括每个地区 (Region) 销售额 (Sales) 的视观表:
AS SELECT A1.Region_Name REGION, SUM(A2.Sales) SALES
FROM Geography A1, Store_Information A2
WHERE A1.Store_Name = A2.Store_Name
GROUP BY A1.Region_Name;
这就给我们有一个名为 V_REGION_SALES 的视观表。这个视观表包含不同地区的销售哦。如果我们要从这个视观表中获取资料,我们就打入,
结果:
REGION | SALES |
East | 700 |
West | 2050 |
索引 (Index) 可以帮助我们从表格中快速地找到需要的资料。举例来说,假设我们要在一本园艺书中找如何种植青椒的讯息。若这本书没有索引的话,那我们是必须要从头开始 读,直到我们找到有关种直青椒的地方为止。若这本书有索引的话,我们就可以先去索引找出种植青椒的资讯是在哪一页,然后直接到那一页去阅读。很明显地,运 用索引是一种有效且省时的方式。
从资料库表格中寻找资料也是同样的原理。如果一个表格没有索引的话,资料库系统就需要将整个表格的资料读出 (这个过程叫做'table scan')。若有适当的索引存在,资料库系统就可以先由这个索引去找出需要的资料是在表格的什么地方,然后直接去那些地方抓资料。这样子速度就快多了。
因此,在表格上建立索引是一件有利于系统效率的事。一个索引可以涵盖一或多个栏位。建立索引的语法如下:
现在假设我们有以下这个表格,
Customer 表格
栏位名称 | 资料种类 |
First_Name | char(50) |
Last_Name | char(50) |
Address | char(50) |
City | char(50) |
Country | char(25) |
Birth_Date | datetime |
若我們要在 Last_Name 這個欄位上建一個索引,我們就打入以下的指令,
ON Customer (Last_Name);
若我们要在 Last_Name 这个栏位上建一个索引,我们就打入以下的指令,
ON Customer (City, Country);
索引的命名并没有一个固定的方式。通常会用的方式是在名称前加一个字首,例如 "IDX_" ,来避免与资料库中的其他物件混淆。另外,在索引名之内包括表格名及栏位名也是一个好的方式。
请读者注意,每个资料库会有它本身的 CREATE INDEX 语法,而不同资料库的语法会有不同。因此,在下指令前,请先由资料库使用手册中确认正确的语法。
在表格被建立在资料库中后,我们常常会发现,这个表格的结构需要有所改变。常见的改变如下:
- 加一个栏位
- 删去一个栏位
- 改变栏位名称
- 改变栏位的资料种类
以上列出的改变并不是所有可能的改变。ALTER TABLE 也可以被用来作其他的改变,例如改变主键定义。
ALTER TABLE 的语法如下:
[改变方式];
[改变方式] 的详细写法会依我们想要达到的目标而有所不同。再以上列出的改变中,[改变方式] 如下:
- 加一个栏位: ADD "栏位 1" "栏位 1 资料种类"
- 删去一个栏位: DROP "栏位 1"
- 改变栏位名称: CHANGE "原本栏位名" "新栏位名" "新栏位名资料种类"
- 改变栏位的资料种类: MODIFY "栏位 1" "新资料种类"
以下我们用在 CREATE TABLE 一页建出的 Customer 表格来当作例子:
Customer 表格
栏位名称 | 资料种类 |
First_Name | char(50) |
Last_Name | char(50) |
Address | char(50) |
City | char(50) |
Country | char(25) |
Birth_Date | datetime |
第一,我们要加入一个叫做 "Gender" 的栏位。这可以用以下的指令达成:
这个指令执行后的表格架构是:
Customer 表格
栏位名称 | 资料种类 |
First_Name | char(50) |
Last_Name | char(50) |
Address | char(50) |
City | char(50) |
Country | char(25) |
Birth_Date | datetime |
Gender | char(1) |
接下来,我们要把 "Address" 栏位改名为 "Addr"。这可以用以下的指令达成:
这个指令执行后的表格架构是:
Customer 表格
栏位名称 | 资料种类 |
First_Name | char(50) |
Last_Name | char(50) |
Addr | char(50) |
City | char(50) |
Country | char(25) |
Birth_Date | datetime |
Gender | char(1) |
再来,我们要将 "Addr" 栏位的资料种类改为 char(30)。这可以用以下的指令达成:
这个指令执行后的表格架构是:
Customer 表格
栏位名称 | 资料种类 |
First_Name | char(50) |
Last_Name | char(50) |
Addr | char(30) |
City | char(50) |
Country | char(25) |
Birth_Date | datetime |
Gender | char(1) |
最后,我们要删除 "Gender" 栏位。这可以用以下的指令达成:
这个指令执行后的表格架构是:
Customer 表格
栏位名称 | 资料种类 |
First_Name | char(50) |
Last_Name | char(50) |
Addr | char(30) |
City | char(50) |
Country | char(25) |
Birth_Date | datetime |
主键 (Primary Key) 中的每一笔资料都是表格中的唯一值。换言之,它是用来独一无二地确认一个表格中的每一行资料。主键可以是原本资料内的一个栏位,或是一个人造栏位 (与原本资料没有关系的栏位)。主键可以包含一或多个栏位。当主键包含多个栏位时,称为组合键 (Composite Key)。
主键可以在建置新表格时设定 (运用 CREATE TABLE 语句),或是以改变现有的表格架构方式设定 (运用 ALTER TABLE)。
以下举几个在建置新表格时设定主键的方式:
MySQL:
(SID integer,
Last_Name varchar(30),
First_Name varchar(30),
PRIMARY KEY (SID));
Oracle:
(SID integer PRIMARY KEY,
Last_Name varchar(30),
First_Name varchar(30));
SQL Server:
(SID integer PRIMARY KEY,
Last_Name varchar(30),
First_Name varchar(30));
以下则是以改变现有表格架构来设定主键的方式:
MySQL:
Oracle:
SQL Server:
请注意,在用 ALTER TABLE 语句来添加主键之前,我们需要确认被用来当做主键的栏位是设定为 『NOT NULL』 ;也就是说,那个栏位一定不能没有资料。
外来键是一个(或数个)指向另外一个表格主键的栏位。外来键的目的是确定资料的参考完整性 (referential integrity)。换言之,只有被准许的资料值才会被存入资料库内。
举例来说,假设我们有两个表格:一个 CUSTOMER 表格,里面记录了所有顾客的资料;另一个 ORDERS 表格,里面记录了所有顾客订购的资料。在这里的一个限制,就是所有的订购资料中的顾客,都一定是要跟在 CUSTOMER 表格中存在。在这里,我们就会在 ORDERS 表格中设定一个外来键,而这个外来键是指向 CUSTOMER 表格中的主键。这样一来,我们就可以确定所有在 ORDERS 表格中的顾客都存在 CUSTOMER 表格中。换句话说,ORDERS 表格之中,不能有任何顾客是不存在于 CUSTOMER 表格中的资料。
这两个表格的结构将会是如下:
CUSTOMER 表格
栏位名 | 性质 |
SID | 主键 |
Last_Name | |
First_Name |
ORDERS 表格
栏位名 | 性质 |
Order_ID | 主键 |
Order_Date | |
Customer_SID | 外来键 |
Amount |
在以上的例子中,ORDERS 表格中的 Customer_SID 栏位是一个指向 CUSTOMER 表格中 SID 栏位的外来键。
以下列出几个在建置 ORDERS 表格时指定外来键的方式:
MySQL:
(Order_ID integer,
Order_Date date,
Customer_SID integer,
Amount double,
PRIMARY KEY (Order_ID),
FOREIGN KEY (Customer_SID) REFERENCES CUSTOMER (SID));
Oracle:
(Order_ID integer PRIMARY KEY,
Order_Date date,
Customer_SID integer REFERENCES CUSTOMER (SID),
Amount double);
SQL Server:
(Order_ID integer PRIMARY KEY,
Order_Date datetime,
Customer_SID integer REFERENCES CUSTOMER (SID),
Amount double);
以下的例子则是藉着改变表格架构来指定外来键。这里假设 ORDERS 表格已经被建置,而外来键尚未被指定:
MySQL:
ADD FOREIGN KEY (Customer_SID) REFERENCES CUSTOMER (SID);
Oracle:
ADD (CONSTRAINT fk_orders1) FOREIGN KEY (Customer_SID) REFERENCES CUSTOMER (SID);
SQL Server:
ADD FOREIGN KEY (Customer_SID) REFERENCES CUSTOMER (SID);
有时候我们会决定我们需要从数据库中清除一个表格。事实上,如果我们不能这样做的话,那将会是一个很大的问题,因为数据库管理师 (Database Administrator -- DBA) 势必无法对数据库做有效率的管理。还好,SQL 有提供一个 DROP TABLE 的语法来让我们清除表格。 DROP TABLE 的语法是:
我们如果要清除在SQL CREATE 中建立的顾客表格,我们就打入:
有时候我们会需要清除一个表格中的所有资料。要达到者个目的,一种方式是我们在 SQL DROP 那一页 看到 的 DROP TABLE 指令。不过这样整个表格就消失,而无法再被用了。另一种方式就是运用 TRUNCATE TABLE 的指令。在这个指令之下,表格中的资料会完全消失,可是表格本身会继续存在。 TRUNCATE TABLE 的语法为下:
所以,我们如果要清除在 SQL Create Table 那一页建立的顾客表格之内的资料,我们就打入:
到目前为止,我们学到了将如何把资料由表格中取出。但是这些资料是如果进入这些表格的呢? 这就是这一页 (INSERT INTO) 和下一页 (UPDATE) 要讨论的。
基本上,我们有两种作法可以将资料输入表格中内。一种是一次输入一笔,另一种是一次输入好几笔。我们先来看一次输入一笔的方式。
依照惯例,我们先介绍语法。一次输入一笔资料的语法如下:
VALUES ("值1", "值2", ...);
假设我们有一个架构如下的表格:
Store_Information 表格
栏位名称 | 资料种类 |
Store_Name | char(50) |
Sales | float |
Txn_Date | datetime |
而我们要加以下的这一笔资料进去这个表格:在 January 10, 1999,Los Angeles 店有$900 的营业额。我们就打入以下的 SQL 语句:
VALUES ('Los Angeles', 900, 'Jan-10-1999');
第二种 INSERT INTO 能够让我们一次输入多笔的资料。跟上面刚的例子不同的是,现在我们要用 SELECT 指令来指明要输入表格的资料。如果您想说,这是不是说资料是从另一个表格来的,那您就想对了。一次输入多笔的资料的语法是:
SELECT "栏位3", "栏位4", ...
FROM "表格2";
以上的语法是最基本的。这整句 SQL 也可以含有 WHERE、 GROUP BY、及 HAVING 等子句,以及表格连接及别名等等。
举例来说,若我们想要将 1998 年的营业额资料放入 Store_Information 表格,而我们知道资料的来源是可以由 Sales_Information 表格取得的话,那我们就可以打入以下的 SQL:
SELECT store_name, Sales, Txn_Date
FROM Sales_Information
WHERE Year (Txn_Date) = 1998;
在这里,我用了 SQL Server 中的函数来由日期中找出年。不同的数据库会有不同的语法。举个例来说,在 Oracle 上,您将会使用 WHERE TO_CHAR (Txn_Date, 'yyyy') = 1998。
我们有时候可能会需要修改表格中的资料。在这个时候,我们就需要用到 UPDATE 指令。这个指令的语法是:
SET "栏位1" = [新值]
WHERE "条件";
最容易了解这个语法的方式是透过一个例子。假设我们有以下的表格:
Store_Information 表格
Store_Name | Sales | Txn_Date |
Los Angeles | 1500 | 05-Jan-1999 |
San Diego | 250 | 07-Jan-1999 |
Los Angeles | 300 | 08-Jan-1999 |
Boston | 700 | 08-Jan-1999 |
我们发现说 Los Angeles 在 08-Jan-1999 的营业额实际上是 $500,而不是表格中所储存的 $300,因此我们用以下的 SQL 来修改那一笔资料:
SET Sales = 500
WHERE Store_Name = 'Los Angeles'
AND Txn_Date = 'Jan-08-1999';
现在表格的内容变成:
Store_Information 表格
Store_Name | Sales | Txn_Date |
Los Angeles | 1500 | 05-Jan-1999 |
San Diego | 250 | 07-Jan-1999 |
Los Angeles | 500 | 08-Jan-1999 |
Boston | 700 | 08-Jan-1999 |
在这个例子中,只有一笔资料符合 WHERE 子句中的条件。如果有多笔资料符合条件的话,每一笔符合条件的资料都会被修改的。
我们也可以同时修改好几个栏位。这语法如下:
SET "栏位1" = [值1], "栏位2" = [值2]
WHERE "条件";
在某些情况下,我们会需要直接由数据库中去除一些资料。这可以藉由 DELETE FROM 指令来达成。它的语法是:
WHERE "条件";
以下我们用个实例说明。假设我们有以下这个表格:
Store_Information 表格
Store_Name | Sales | Txn_Date |
Los Angeles | 1500 | 05-Jan-1999 |
San Diego | 250 | 07-Jan-1999 |
Los Angeles | 300 | 08-Jan-1999 |
Boston | 700 | 08-Jan-1999 |
而我们需要将有关 Los Angeles 的资料全部去除。在这里我们可以用以下的 SQL 来达到这个目的:
WHERE Store_Name = 'Los Angeles';
现在表格的内容变成:
Store_Information 表格
Store_Name | Sales | Txn_Date |
San Diego | 250 | 07-Jan-1999 |
Boston | 700 | 08-Jan-1999 |