184.数据操纵语言DML

5.5 SQL的数据操纵功能

 

5.5.1 数据插入

  使用CREATE语句创建的数据表还只是一个“空壳”,表中没有任何数据。利用SQL语言提供的INSERT语句可以完成向数据表插入数据的任务。

INSERT语句的语法格式为:

INSERT [INTO] <table>[(<column1>[, <column2>…])]

VALUES(<value1>[, <value2>…]);

   table表示表名,value1, value2, …分别表示待插入的常量值,它们插入后形成同一条记录上的各个字段值,且value1与字段column1对应,value2与字段column2对应,等。

 

  关于插入语句INSERT,应注意以下几点:

  <table>后面字段的顺序和数量都可以是任意的(当然,字段的数量必须少于或等于表中定义字段的数量)。但是对给定顺序的字段列表,VALUES子句中的常量值要分别按位置与字段列表中的字段相对应,而且数据类型也要一致。每个常量值必须是具体的值,而不能没有值。注意,“没有值”不是空值(NULL),它们是两个不同的概念。

  如果<table>后面没有指定字段列表,那么待插入的常量值的顺序必须与表中定义字段的顺序一样。

  除了在定义表时被设置为NOT NULL以外,任何数据类型的字段都可以插入NULL(空值)。

 

【例5.25用INSERT语句将一个学号为“20170201”、姓名为“刘洋”、性别为“女”、出生日期为“1997-02-03”、专业为“计算 机应用技术”、平均成绩为“98.5”、系别为“计算机系”的学生记录插入数据表student中。

根据以上信息,构造以下的记录:('20170201','刘洋','女','1997-02-03','计算机应用技术',98.5,'计算机系')。该记录中各分量值(常量值)的顺序与表student中定义字段的顺序相同,且数据类型也分别相同,因此可以用不带字段列表的INSERT语句来实现数据插入。代码如下:

INSERT student(s_no,s_name,s_sex,s_birthday, s_speciality,s_avgrade,s_dept)

VALUES('20170201','刘洋','','1997-02-03','计算机应用技术',98.5,'计算机系');

该语句等价下列语句:

INSERT INTO student

VALUES('20170201','刘洋','','1997-02-03','计算机应用技术',98.5,'计算机系');

 

 

【例5.26用INSERT语句将一个学生记录('20170202','王晓珂', 88.1) 插入数据表student中。

由于该记录并不包含所有的字段值,故在该INSERT语句中必须显式指定字段列表(s_no, s_name, s_avgrade)。其实现代码如下:

INSERT INTO student(s_no,s_name,s_avgrade)

VALUES('20170202','王晓珂', 88.1); 

由于字段的顺序可以是任意的(但插入值要与字段对应),所以该语句等价与下列的INSERT语句:

INSERT  INTO student(s_name, s_avgrade, s_no)

VALUES('王晓珂', 88.1, '20170202');

 

 

  注意:主键字段的值不能重复,也不能为空值(NULL);

  被设为NOT NULL的字段的插入值也不能为空值。

  此外,对于其他没有被插入的字段(如字段s_sex, s_birthday等),如果在定义表时设置了默认值,则这些字段会自动填上默认值,否则自动填上空值。

  在执行上述语句后,新插入的记录在字段s_speciality和s_dept上分别取默认值“计算机软件与理论”和“计算机科学系”,在字段s_sex和s_birthday,上都取空值(NULL)。

  

 

  另一种插入数据的方法是在INSERT语句中嵌入子查询,以子查询的返回结果集作为插入的数据。这样可以实现数据的批量插入,这在许多地方都有应用。但要求子查询的返回结果集和被插入数据的数据表在结果上要一致,否则无法完成插入操作。

  例5.27】  查询表student中学生的学号、姓名、专业、平均成绩和系别,并将查询结果输入到另一个数据表中。

首先要创建一个包含学号(s_no)、姓名(s_name)、专业(s_speciality)、平均成绩(s_avgrade)和系别(s_dept)的数据表student2,相应CREATE语句如下:

CREATE TABLE student2(

s_no char(8) PRIMARY KEY,

s_name char(8) NOT NULL,

s_speciality varchar(50) DEFAULT  '计算机软件与理论',

s_avgrade numeric(3,1) CHECK(s_avgrade >= 0 AND s_avgrade <= 100),

s_dept varchar(50) DEFAULT  '计算机科学系'

); 

创建如下的查询:

SELECT s_no, s_name, s_speciality, s_avgrade, s_dept

FROM student;

 

 

  可以看出,上述查询返回的结果集在结构上与表student2是一致的。所以,该查询可以作为子查询嵌入到用于向表student1插入数据的INSERT语句中,从而实现将查询结果插入到表student2中的目的。相应的SQL语句如下:

INSERT INTO student2(s_no, s_name, s_speciality, s_avgrade, s_dept)

(SELECT s_no, s_name, s_speciality, s_avgrade, s_dept

FROM student); 

 

 

       注意:上述INSERT语句中并无关键字VALUES

 

 

 

5.5.2 数据更新

  在数据输入到数据表以后,或者是由于错误的输入,或者是由于应用环境和时间的变化等原因,都有可能需要对表中的数据进行修改。在SQL语句中,UPDATE语句提供了数据修改功能。其语法格式如下:

UPDATE <table>

SET <column1> = <value1>[,<column2> = <value2>…]

[WHERE <condition_expression>]

  <table>表示要修改数据的表;关键字SET后面的column1, column2, …表示要修改的字段,value1, value2, …对应字段修改后的新值;condition_expression为一逻辑表达式,此处表示修改条件。如果UPDATE语句不包含WHERE子句,则表示无条件对表中所有记录都进行修改(无条件修改);如果包含了WHERE子句,那么只有对满足修改条件的记录进行修改(有条件修改)。

 

 

【例5.28将所有学生的平均成绩都减5分。

这是一个无条件修改,相应语句如下:

UPDATE student

SET s_avgrade = s_avgrade - 5;

 

【例5.29将所有女学生的平均成绩都加上其原来分数的0.5%。

这是一个有条件修改,相应语句如下:

UPDATE student

SET s_avgrade = s_avgrade + s_avgrade*0.005

WHERE s_sex = '';

 

  在项目开发实践中,经常遇到这样的操作:用一个表去更新另外一个表。在这种更新操作中,有一些“技巧”需要注意。根据1.5.2节的分析,两个表(实体)之间的联系主要有三种情况:(1:1)、(1:n)和(m:n)。如果两个表之间的联系是(m:n),则不宜用任何一个表去更新另外一个表;如果它们的联系是(1:n),则可以用“1”对应的表去更新“n”对应的表;如果它们的联系是(1:1),则可以用其中任何一个表去更新另外一个表。

 

 

【例5.30用学分表credit去更新课程信息表SC2。

从SQL语法上看,用一个表去更新另一个表,相应语句并不复杂。但要保证其语义上的正确性,这并不容易。我们考虑这样的例子:学分表credit保存了每门课程的课程名和学分,课程信息表SC2则保存学生选修的课程信息,包括学号、课程名、成绩和学分。表credit和表SC2的定义代码如下:

CREATE TABLE credit(  -- 表credit

c_name varchar(20)  PRIMARY KEY,

c_credit    int

);

CREATE TABLE SC2(  -- 表SC2  

s_no char(8),

c_name varchar(20),

c_gradenumeric(3,1)       CHECK(c_grade >= 0 AND c_grade <= 100),

c_credit   int,

PRIMARY KEY(s_no, c_name)       --将(s_no, c_name)设为主键

);

然后在这两个表中添加相关数据:

INSERT credit VALUES('英语',3);

INSERT credit VALUES('数据库原理',4);

INSERT credit VALUES('算法设计与分析',2);

 

INSERT SC2(s_no,c_name,c_grade) VALUES('20170201','英语',80.2);

INSERT SC2(s_no,c_name,c_grade) VALUES('20170201','数据库原理',70.0);

INSERT SC2(s_no,c_name,c_grade) VALUES('20170201','算法设计与分析',92.4);

INSERT SC2(s_no,c_name,c_grade) VALUES('20170202','英语',81.9);

INSERT SC2(s_no,c_name,c_grade) VALUES('20170202','算法设计与分析',85.2);

INSERT SC2(s_no,c_name,c_grade) VALUES('20170203','多媒体技术',68.1);

注意,表SC2中的学分字段c_credit未添加任何数据。可以看到,表credit和表SC2之间的联系是基于课程名称c_name的(1:n)联系(“一对多”联系),即表credit中的一条数据可能对应着表SC2中的多条数据,因此可以用“1”对应的表c_credit去更新“n”对应的表SC2。具体地,可以用表credit中的学分字段c_credit去更新表SC2中的学分字段c_credit,相应UPDATE语句如下:

UPDATE  SC2

SET SC2.c_credit = credit.c_credit

FROM SC2

JOIN credit

ON SC2.c_name = credit.c_name

执行上述语句后,表SC2中的内容如下:

s_no c_namec_grade                c_credit

---------------------------------------------------------------------------------------------------

20170201数据库原理                      70.04

20170201算法设计与分析92.42

20170201英语80.23

20170202算法设计与分析85.22

20170202英语81.93

20170203多媒体技术     68.1NULL

可以看到,更新结果是正确的。

需要注意的是,在“一对多”的两个表中,必须保证是用“1”表去更新“n”表,否则极为可能出现问题。

  说明: 如果用表SC2去更新表credit,则会容易出现不一致性等问题。请读者考虑这个问题。

 

 

【例5.30假设表student中的平均成绩(s_avgrade)是由表SC中的课程成绩(c_grade)平均得到的,请通过查询表SC的方法来更新表student中的s_avgrade字段值,使之满足上述假设。    
        对于“刘洋”的平均成绩,它是《英语》、《数据库原理》和《算法设计与分析》这三门课程成绩的平均值(因为表SC显示“刘洋”选修了这三门课程),结果应该是(70.0+92.4+80.2)/3=80.9。对于其他学生的平均成绩亦可类推。

         但是用SQL语言求出各个学生的平均成绩并填到表student中,这不是一件很容易的事情。我们通过创建一个用于存放中间结果的数据表的方法来解决这个问题,相应的SQL语句及其说明如下:

(1) 创建一个用于存放中间结果的数据表tmp_table

CREATE TABLE tmp_table(

s_no char(8),  

s_avgrade numeric(3,1)  

);

(2)通过按学号分组的方法求各个学生的平均成绩,并将其学号和平均成绩存放到表tmp_table中

INSERT INTO tmp_table(s_no,s_avgrade)

(SELECT  s_no, AVG(c_grade) --c_grade

FROM SC

GROUP BY s_no);

 

表student和表tmp_table的联系是基于字段s_no的(1:1)联系。因此,我们可以用表tmp_table中的平均成绩字段s_avgrade去更新表student中的平均成绩字段s_avgrade:

UPDATE student

SET s_avgrade = b.s_avgrade

FROM  student as a

JOIN tmp_table as b

ON a.s_no = b.s_no

DROP TABLE tmp_table; -- 删除临时数据表

经过一次性执行以上代码后即可计算出各位学生的平均成绩。

 

 

5.5.3 数据删除

  一般来说,数据也有个生成、发展和淘汰的过程。随着时间的推移,在经过长期使用后有些数据必须予以淘汰。对数据库来说,淘汰就意味着删除。在SQL语言中,DELETE  语句提供了数据删除功能,其一般语法格式如下:

DELETE   

FROM <table>

[WHERE <condition_expression>];

    table表示要删除数据的表,condition_expression亦为一逻辑表达式,此处表示删除条件。如果DELETE语句不包含WHERE子句,则表示无条件删除表<table>中所有的数据(无条件删除);如果包含了WHERE子句,那么只删除那些满足删除条件的记录(有条件删除)。

 

【例5.31删除表student中的所有数据。

这是一个无条件删除,其实现语句如下:

DELETE FROM student;

 


【例5.32删除表student中没有选修任何课程的学生。

这是一个有条件删除。由于学生的选课信息保存在表SC中,所以这个删除操作要涉及到两个数据表。一个直观的想法是,只要一个学生的学号没有在表SC中出现,则表明该学生没有选修课程,应予以删除。因此,很容易想到使用子查询来实现:

DELETE   

FROM student

WHERE s_no NOT IN (

SELECT s_no

FROM SC);

 

 

 

 

posted @ 2019-06-20 23:26  Zander_Zhao  阅读(299)  评论(0编辑  收藏  举报