zoukankan      html  css  js  c++  java
  • sql 语言

    3.1  SQL语言的动词

    SQL 功 

    动   

    SELECT

    CREATE,DROP,ALTER

    INSERT,UPDATE

    DELETE

    GRANT,REVOKE

    基本表的定义、删除与修改

    一、定义基本表

    CREATE TABLE <表名>

          (<列名> <数据类型>[ <列级完整性约束条件> ]

          [,<列名> <数据类型>[ <列级完整性约束条件>] ] …

          [,<表级完整性约束条件> ] );

       如果完整性约束条件涉及到该表的多个属性列,则必须定义在表级上,否则既可以定义在列级也可以定义在表级。

    学生表Student

    建立“学生”表Student,学号是主码,姓名取值唯一。

        

        CREATE TABLE Student         

                 (Sno   CHAR(9) PRIMARY KEY, /* 列级完整性约束条件*/                 

                Sname  CHAR(20) UNIQUE,     /* Sname取唯一值*/

                Ssex    CHAR(2),

                Sage   SMALLINT,

                Sdept  CHAR(20)

               );

    课程表Course

    [例6] 建立一个“课程”表Course

          CREATE TABLE  Course

                   ( Cno       CHAR(4) PRIMARY KEY,

                     Cname  CHAR(40),           

                     Cpno     CHAR(4) ,                                            

                     Ccredit  SMALLINT,

                    FOREIGN KEY (Cpno) REFERENCES  Course(Cno)

                );

    学生选课表SC

    [例7]  建立一个“学生选课”表SC

           CREATE TABLE  SC

             (Sno  CHAR(9),

             Cno  CHAR(4), 

             Grade    SMALLINT,

             PRIMARY KEY (Sno,Cno), 

                         /* 主码由两个属性构成,必须作为表级完整性进行定义*/

             FOREIGN KEY (Sno) REFERENCES Student(Sno),

                        /* 表级完整性约束条件,Sno是外码,被参照表是Student */

             FOREIGN KEY (Cno) REFERENCES Course(Cno)

                       /* 表级完整性约束条件, Cno是外码,被参照表是Course*/

                  );

    数据类型

    数据类型

    含义

    CHAR(n)

    长度为n的定长字符串

    VARCHAR(n)

    最大长度为n的变长字符串

    INT

    长整数(也可以写作INTEGER

    SMALLINT

    短整数

    NUMERIC(p,d)

    定点数,由p位数字(不包括符号、小数点)组成,小数后面有d位数字

    REAL

    取决于机器精度的浮点数

    Double Precision

    取决于机器精度的双精度浮点数

    FLOAT(n)

    浮点数,精度至少为n位数字

    DATE

    日期,包含年、月、日,格式为YYYY-MM-DD

    TIME

    时间,包含一日的时、分、秒,格式为HH:MM:SS

    二、修改基本表

    ALTER TABLE <表名>

    [ ADD <新列名> <数据类型> [ 完整性约束 ] ]

    [ DROP <完整性约束名> ]

    [ ALTER COLUMN<列名> <数据类型> ];

    [例8]向Student表增加“入学时间”列,其数据类型为日期型。

         ALTER TABLE Student ADD S_entrance DATE;

    ¡      不论基本表中原来是否已有数据,新增加的列一律为空值。 

    [例9]将年龄的数据类型由字符型(假设原来的数据类型是字符型)改为整数。

                      ALTER TABLE Student ALTER COLUMN Sage INT;

    [例10]增加课程名称必须取唯一值的约束条件。

                      ALTER TABLE Course ADD UNIQUE(Cname);

    三、删除基本表

           DROP TABLE <表名>[RESTRICT| CASCADE];

    n       RESTRICT:删除表是有限制的。

    • 欲删除的基本表不能被其他表的约束所引用
    • 如果存在依赖该表的对象,则此表不能被删除

    n       CASCADE:删除该表没有限制。

    • 在删除基本表的同时,相关的依赖对象一起删除

    [例11]  删除Student表

         DROP TABLE  Student  CASCADE ;

    n       基本表定义被删除,数据被删除

    n       表上建立的索引、视图、触发器等一般也将被删除

    [例12]若表上建有视图,选择RESTRICT时表不能删除  

        CREATE VIEW IS_Student     

           AS

               SELECT Sno,Sname,Sage

               FROM  Student

                   WHERE Sdept='IS';

    DROP TABLE Student RESTRICT;  

              --ERROR: cannot drop table Student because other

                                objects depend on it

          

    [例12]如果选择CASCADE时可以删除表,视图也自动被删除

    DROP TABLE Student CASCADE;         

     --NOTICE: drop cascades to view IS_Student

    SELECT * FROM IS_Student;

    --ERROR: relation " IS_Student " does not exist

    索引的建立与删除

    l       建立索引的目的:加快查询速度

    l       谁可以建立索引

    ¡      DBA 或 表的属主(即建立表的人)

    ¡      DBMS一般会自动建立以下列上的索引

     PRIMARY  KEY

     UNIQUE、

    索引

    l       谁维护索引DBMS自动完成 

    l       使用索引 DBMS自动选择是否使用索引以及使用哪些索引

    l       RDBMS中索引一般采用B+树、HASH索引来实现

    n       B+树索引具有动态平衡的优点

    n       HASH索引具有查找速度快的特点

    l       采用B+树,还是HASH索引 则由具体的RDBMS来决定

    l       索引是关系数据库的内部实现技术,属于内模式的范畴

    l       CREATE INDEX语句定义索引时,可以定义索引是唯一索引、非唯一索引或聚簇索引

    一、建立索引

    l       语句格式

    CREATE [UNIQUE] [CLUSTER] INDEX <索引名>

    ON <表名>(<列名>[<次序>][,<列名>[<次序>] ]…);   

    [例13] CREATE CLUSTER INDEX Stusname

               ON   Student(Sname);

    ¡      在Student表的Sname(姓名)列上建立一个聚簇索引

    l       在最经常查询的列上建立聚簇索引以提高查询效率

    l       一个基本表上最多只能建立一个聚簇索引

    l       经常更新的列不宜建立聚簇索引

    [例14]为学生-课程数据库中的Student,Course,SC三个表建  立索引。

        

    CREATE UNIQUE INDEX  Stusno ON Student(Sno);

    CREATE UNIQUE INDEX  Coucno ON Course(Cno);

    CREATE UNIQUE INDEX  SCno ON SC(Sno ASC,Cno DESC);

        

          Student表按学号升序建唯一索引

          Course表按课程号升序建唯一索引

          SC表按学号升序和课程号降序建唯一索引

    二、删除索引

    l       DROP INDEX <索引名>;

    删除索引时,系统会从数据字典中删去有关该索引的

    描述。

    [例15]  删除Student表的Stusname索引

              DROP INDEX Stusname;

    数据查询

    l       语句格式

           SELECT [ALL|DISTINCT] <目标列表达式>

                                                    [,<目标列表达式>] …

    FROM <表名或视图名>[, <表名或视图名> ] …

    [ WHERE <条件表达式> ]

    [ GROUP BY <列名1> [ HAVING <条件表达式> ] ]

    [ ORDER BY <列名2> [ ASC|DESC ] ];

    3.4.1 单表查询

    一、 选择表中的若干列

    1.查询指定列

           [例1]  查询全体学生的学号与姓名。

                  SELECT Sno,Sname

                  FROM Student; 

           [例2]  查询全体学生的姓名、学号、所在系。

                  SELECT Sname,Sno,Sdept

                  FROM Student;

    2. 查询全部列

    l       选出所有属性列:

    n       在SELECT关键字后面列出所有列名

    n       将<目标列表达式>指定为 *

    [例3]  查询全体学生的详细记录。

    SELECT  Sno,Sname,Ssex,Sage,Sdept

    FROM Student;

       或

    SELECT  *

    FROM Student;

    3. 查询经过计算的值

    l       SELECT子句的<目标列表达式>可以为:

    ¡      算术表达式

    ¡      字符串常量

    ¡      函数

    ¡      列别名

    [例4]  查全体学生的姓名及其出生年份。

    SELECT Sname,2004-Sage    /*假定当年的年份为2004年*/

    FROM Student;

    输出结果:

                  Sname   2004-Sage

                

                   李勇      1984

                   刘晨      1985

                   王敏      1986

                   张立      1985

    例5]  查询全体学生的姓名、出生年份和所有系,要求用小写字母表示所有系名

    SELECT Sname,‘Year of Birth: ',2004-Sage,

                     ISLOWER(Sdept)

    FROM Student;

    输出结果:

     Sname   'Year of Birth:'  2004-Sage   ISLOWER(Sdept)

          李勇    Year of Birth:    1984              cs

          刘晨    Year of Birth:    1985              is

          王敏    Year of Birth:    1986              ma

          张立    Year of Birth:    1985      is

    使用列别名改变查询结果的列标题:

          

         SELECT Sname NAME,'Year of Birth: ’  BIRTH,

           2000-Sage  BIRTHDAY,LOWER(Sdept)  DEPARTMENT

           FROM Student;

    输出结果:

        NAME      BIRTH         BIRTHDAY   DEPARTMENT

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

         李勇    Year of Birth:    1984             cs

         刘晨    Year of Birth:    1985             is

         王敏    Year of Birth:    1986             ma

         张立    Year of Birth:    1985             is

    二、选择表中的若干元组

    1. 消除取值重复的行

            如果没有指定DISTINCT关键词,则缺省为ALL

    [例6]  查询选修了课程的学生学号。

        SELECT Sno   FROM SC;

           等价于:

           SELECT ALL  Sno  FROM SC;

           执行上面的SELECT语句后,结果为:

                                           Sno

                                       200215121

                                       200215121

                                       200215121

                                       200215122

                                       200215122

    1. 指定DISTINCT关键词,去掉表中重复的行

      

       SELECT DISTINCT Sno

        FROM SC;

        执行结果:

                                           Sno

                                       200215121

                                       200215122

    表3.4 常用的查询条件

    查 询 条 件

    谓    词

    比    较

    =,>,<,>=,<=,!=,<>,!>,!<;NOT+上述比较运算符

    确定范围

    BETWEEN AND,NOT BETWEEN AND

    确定集合

    IN,NOT IN

    字符匹配

    LIKE,NOT LIKE

    空    值

    IS NULL,IS NOT NULL

    多重条件(逻辑运算)

    AND,OR,NOT

    (1) 比较大小

    [例7]  查询计算机科学系全体学生的名单。

        SELECT Sname

        FROM Student

        WHERE Sdept=‘CS’;

    [例8]  查询所有年龄在20岁以下的学生姓名及其年龄。

         SELECT Sname,Sage

    FROM    Student   

    WHERE Sage < 20;

    [例9]  查询考试成绩有不及格的学生的学号。

        SELECT DISTINCT Sno

        FROM  SC

        WHERE Grade<60;

    (2)确定范围

    l       谓词:   BETWEEN …  AND  …

                     NOT BETWEEN  …  AND  …

    [例10] 查询年龄在20~23岁(包括20岁和23岁)之间的学生的

                 姓名、系别和年龄

          SELECT Sname,Sdept,Sage

    FROM     Student

    WHERE   Sage BETWEEN 20 AND 23;

    [例11]  查询年龄不在20~23岁之间的学生姓名、系别和年龄

                  SELECT Sname,Sdept,Sage

                  FROM    Student

                  WHERE Sage NOT BETWEEN 20 AND 23;

    (3) 确定集合

    l       谓词:IN <值表>,  NOT IN <值表>         

    [例12]查询信息系(IS)、数学系(MA)和计算机科学系(CS)学生的姓名和性别。

           SELECT Sname,Ssex

           FROM  Student

           WHERE Sdept IN ( 'IS','MA','CS' );

    [例13]查询既不是信息系、数学系,也不是计算机科学系的学生的姓名和性别。

    SELECT Sname,Ssex

    FROM Student

            WHERE Sdept NOT IN ( 'IS','MA','CS' );

    (4)字符匹配

    l       谓词: [NOT] LIKE  ‘<匹配串>’  [ESCAPE ‘ <换码字符>’]

    1)      匹配串为固定字符串

    [例14]  查询学号为200215121的学生的详细情况。

         SELECT *   

         FROM  Student 

         WHERE  Sno LIKE ‘200215121';

    等价于:

          SELECT  *

          FROM  Student

          WHERE Sno = ' 200215121 ';

    2) 匹配串为含通配符的字符串

    [例15]  查询所有姓刘学生的姓名、学号和性别。

          SELECT Sname,Sno,Ssex

          FROM Student

          WHERE  Sname LIKE ‘刘%’;

    [例16]  查询姓"欧阳"且全名为三个汉字的学生的姓名。

          SELECT Sname

          FROM   Student

          WHERE  Sname LIKE '欧阳__';

    [例17]  查询名字中第2个字为"阳"字的学生的姓名和学号。

          SELECT Sname,Sno

          FROM Student

          WHERE Sname LIKE ‘__阳%’;

    [例18]  查询所有不姓刘的学生姓名。

          SELECT Sname,Sno,Ssex

          FROM Student

          WHERE Sname NOT LIKE '刘%';

    3) 使用换码字符将通配符转义为普通字符

     [例19]  查询DB_Design课程的课程号和学分。

          SELECT Cno,Ccredit

          FROM Course

          WHERE Cname LIKE 'DB\_Design' ESCAPE '‘;

    [例20]  查询以"DB_"开头,且倒数第3个字符为 i的课程的详细情况。

          SELECT  *

          FROM   Course

          WHERE  Cname LIKE  'DB\_%i_ _' ESCAPE ' ‘;

     ESCAPE '\' 表示“ \” 为换码字符

    (5) 涉及空值的查询

    n       谓词: IS NULL 或 IS NOT NULL

    n        “IS” 不能用 “=” 代替

          [例21]  某些学生选修课程后没有参加考试,所以有选课记录,但没

          有考试成绩。查询缺少成绩的学生的学号和相应的课程号。

            SELECT Sno,Cno

          FROM  SC

          WHERE  Grade IS NULL

    [例22]  查所有有成绩的学生学号和课程号。

          SELECT Sno,Cno

          FROM  SC

          WHERE  Grade IS NOT NULL;

    (6) 多重条件查询

    l       逻辑运算符:AND和 OR来联结多个查询条件

    l        AND的优先级高于OR

    l        可以用括号改变优先级

    l       可用来实现多种其他谓词

    l        [NOT] IN

    l        [NOT] BETWEEN …   AND  …

    [例23]  查询计算机系年龄在20岁以下的学生姓名。

         SELECT Sname

           FROM  Student

           WHERE Sdept= 'CS' AND Sage<20

    l       改写[例12]

    [例12]  查询信息系(IS)、数学系(MA)和计算机科学系(CS)学生的姓名和性别。

    SELECT Sname,Ssex

    FROM Student

    WHERE Sdept IN ( 'IS','MA','CS' )

    可改写为:

    SELECT Sname,Ssex

    FROM   Student

    WHERE  Sdept= ' IS ' OR Sdept= ' MA' OR Sdept= ' CS ';

    三、ORDER BY子句

    l       ORDER BY子句

    n       可以按一个或多个属性列排序

    n       升序:ASC;降序:DESC;缺省值为升序

    l       当排序列含空值时

    n       ASC:排序列为空值的元组最后显示

    n       DESC:排序列为空值的元组最先显示

    [例24]  查询选修了3号课程的学生的学号及其成绩,查询结果按分数降序排列。

            SELECT Sno,Grade

            FROM  SC

            WHERE  Cno= ' 3 '

            ORDER BY Grade DESC;

    [例25]  查询全体学生情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列。

            SELECT  *

            FROM  Student

            ORDER BY Sdept,Sage DESC; 

    四、聚集函数

    l       聚集函数:

    ¡      计数

    COUNT([DISTINCT|ALL] *)

    COUNT([DISTINCT|ALL] <列名>)

    ¡      计算总和

    SUM([DISTINCT|ALL] <列名>)   

    ¡       计算平均值

    AVG([DISTINCT|ALL] <列名>)

    ¡      最大最小值

           MAX([DISTINCT|ALL] <列名>)

            MIN([DISTINCT|ALL] <列名>)

    [例26]  查询学生总人数。

        SELECT COUNT(*)

        FROM  Student; 

         [例27]  查询选修了课程的学生人数。

         SELECT COUNT(DISTINCT Sno)

         FROM SC;

         [例28]  计算1号课程的学生平均成绩。

              SELECT AVG(Grade)

              FROM SC

              WHERE Cno= ' 1 ';

    五、GROUP BY子句

    l       GROUP BY子句分组:

         细化聚集函数的作用对象

    ¡       未对查询结果分组,聚集函数将作用于整个查询结果

    ¡       对查询结果分组后,聚集函数将分别作用于每个组

    ¡      作用对象是查询的中间结果表

    ¡      按指定的一列或多列值分组,值相等的为一组

    [例31]  求各个课程号及相应的选课人数。

         SELECT Cno,COUNT(Sno)

         FROM    SC

         GROUP BY Cno; 

         查询结果:

               Cno        COUNT(Sno)

                        1             22

                      2             34

                     3             44

                        4             33

                    5             48

    [例32]  查询选修了3门以上课程的学生学号。

         SELECT Sno

         FROM  SC

         GROUP BY Sno

         HAVING  COUNT(*) >3;     

    l       HAVING短语与WHERE子句的区别:

    ¡      作用对象不同

    ¡      WHERE子句作用于基表或视图,从中选择满足条件的元组

    ¡      HAVING短语作用于组,从中选择满足条件的组。

    3.4.2 连接查询

    l       连接查询:同时涉及多个表的查询

    l       连接条件或连接谓词:用来连接两个表的条件

            一般格式:

    n       [<表名1>.]<列名1>  <比较运算符>  [<表名2>.]<列名2>

    n       [<表名1>.]<列名1> BETWEEN [<表名2>.]<列名2> AND [<表名2>.]<列名3>

    l       连接字段:连接谓词中的列名称

    n       连接条件中的各连接字段类型必须是可比的,但名字不必是相同的

    n       嵌套循环法(NESTED-LOOP)

    n       首先在表1中找到第一个元组,然后从头开始扫描表2,逐一查找满足连接件的元组,找到后就将表1中的第一个元组与该元组拼接起来,形成结果表中一个元组。

    n       表2全部查找完后,再找表1中第二个元组,然后再从头开始扫描表2,逐一查找满足连接条件的元组,找到后就将表1中的第二个元组与该元组拼接起来,形成结果表中一个元组。

    n       重复上述操作,直到表1中的全部元组都处理完毕

    常用于=连接

    ¡      首先按连接属性对表1和表2排序

    ¡      对表1的第一个元组,从头开始扫描表2,顺序查找满足连接条件的元组,找到后就将表1中的第一个元组与该元组拼接起来,形成结果表中一个元组。当遇到表2中第一条大于表1连接字段值的元组时,对表2的查询不再继续

    ¡      找到表1的第二条元组,然后从刚才的中断点处继续顺序扫描表2,查找满足连接条件的元组,找到后就将表1中的第一个元组与该元组拼接起来,形成结果表中一个元组。直接遇到表2中大于表1连接字段值的元组时,对表2的查询不再继续

    ¡      重复上述操作,直到表1或表2中的全部元组都处理完毕为止

    ¡      对表2按连接字段建立索引

    ¡      对表1中的每个元组,依次根据其连接字段值查询表2的索引,从中找到满足条件的元组,找到后就将表1中的第一个元组与该元组拼接起来,形成结果表中一个元组

    一、等值与非等值连接查询

    l       等值连接:连接运算符为=

    [例33]  查询每个学生及其选修课程的情况

                            SELECT  Student.*,SC.*

                                 FROM     Student,SC

                         WHERE  Student.Sno = SC.Sno;

    查询结果:

    Student.Sno

    Sname

    Ssex

    Sage

    Sdept

    SC.Sno

    Cno

    Grade

    200215121

    李勇

    20

    CS

    200215121

    1

    92

    200215121

    李勇

    20

    CS

    200215121

    2

    85

    200215121

    李勇

    20

    CS

    200215121

    3

    88

    200215122

    刘晨

    19

    CS

    200215122

    2

    90

    200215122

    刘晨

    19

    CS

    200215122

    3

    80

    l       自然连接:

    [例34]     对[例33]用自然连接完成。

     SELECT  Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade

     FROM     Student,SC

     WHERE  Student.Sno = SC.Sno;

    二、自身连接

    l       自身连接:一个表与其自己进行连接

    l       需要给表起别名以示区别

    l       由于所有属性名都是同名属性,因此必须使用别名前缀

    [例35]查询每一门课的间接先修课(即先修课的先修课)

        SELECT  FIRST.Cno,SECOND.Cpno

         FROM  Course  FIRST,Course  SECOND

         WHERE FIRST.Cpno = SECOND.Cno;

    FIRST表(Course表)

    Cno

    Cname

    Cpno

    Ccredit

    1

    数据库

    5

    4

    2

    数学

     

    2

    3

    信息系统

    1

    4

    4

    操作系统

    6

    3

    5

    数据结构

    7

    4

    6

    数据处理

     

    2

    7

    PASCAL语言

    6

    4

     

    SECOND表(Course表)

    Cno

    Cname

    Cpno

    Ccredit

    1

    数据库

    5

    4

    2

    数学

     

    2

    3

    信息系统

    1

    4

    4

    操作系统

    6

    3

    5

    数据结构

    7

    4

    6

    数据处理

     

    2

    7

    PASCAL语言

    6

    4

    查询结果:

    Cno

    Pcno

    1

    7

    3

    5

    5

    6

    三、外连接

    l       外连接与普通连接的区别

    ¡      普通连接操作只输出满足连接条件的元组

    ¡      外连接操作以指定表为连接主体,将主体表中不满足连接条件的元组一并输出

    [例 36] 改写[例33]

       SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade

        FROM  Student  LEFT OUT JOIN SC ON (Student.Sno=SC.Sno);

       

    Student.Sno

    Sname

    Ssex

    Sage

    Sdept

    Cno

    Grade

    200215121

    李勇

    20

    CS

    1

    92

    200215121

    李勇

    20

    CS

    2

    85

    200215121

    李勇

    20

    CS

    3

    88

    200215122

    刘晨

    19

    CS

    2

    90

    200215122

    刘晨

    19

    CS

    3

    80

    200215123

    王敏

    18

    MA

    NULL

    NULL

    200215125

    张立

    19

    IS

    NULL

    NULL

    l       左外连接

    ¡      列出左边关系(如本例Student)中所有的元组

    l        右外连接

    ¡      列出右边关系中所有的元组

    四、复合条件连接

    l       复合条件连接:WHERE子句中含多个连接条件

    [例37]查询选修2号课程且成绩在90分以上的所有学生            

                  SELECT Student.Sno, Sname

                  FROM    Student, SC

                  WHERE Student.Sno = SC.Sno AND  

                                                /* 连接谓词*/

                             SC.Cno= ‘2’ AND SC.Grade > 90;      

                                       /* 其他限定条件 */

    [例38]查询每个学生的学号、姓名、选修的课程名及成绩

      SELECT Student.Sno,Sname,Cname,Grade

       FROM    Student,SC,Course    /*多表连接*/

       WHERE Student.Sno = SC.Sno

                       and SC.Cno = Course.Cno;

    3.4.3 嵌套查询

    SELECT Sname                              /*外层查询/父查询*/

         FROM Student

         WHERE Sno IN

                            (SELECT Sno             /*内层查询/子查询*/

                                FROM SC

                                WHERE Cno= ' 2 ');

    ¡      子查询的限制

    • 不能使用ORDER BY子句

    ¡      层层嵌套方式反映了 SQL语言的结构化

    ¡      有些嵌套查询可以用连接运算替代

    l       不相关子查询:

        子查询的查询条件不依赖于父查询

    n       由里向外 逐层处理。即每个子查询在上一级查询处理之前求解,子查询的结果用于建立其父查询的查找条件。

    n       相关子查询:子查询的查询条件依赖于父查询

    n       首先取外层查询中表的第一个元组,根据它与内层查询相关的属性值处理内层查询,若WHERE子句返回值为真,则取此元组放入结果表

    n       然后再取外层表的下一个元组

    n       重复这一过程,直至外层表全部检查完为止

    一、带有IN谓词的子查询

    [例39]  查询与“刘晨”在同一个系学习的学生。

             此查询要求可以分步来完成

        ① 确定“刘晨”所在系名            

            SELECT  Sdept 

             FROM     Student                           

             WHERE  Sname= ' 刘晨 ';

               结果为: CS

    ② 查找所有在IS系学习的学生。   

            SELECT   Sno,Sname,Sdept    

            FROM      Student                

            WHERE  Sdept= ' CS ';

    结果为:

    Sno

    Sname

    Sdept

    200215121

    李勇

    CS

    200215122

    刘晨

    CS

    将第一步查询嵌入到第二步查询的条件中

        SELECT Sno,Sname,Sdept

               FROM Student

         WHERE Sdept  IN

                      (SELECT Sdept

                       FROM Student

                       WHERE Sname= ‘ 刘晨 ’);

        此查询为不相关子查询。

    用自身连接完成[例39]查询要求

         SELECT  S1.Sno,S1.Sname,S1.Sdept

          FROM     Student S1,Student S2

          WHERE  S1.Sdept = S2.Sdept  AND

                          S2.Sname = '刘晨';

    [例40]查询选修了课程名为“信息系统”的学生学号和姓名

          SELECT Sno,Sname                       ③ 最后在Student关系中

          FROM    Student                                     取出Sno和Sname

          WHERE Sno  IN

                 (SELECT Sno                              ② 然后在SC关系中找出选

                  FROM    SC                                    修了3号课程的学生学号

                  WHERE  Cno IN

                         (SELECT Cno                       ① 首先在Course关系中找出

                           FROM Course                      “信息系统”的课程号,为3号

                           WHERE Cname= ‘信息系统’

                         )

                  );

    用连接查询实现[例40]

         SELECT Sno,Sname

          FROM    Student,SC,Course

          WHERE Student.Sno = SC.Sno  AND

                         SC.Cno = Course.Cno AND

                         Course.Cname=‘信息系统’;

    二、 带有比较运算符的子查询

    l       当能确切知道内层查询返回单值时,可用比较运算符(>,<,=,>=,<=,!=或< >)。

    l       与ANY或ALL谓词配合使用

    例:假设一个学生只可能在一个系学习,并且必须属于一个系,则在[例39]可以用 = 代替IN :

         SELECT Sno,Sname,Sdept

         FROM    Student

         WHERE Sdept   =

                       (SELECT Sdept

                        FROM    Student

                        WHERE Sname= ‘刘晨’);

    子查询一定要跟在比较符之后

        错误的例子:

         SELECT  Sno,Sname,Sdept

         FROM     Student

         WHERE ( SELECT Sdept

                           FROM Student

                           WHERE Sname= ‘ 刘晨 ’ )

                           = Sdept;

    [例41]找出每个学生超过他选修课程平均成绩的课程号。

       SELECT Sno, Cno

        FROM  SC  x

        WHERE Grade >=(SELECT AVG(Grade)

                                          FROM  SC y

                                       WHERE y.Sno=x.Sno);

    l       可能的执行过程:

    1. 从外层查询中取出SC的一个元组x,将元组x的Sno值(200215121)传送给内层查询。

           SELECT AVG(Grade)

           FROM SC y

           WHERE y.Sno='200215121';

    2. 执行内层查询,得到值88(近似值),用该值代替内层查询,得到外层查询:

           SELECT Sno, Cno

           FROM  SC x

           WHERE Grade >=88;

    3. 执行这个查询,得到

        (200215121,1)

        (200215121,3)

    4.外层查询取出下一个元组重复做上述1至3步骤,直到外层的SC元组全部处理完毕。结果为:

        (200215121,1)

        (200215121,3)

        (200215122,2)

    三、 带有ANY(SOME)或ALL谓词的子查询

    谓词语义

    ¡      ANY:任意一个值

    ¡      ALL:所有值

    需要配合使用比较运算符

    > ANY    大于子查询结果中的某个值      

     > ALL   大于子查询结果中的所有值

    < ANY    小于子查询结果中的某个值   

    < ALL     小于子查询结果中的所有值

    >= ANY  大于等于子查询结果中的某个值   

    >= ALL   大于等于子查询结果中的所有值

    <= ANY  小于等于子查询结果中的某个值   

    <= ALL   小于等于子查询结果中的所有值

    = ANY    等于子查询结果中的某个值       

    =ALL      等于子查询结果中的所有值(通常没有实际意义)

    !=(或<>)ANY   不等于子查询结果中的某个值

    !=(或<>)ALL    不等于子查询结果中的任何一个值

    例42]  查询其他系中比计算机科学某一学生年龄小的学生姓名和年龄

        SELECT Sname,Sage

        FROM    Student

        WHERE Sage < ANY (SELECT  Sage

                                             FROM    Student

                                             WHERE Sdept= ' CS ')

               AND Sdept <> ‘CS ' ;           /*父查询块中的条件 */

    结果:

    Sname

    Sage

    王敏

    18

    张立

    19

    执行过程:

       1.RDBMS执行此查询时,首先处理子查询,找出

          CS系中所有学生的年龄,构成一个集合(20,19)

       2. 处理父查询,找所有不是CS系且年龄小于

            20 或 19的学生

    用聚集函数实现[例42]

        SELECT Sname,Sage

         FROM   Student

         WHERE Sage <

                                 (SELECT MAX(Sage)

                                   FROM Student

                                   WHERE Sdept= ‘CS ')

               AND Sdept <> ' CS ’;

    [例43]  查询其他系中比计算机科学系所有学生年龄都小的学生姓名及年龄。

    方法一:用ALL谓词

        SELECT Sname,Sage

        FROM Student

        WHERE Sage < ALL

                               (SELECT Sage

                                FROM Student

                                WHERE Sdept= ' CS ')

               AND Sdept <> ' CS ’;

    方法二:用聚集函数

            SELECT Sname,Sage

            FROM Student

            WHERE Sage <

                                   (SELECT MIN(Sage)

                                    FROM Student

                                    WHERE Sdept= ' CS ')

                  AND Sdept <>' CS ’;

    表3.5 ANY(或SOME),ALL谓词与聚集函数、IN谓词的等价转换关系

     

    =

    <>或!=

    <=

    >=

    ANY

    IN

        --

    <MAX

    <=MAX

    >MIN

    >= MIN

    ALL

    --

     NOT IN

    <MIN

    <= MIN

    >MAX

    >= MAX

      四、 带有EXISTS谓词的子查询

    l       1. EXISTS谓词

    n       存在量词$

    n       带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”。

    • 若内层查询结果非空,则外层的WHERE子句返回真值
    • 若内层查询结果为空,则外层的WHERE子句返回假值

    n       由EXISTS引出的子查询,其目标列表达式通常都用* ,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义

    l       2. NOT EXISTS谓词

    • 若内层查询结果非空,则外层的WHERE子句返回假值
    • 若内层查询结果为空,则外层的WHERE子句返回真值

    [例44]查询所有选修了1号课程的学生姓名。

    思路分析:

    n       本查询涉及Student和SC关系

    n       在Student中依次取每个元组的Sno值,用此值去检查SC关系

    n       若SC中存在这样的元组,其Sno值等于此Student.Sno值,并且其Cno= '1',则取此Student.Sname送入结果关系

    n       用嵌套查询

         SELECT Sname

         FROM Student

         WHERE EXISTS

                       (SELECT *

                        FROM SC

                        WHERE Sno=Student.Sno AND Cno= ' 1 ');

    n       用连接运算

           SELECT Sname

           FROM Student, SC

           WHERE Student.Sno=SC.Sno AND SC.Cno= '1';

    例45]  查询没有选修1号课程的学生姓名。

         SELECT Sname

         FROM Student

         WHERE NOT EXISTS

                       (SELECT *

                        FROM SC

                        WHERE Sno = Student.Sno AND Cno='1');

    l       不同形式的查询间的替换

    n       一些带EXISTS或NOT EXISTS谓词的子查询不能被其他形式的子查询等价替换

    n       所有带IN谓词、比较运算符、ANY和ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换

    l        用EXISTS/NOT EXISTS实现全称量词(难点)

    SQL语言中没有全称量词" (For all)

    可以把带有全称量词的谓词转换为等价的带有存在量词的谓词:

            ("x)P ≡ Ø ($ x(Ø P))

       

    例:[例39]查询与“刘晨”在同一个系学习的学生。

           可以用带EXISTS谓词的子查询替换:

         SELECT Sno,Sname,Sdept

         FROM Student S1

          WHERE EXISTS

                     (SELECT *

                         FROM Student S2

                         WHERE S2.Sdept = S1.Sdept AND

                                       S2.Sname = ‘刘晨’);

    [例46] 查询选修了全部课程的学生姓名。

            SELECT Sname

            FROM Student

            WHERE NOT EXISTS

                        (SELECT *

                            FROM Course

                            WHERE NOT EXISTS

                                          (SELECT *

                                           FROM SC

                                           WHERE Sno= Student.Sno

                                                 AND Cno= Course.Cno

                                           )

                           );

    用EXISTS/NOT EXISTS实现逻辑蕴函(难点)

    ¡      SQL语言中没有蕴函(Implication)逻辑运算

    ¡      可以利用谓词演算将逻辑蕴函谓词等价转换为:

                       p ® q ≡ Ø p∨q

    [例47]查询至少选修了学生200215122选修的全部课程的学生号码。

    解题思路:

    n       用逻辑蕴函表达:查询学号为x的学生,对所有的课程y,只要200215122学生选修了课程y,则x也选修了y。

    n       形式化表示:

           用P表示谓词 “学生200215122选修了课程y”

           用q表示谓词 “学生x选修了课程y”

    n       用NOT EXISTS谓词表示:    

         SELECT DISTINCT Sno

           FROM SC SCX

           WHERE NOT EXISTS

                         (SELECT *

                          FROM SC SCY

                          WHERE SCY.Sno = ' 200215122 '  AND

                                        NOT EXISTS

                                        (SELECT *

                                         FROM SC SCZ

                                         WHERE SCZ.Sno=SCX.Sno AND

                                                       SCZ.Cno=SCY.Cno));

    3.4.4 集合查询

    l       集合操作的种类

    ¡      并操作UNION

    ¡      交操作INTERSECT

    ¡      差操作EXCEPT

    l       参加集合操作的各查询结果的列数必须相同;对应项的数据类型也必须相同

    [例48]  查询计算机科学系的学生及年龄不大于19岁的学生。

    方法一:

            SELECT *

            FROM Student

            WHERE Sdept= 'CS'

            UNION

            SELECT *

            FROM Student

            WHERE Sage<=19;

    n       UNION:将多个查询结果合并起来时,系统自动去掉重复元组。

    n       UNION ALL:将多个查询结果合并起来时,保留重复元组

    方法二:

           SELECT  DISTINCT  *

            FROM Student

            WHERE Sdept= 'CS'  OR  Sage<=19;

    [例49]  查询选修了课程1或者选修了课程2的学生。

            SELECT Sno

            FROM SC

            WHERE Cno=' 1 '

            UNION

            SELECT Sno

            FROM SC

            WHERE Cno= ' 2 ';

    [例50]  查询计算机科学系的学生与年龄不大于19岁的学生的交集

    SELECT *

    FROM Student

    WHERE Sdept='CS'

    INTERSECT

    SELECT *

    FROM Student

    WHERE Sage<=19

    l       [例50] 实际上就是查询计算机科学系中年龄不大于19岁的学生

                  SELECT *

                   FROM Student

                   WHERE Sdept= 'CS' AND  Sage<=19;

    [例51]  查询选修课程1的学生集合与选修课程2的学生集合的交集

         SELECT Sno

        FROM SC

        WHERE Cno=' 1 '

        INTERSECT

        SELECT Sno

        FROM SC

        WHERE Cno='2 ';

    [例51]实际上是查询既选修了课程1又选修了课程2     的学生

            SELECT Sno

              FROM SC

              WHERE Cno=' 1 ' AND Sno IN

                                                    (SELECT Sno

                                                     FROM SC

                                                     WHERE Cno=' 2 ');

    [例52]  查询计算机科学系的学生与年龄不大于19岁的学生的差集。

        SELECT *

        FROM Student

        WHERE Sdept='CS'

        EXCEPT

        SELECT  *

        FROM Student

        WHERE Sage <=19;

       [例52]实际上是查询计算机科学系中年龄大于19岁的学生

            SELECT *

            FROM Student

            WHERE Sdept= 'CS' AND  Sage>19;

    3.4.5 Select语句的一般形式

    SELECT [ALL|DISTINCT] 

       <目标列表达式> [别名] [ ,<目标列表达式> [别名]] …

     FROM     <表名或视图名> [别名]

                 [ ,<表名或视图名> [别名]] …

     [WHERE <条件表达式>]

     [GROUP BY <列名1>

     [HAVING     <条件表达式>]]

     [ORDER BY <列名2> [ASC|DESC]

    数据更新

    3.5.1          插入数据

    l       两种插入数据方式

    1. 插入元组

    2. 插入子查询结果

    • 可以一次插入多个元组

    一、插入元组

    l       语句格式

           INSERT

           INTO <表名> [(<属性列1>[,<属性列2 >…)]

           VALUES (<常量1> [,<常量2>]    …           )

    l       功能

    n       将新元组插入指定表中

    l       INTO子句

    n       属性列的顺序可与表定义中的顺序不一致

    n       没有指定属性列

    n       指定部分属性列

    l        VALUES子句

    n        提供的值必须与INTO子句匹配

    l       值的个数

    l       值的类型

    [例1]  将一个新学生元组(学号:200215128;姓名:陈冬;性别:男;所在系:IS;年龄:18岁)插入到Student表中。

        INSERT

        INTO  Student (Sno,Sname,Ssex,Sdept,Sage)

        VALUES ('200215128','陈冬','男','IS',18);

    [例2]  将学生张成民的信息插入到Student表中。

               INSERT

                      INTO  Student

                      VALUES (‘200215126’, ‘张成民’, ‘男’,18,'CS');

    [例3]  插入一条选课记录( '200215128','1 ')。

        INSERT

        INTO SC(Sno,Cno)

        VALUES (‘ 200215128 ’,‘ 1 ’);

       RDBMS将在新插入记录的Grade列上自动地赋空值。

       或者:

        INSERT

        INTO SC

        VALUES (' 200215128 ',' 1 ',NULL);

    二、插入子查询结果

    l       语句格式

        INSERT

        INTO <表名>  [(<属性列1> [,<属性列2>…  )]

        子查询;

    l       功能

        将子查询结果插入指定表中

    l       INTO子句(与插入元组类似)

    l        子查询

    n       SELECT子句目标列必须与INTO子句匹配

    • 值的个数
    • 值的类型

    [例4]  对每一个系,求学生的平均年龄,并把结果存入数据库。

    第一步:建表

          CREATE  TABLE  Dept_age

              (Sdept  CHAR(15)                /* 系名*/

               Avg_age SMALLINT);      /*学生平均年龄*/

    第二步:插入数据

            INSERT

             INTO  Dept_age(Sdept,Avg_age)

                  SELECT  Sdept,AVG(Sage)

                  FROM  Student

                  GROUP BY Sdept;

    RDBMS在执行插入语句时会检查所插元组是

    否破坏表上已定义的完整性规则

    ¡      实体完整性

    ¡      参照完整性

    ¡      用户定义的完整性

    • NOT NULL约束
    • UNIQUE约束
    • 值域约束

    3.5.2          修改数据

    l       语句格式

       UPDATE  <表名>

        SET  <列名>=<表达式>[,<列名>=<表达式>]…

        [WHERE <条件>];

    l       功能

    n       修改指定表中满足WHERE子句条件的元组

    n       SET子句

    l       指定修改方式

    l       要修改的列

    l       修改后取值

    n       WHERE子句

    l       指定要修改的元组

    l       缺省表示要修改表中的所有元组

    l       三种修改方式

    1. 修改某一个元组的值

    2. 修改多个元组的值

    3. 带子查询的修改语句

    [例5]  将学生200215121的年龄改为22岁

             UPDATE  Student

             SET Sage=22

             WHERE  Sno=' 200215121 ';

    [例6]  将所有学生的年龄增加1岁

             UPDATE Student

             SET Sage= Sage+1;

    [例7]  将计算机科学系全体学生的成绩置零。

            UPDATE SC

            SET  Grade=0

            WHERE  'CS'=

                           (SELETE Sdept

                            FROM  Student

                            WHERE  Student.Sno = SC.Sno);

    3.5.3          删除数据

    l       语句格式

           DELETE

           FROM     <表名>

           [WHERE <条件>];

    l       功能

    n       删除指定表中满足WHERE子句条件的元组

    l       WHERE子句

    n       指定要删除的元组

    n       缺省表示要删除表中的全部元组,表的定义仍在字典中

    l       三种删除方式

    1. 删除某一个元组的值

    2. 删除多个元组的值

    3. 带子查询的删除语句

    [例8]  删除学号为200215128的学生记录。

            DELETE

             FROM Student

             WHERE Sno= 200215128 ';

    [例9]  删除所有的学生选课记录。

            DELETE

            FROM SC;

    [例10]  删除计算机科学系所有学生的选课记录。

            DELETE

            FROM SC

            WHERE  'CS'=

                               (SELETE Sdept

                                FROM Student

                                WHERE Student.Sno=SC.Sno);

    视图

    视图的特点

    l       虚表,是从一个或几个基本表(或视图)导出的表

    l       只存放视图的定义,不存放视图对应的数据

    l       基表中的数据发生变化,从视图中查询出的数据也随之改变

    基于视图的操作

    l        查询

    l        删除

    l        受限更新

    l        定义基于该视图的新视图

    定义视图

    l       建立视图

    l       删除视图

    一、建立视图

    l       语句格式

           CREATE  VIEW

                 <视图名>  [(<列名>  [,<列名>]…)]

           AS  <子查询>

           [WITH  CHECK  OPTION];

    l       组成视图的属性列名:全部省略或全部指定

    l       子查询不允许含有ORDER BY子句和DISTINCT短语

    l       RDBMS执行CREATE VIEW语句时只是把视图定义存入数据字典,并不执行其中的SELECT语句。

    l       在对视图查询时,按视图的定义从基本表中将数据查出。

    [例1]  建立信息系学生的视图。

            CREATE VIEW IS_Student

            AS

            SELECT Sno,Sname,Sage

            FROM    Student

            WHERE  Sdept= 'IS';

    [例2]建立信息系学生的视图,并要求进行修改和插入操作时仍需保证该视图只有信息系的学生 。

            CREATE VIEW IS_Student

            AS

            SELECT Sno,Sname,Sage

            FROM  Student

            WHERE  Sdept= 'IS'

            WITH CHECK OPTION;

    对IS_Student视图的更新操作:

    l       修改操作:自动加上Sdept= 'IS'的条件

    l       删除操作:自动加上Sdept= 'IS'的条件

    l       插入操作:自动检查Sdept属性值是否为'IS'

    ¡      如果不是,则拒绝该插入操作

    ¡      如果没有提供Sdept属性值,则自动定义Sdept为'IS'

    l       基于多个基表的视图

    [例3]  建立信息系选修了1号课程的学生视图。

            CREATE VIEW IS_S1(Sno,Sname,Grade)

            AS

            SELECT Student.Sno,Sname,Grade

            FROM  Student,SC

            WHERE  Sdept= 'IS' AND

                           Student.Sno=SC.Sno AND

                           SC.Cno= '1';

    l       基于视图的视图

           [例4]  建立信息系选修了1号课程且成绩在90分以上的学生的视图。

            CREATE VIEW IS_S2

            AS

            SELECT Sno,Sname,Grade

            FROM  IS_S1

            WHERE  Grade>=90;

    l       带表达式的视图

    [例5]  定义一个反映学生出生年份的视图。

            CREATE  VIEW BT_S(Sno,Sname,Sbirth)

            AS

            SELECT Sno,Sname,2000-Sage

            FROM  Student;

    l       分组视图

    [例6]  将学生的学号及他的平均成绩定义为一个视图

                  假设SC表中“成绩”列Grade为数字型

                CREAT  VIEW S_G(Sno,Gavg)

                 AS 

                 SELECT Sno,AVG(Grade)

                 FROM  SC

                 GROUP BY Sno;

    l       不指定属性列

    [例7]将Student表中所有女生记录定义为一个视图

          CREATE VIEW F_Student(F_Sno,name,sex,age,dept)

          AS

          SELECT *

          FROM  Student

          WHERE Ssex=‘女’;

         缺点:

         修改基表Student的结构后,Student表与F_Student视图的映象关系被破坏,导致该视图不能正确工作。

    二、删除视图

    l       语句的格式:

                  DROP  VIEW  <视图名>;

    ¡      该语句从数据字典中删除指定的视图定义

    ¡      如果该视图上还导出了其他视图,使用CASCADE级联删除语句,把该视图和由它导出的所有视图一起删除

    ¡      删除基表时,由该基表导出的所有视图定义都必须显式地使用DROP VIEW语句删除

    [例8]  删除视图BT_S: DROP VIEW BT_S;

                  

                  删除视图IS_S1:DROP VIEW IS_S1;

    • 拒绝执行
    • 级联删除:

                         DROP VIEW IS_S1 CASCADE;      

    查询视图

    l       用户角度:查询视图与查询基本表相同

    l       RDBMS实现视图查询的方法

    ¡      视图消解法(View Resolution)

    l       进行有效性检查

    l       转换成等价的对基本表的查询

    l       执行修正后的查询

    [例9]  在信息系学生的视图中找出年龄小于20岁的学生。

            SELECT   Sno,Sage

            FROM      IS_Student

            WHERE   Sage<20;

    IS_Student视图的定义 (参见视图定义例1)

    视图消解转换后的查询语句为:

     SELECT  Sno,Sage      

     FROM  Student

     WHERE  Sdept= 'IS'  AND  Sage<20;

    [例10]  查询选修了1号课程的信息系学生

    SELECT  IS_Student.Sno,Sname

    FROM     IS_Student,SC

    WHERE  IS_Student.Sno =SC.Sno AND SC.Cno= '1';

    l       视图消解法的局限

    ¡      有些情况下,视图消解法不能生成正确查询。

    [例11]在S_G视图中查询平均成绩在90分以上的学生学号和平均成绩

    SELECT *

    FROM   S_G

    WHERE  Gavg>=90;

           S_G视图的子查询定义:

             CREATE VIEW S_G (Sno,Gavg)

             AS

    SELECT  Sno,AVG(Grade)

    FROM  SC

    GROUP BY Sno;

    错误:

    SELECT Sno,AVG(Grade)

    FROM     SC

    WHERE  AVG(Grade)>=90

    GROUP BY Sno;

    正确:

    SELECT  Sno,AVG(Grade)

    FROM  SC

    GROUP BY Sno

    HAVING AVG(Grade)>=90;

    更新视图

    [例12]  将信息系学生视图IS_Student中学号200215122的学生姓名改为“刘辰”。

    UPDATE  IS_Student

    SET  Sname= '刘辰'

    WHERE  Sno= ' 200215122 ';

    转换后的语句:

    UPDATE  Student

    SET Sname= '刘辰'

    WHERE Sno= ' 200215122 ' AND Sdept= 'IS';

    [例13]  向信息系学生视图IS_S中插入一个新的学生记录:200215129,赵新,20岁

    INSERT

    INTO IS_Student

    VALUES(‘95029’,‘赵新’,20);

    转换为对基本表的更新:

    INSERT

    INTO   Student(Sno,Sname,Sage,Sdept)

    VALUES(‘200215129 ','赵新',20,'IS' );

    [例14]删除信息系学生视图IS_Student中学号为200215129的记录

    DELETE

    FROM IS_Student

    WHERE Sno= ' 200215129 ';

    转换为对基本表的更新:

    DELETE

    FROM Student

    WHERE Sno= ' 200215129 ' AND Sdept= 'IS';

    l       更新视图的限制:一些视图是不可更新的,因为对这些视图的更新不能唯一地有意义地转换成对相应基本表的更新

    例:视图S_G为不可更新视图。

    UPDATE  S_G

    SET          Gavg=90

    WHERE  Sno= ‘200215121’;

    这个对视图的更新无法转换成对基本表SC的更新

    l       允许对行列子集视图进行更新

    l       对其他类型视图的更新不同系统有不同限制

    视图的作用

    l       1. 视图能够简化用户的操作

    l       2. 视图使用户能以多种角度看待同一数据

    l       3. 视图对重构数据库提供了一定程度的逻辑独立性

    l       4. 视图能够对机密数据提供安全保护

    l       5. 适当的利用视图可以更清晰的表达查询

  • 相关阅读:
    [Angular] How to show global loading spinner for application between page navigation
    [NgRx] NgRx Data Fetching Solution
    [NgRx] NgRx Entity Adapter Configuration
    [Javascript] Construct a Regex to Match Twitter Mentions with Regexr
    [Dart] Dynamic variable in Dart
    [NgRx] NgRx Runtime Checks
    [NgRx] Setting up NgRx Router Store and the Time-Travelling Debugger
    浅析数据库设计三范式
    建议别买三星Gear:半电脑产品 设计糟糕
    持续集成理论和实践的新进展
  • 原文地址:https://www.cnblogs.com/zhoug2020/p/3320515.html
Copyright © 2011-2022 走看看