3. 数据库系统的用户接口以及SQL语言

DBMS的用户接口

Screenshot 2022-10-11 at 18.28.10.png

数据库管理系统作为一个系统软件一定要让用户会用,让用户去管理数据,那么它一定要提供一些访问接口,让用户通过这些接口来使用数据库。
这些接口包括:

  1. Query Languages(查询语言):基本每种数据库系统都会根据他采用的数据模型,向用户提供某种类型的查询语言,允许用户通过查询语言来访问系统。
    1)形式化的查询语言(Formal Query Language):SQL,有严格语法。
    2)表格式的查询语言(Tabular Query Language):用填表格的方式来表达要查什么。
    3)图形化查询语言(Graphic Query Language)
    4)受限的自然语言查询(Limited Natural Language Query Language):我要查年龄大于24岁的学生。
  2. GUI(图形化用户界面):访问并维护数据库数据。
  3. APIs:在应用程序中访问数据库(ODBC)
  4. Class Library:类裤(Java,C++)


    Screenshot 2022-10-11 at 18.28.25.png

关系型数据库的查询语言

Screenshot 2022-10-11 at 18.28.33.png

查询语言没有编程能力,不是图灵完备的。


Screenshot 2022-10-11 at 18.28.40.png

关系型数据库查询语言形式化基础:
数学化查询语言:关系代数&关系演算,SQL就是以关系演算为基础开发出来的。


Screenshot 2022-10-11 at 18.28.47.png

QL语言就是SELECT语句。
DML语言主要对数据库中的数据进行插入、删除、更改操作。

重要的术语和概念

Screenshot 2022-10-11 at 18.50.27.png
  1. Base table(基表):一个基表就是一个关系,物理存在于磁盘上。
  2. View(视图):虚表,不真实存储在数据库里的数据,是通过计算基表从而得出的结论。
  3. Data type supported(数据库产品支持的数据类型):短整型,长整型数据。
  4. NULL(空值):SQL中的一个关键字(保留字)。布尔表达式判断:True、False、NULL。
  5. UNIQUE:SQL中的一个关键字(保留字)。在数据库中创建一张表时,用UNIQUE说明,某属性不允许在其他表里重复。
  6. DEFAULT:SQL中的一个关键字(保留字)。为数据库中某张表的某个属性指定一个默认值。
  7. PRIMARY KEY:指定某个属性是主键。
  8. FOREIGN KEY:指定某个属性是外键。如果是外键的话,一定是引用了另一张表的主键。一旦某个属性被定义成外键,系统就会自动检查表格之间引用完整性的约束。
  9. CHECK:在表中定义约束条件,比如,东南大学的学生入学年龄必须大于15岁。


    Screenshot 2022-10-11 at 18.50.34.png

基本的SQL语句查询

Screenshot 2022-10-11 at 18.50.41.png

SELECT:target-list:查询的目标属性。加了[DISTINCT]的话,就要求系统消除结果中的重复元组;不加的话,不会删除重复元组。
FROM:relation list:这个查询所涉及到的表。
WHERE:qualification就是一个布尔表达式,表示查询结果应该满足的条件。


Screenshot 2022-10-11 at 20.15.17.png

在系统内部一条查询是如何执行的

Screenshot 2022-10-11 at 18.50.47.png
  1. 做FROM子句里牵扯到的所有表的笛卡尔乘积,把所有表都拼起来,拼成一张大表。
  2. 利用WHERE子句里布尔表达式的条件,在第一部中得到的大表中去做筛选,把不满足查询要求的元组剔除掉。
  3. 根据SELECT子句中的target list中我要查的那几个属性,把第二步中筛选出的元组中我不要的投影掉,我要的留下来。
  4. 如果我加了DISTINCT,系统还会把重复元组去掉;如果没加,第三步就是最终结果了。

实例1

Screenshot 2022-10-11 at 20.30.15.png

S.sname:大写S:给Sailor这张表起的‘别名’。
S.sid=R.sid:Sailors和Reserves两张表要按sid做连接。
R.bid=103:
因为Reserve这张表没有水手name的信息,为了获得预定了103这条船的水手的name,我必须让Sailors这张表和Reserves这张表做一个连接操作,通过它们之间的sid相等,就可以找到S.sname。

引入别名(Range Variable)

Screenshot 2022-10-11 at 20.30.22.png

Sailors表的range variable是S,Reserves表的range variable是R。
sid两张表都有,所以需要“表名.关系”,避免引起混淆;像bid和sname只存在在其中一张表中,可以不写别名。但是建议都写。

实例2

Screenshot 2022-10-11 at 20.30.28.png

查找至少预定过一条船的水手:把Reserves这张表投影到sid这个属性上,只要是在Reserves表上出现,则一定预定过船只。
如果是sid加不加DISTINCT没有影响;如果是name加不加DISTINCT有影响,因为存在重名情况。
一般SELECT S.sid, S.sname是最保险的。

实例3

Screenshot 2022-10-11 at 20.30.34.png

在SELECT子句中还可以使用一些表达式。
LIKE:模糊查询。
:匹配一个字符。
%:匹配零个或多个字符。
LIKE 'B
%B':查找水手的姓名是像,以大写字母B开头,后面跟‘一个到多个’字符,以大写字母B结尾。
age1=S.age-5:满足条件的水手的年龄减5得到的这个结果属性起个名字,叫age1。
2S.age AS age2:和 age2=2S.age表示同样的意义。

实例4

Screenshot 2022-10-11 at 21.11.42.png

预定过一艘红船‘或者’一艘绿船的水手编号。
如果单纯想得到sid,不需要查询Sailors这张表,但是这里有一个悬念,加上Sailors表还是有用的。
等价于,所有预定过红船的水手编号 UNION 所有预定过绿船的水手编号。
sql用EXCEPT表示集合的差。如果把UNION换成EXCEPT,得到的结果就是,订过红船,但没有订过绿船的水手的编号。

实例5

Screenshot 2022-10-11 at 21.11.51.png

预定过一艘红船‘和’一艘绿船的水手编号。
不能简单的把(B.color='red' OR B.color='green')里的OR换成AND,这样是错误的,因为如果换成AND,语句意思是“一艘又绿又红的船”,没有这种红配绿的船。
先让Reserves这张表做自连接,这样每一个水手的订船信息就会出现两次。
Sailors S, Boats B1, Reserves R1, Boats B2, Reserves R2:把Boats和Reserves这两张表分别用两次,做他们的自连接。
(B1.color='red' AND B2.color='green'):如果一个水手同时订过红船和绿船,那么他的订船信息就会出现两次,如果出现这样一条元组的话,就找到了对应水手。(一个超长的元组中,前面某列显示它订过红船,后面某列显示它订过绿船)


Screenshot 2022-10-11 at 21.44.55.png

INTERSECT:集合的交。参与运算的关系必须满足并兼容条件。

非关联嵌套查询

Screenshot 2022-10-11 at 21.11.59.png

订过103号船的水手的姓名。
(非关联)嵌套子查询:对Reserves表做选择,把所有订过103号水手的信息找出来做投影,投影到sid上,得到一个结果集合。与外面查询无关联。
外面这个查询:对Sailors这张表做扫描,看水手编号是否在“订过103号船的水手编号集合”里,在,就对了,不在,就不理他。

关联嵌套查询

Screenshot 2022-10-11 at 22.00.42.png

嵌套的子查询条件与外层相关。
如果某个水手预定过103这条船,那么在Reserves表里一定‘存在’一套元组的bid等于103,sid等于水手sid。把Sailors表中每个水手的sid都代入子查询循环查询,S.sid=R.sid了就说明我查到了。
SELECT *:返回所有列,星号是通配符。查找所有属性。
在Reserves表中,NOT EXIST(不存在)另外一条元组,他的sid和目标水手的sid相同,bid是103,他的日期与目标水手的订船日期相等。


Screenshot 2022-10-11 at 22.00.49.png

查找只有一个水手预定过的船。
Reserves表自连接有R1和R2。
SELECT bid
FROM Reserves R2
WHERE R2.sid != R1.sid:所有除R1.sid之外的其他水手订的船的编号集合。
WHERE bid NOT IN:如果这个水手所定的bid不在其他水手所定的bid集合中,则这条船就他一个人订。

比较运算符

Screenshot 2022-10-11 at 22.00.55.png

op可以是大于,小于,等于,大于等于,小于等于,不等于
以大于为例:
op ANY:某属性的值要比集合中随便一个值大就行。
op ALL:某属性的值要比集合中所有的值都大。

IN

Screenshot 2022-10-11 at 22.01.04.png

S.sid=R.sid AND R.bid=B.bid AND B.color='red':把预订了红船的水手的sid找到。
S2.sid=R2.sid AND R2.bid=B2.bid AND B2.color='green':把预订了绿船的水手的sid找到。
S.sid IN:看订了red的水手编号在不在订了green的水手编号集合中。如果在,说明这个水手red green两条船都订了。
What about INTERSECT query?:不能简单替换S.sid by S.sname,会有重名。

除法

Screenshot 2022-10-11 at 22.01.09.png

查找“所有”。
不存在一条船是我没订过的。
SELECT B.bid
FROM Boats B:所有的船。
SELECT R.bid
FROM Reserves R
WHERE R.sid=S.sid:这个水手预定过的船。
所有的船 EXCEPT 这个水手预定过的船 = 这个水手没订过的船
如果不存在这个水手没订过的船,则,这个水手所有船都订过。

如果考试不允许用EXCEPT, 那么:


Screenshot 2022-10-12 at 17.52.44.png

如果在Reserves表中查不到“这个水手没订的船”,则这个水手订了所有船。

聚集函数运算

Screenshot 2022-10-12 at 17.52.50.png

COUNT (*):统计在一个关系中有多少条元组
COUNT ([DISTINCT] A):统计属性A中有多少个值(多少行非空元组)。加DISTINCT则统计属性A有多少个不同的值。
SUM ([DISTINCT] A):把所有元组的属性A的值做求和。加DISTINCT则求和属性A所有不同的值。
AVG ( [DISTINCT] A):对属性A求平均值。加DISTINCT则求属性A所有不同的值的平均值。
MAX (A):求属性A中的最大值。
MIN (A):求属性A中的最小值。


Screenshot 2022-10-12 at 17.53.04.png

1(左上). 问Sailors这张表中元组的个数。
2(右上). 有好几个水手都叫Bob,所有叫Bob的水手的不同的级别的个数。
3(左中). 查找所有级别为10的水手的平均年龄。
4(右中).查找所有级别为10的水手的不同年龄值的平均值。
5(左下).查找级别最高的水手的姓名。


Screenshot 2022-10-12 at 17.53.29.png
  1. SELECT S.sname, MAX (S.age)
    FROM Sailors S:错误的。因为MAX (S.age)计算结果是一个单个的值,姓名与max age无法对应。
  2. SELECT S.sname, S.age
    FROM Sailors S
    WHERE S.age =
    (SELECT MAX (S2.age)
    FROM Sailors S2):正确的。
  3. 换位置也是正确的。

分组聚集函数计算

Screenshot 2022-10-12 at 17.53.53.png

因为SQL不具备编程功能,所以我们不能for loop。那怎么办呢?


Screenshot 2022-10-12 at 17.54.11.png

GROUP BY grouping-list:把WHERE得到的符合条件的元组,按grouping-list里的那些属性的值做分组。属性值相等的元组被分到一组。
然后对每一个组,按照SELECT子句里规定的属性和聚集函数做运算,得到一条结果;即,每一个group,产生一条结果元组。
要求SELECT中出现的attribute,必须对每个group只有一个值,否则会error。
HAVING group-qualification:与WHERE相似,对经过GROUP BY分组后的每个group再做检查,把不满足条件的group去掉。


Screenshot 2022-10-12 at 18.38.39.png

对FROM中出现的表做笛卡尔乘积,把他们拼接起来,然后再按WHERE中的qualification做筛选,不满足条件的元组抛弃掉,然后按分组属性值相等的原则对经过筛选的元组做分组,再按HAVING中group-qualification的条件对每个group做检查,筛选合格group,最后按SELECT中的要求去做相应的计算,每一个group得到一条结果元组。
要求SELECT和HAVING中出现的每一个attribute对分组得到的每个group来讲,它的值必须是单一的。
在SELECT和HAVING中出现的attribute必须是分组属性集grouping-list.的一个子集。

要求SELECT和HAVING中出现的每一个attribute对分组得到的每个group来讲,它值必须是单一的。

Screenshot 2022-10-12 at 18.38.47.png

对所有年龄大于18岁的水手,查每一个水手人数至少为2的年龄级别组中,年龄大于十八岁的水手中最年轻的水手。


Screenshot 2022-10-12 at 18.38.57.png
Screenshot 2022-10-12 at 18.39.05.png
Screenshot 2022-10-12 at 18.39.12.png

查每一条红船的预定人数是多少。
先连接Reserves表和Boats表,拼起来以后就得到了所有船的预定情况的详细信息,然后做筛选,把所有红颜色船的订单情况筛选出来,按照bid做分组,这样同一条船的订船情况就在一个group中,此时对每一个group做COUNT(*),这样我就知道,每一条船有多少人订过它。
如果在WHERE不做筛选,转而用HAVING对group做筛选:逻辑没问题,但是数据库会报错。
SELECT B.bid, COUNT(*) AS scount
FROM Boats B, Reserves R
WHERE R.bid=B.bid
GROUP BY B.bid
HAVING B.color=‘red’:这就是为什么要求,SELECT和HAVING中出现的每个属性,都必须是GROUP BY属性集合的子集。(B.color不是B.bid的子集)
硬用的话怎么办:
SELECT B.bid, COUNT(*) AS scount
FROM Boats B, Reserves R
WHERE R.bid=B.bid
GROUP BY B.bid, B.color=‘red’ (补上)
HAVING B.color=‘red’:在本例中,每条船跟每条船的颜色都是单一的不同的,所以按编号分按颜色分其实不会改变分组情况。
或者这样写:
SELECT B.bid, COUNT(*) AS scount
FROM Boats B, Reserves R
WHERE R.bid=B.bid
GROUP BY B.bid
HAVING B.bid IN (SELECT bid
FROM Boats B
WHERE B.color=‘red’) 也是对的。


image.png

从S2重新开始的原因是S.age>18已经把不满足条件的元组排除掉了,S表不完整了。


image.png

按级别分组,查找平均年龄最小的级别是哪一个。
分组聚集函数运算不能嵌套使用。Temp就是一张临时表。
在FROM中出现嵌套查询:
Screenshot 2022-10-12 at 20.47.18.png

Null Values

Screenshot 2022-10-12 at 20.52.04.png

空值就是不知道,不是零。
引入空值之后带来的一些问题:
在查询时,需要在WHERE和HAVING的布尔表达式中引入一些特殊的运算符来表达对Null value的判断。


Screenshot 2022-10-12 at 21.13.20.png
Screenshot 2022-10-12 at 21.13.40.png
Screenshot 2022-10-12 at 20.52.10.png
Screenshot 2022-10-12 at 20.52.18.png
Screenshot 2022-10-12 at 20.52.28.png
Screenshot 2022-10-12 at 20.52.47.png
Screenshot 2022-10-12 at 20.52.55.png
Screenshot 2022-10-12 at 20.53.02.png
Screenshot 2022-10-12 at 20.53.10.png

扩充的就不学了,拜拜。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354

推荐阅读更多精彩内容