当前位置: 首页 > 科技观察

图解SQL,这也太形象了吧!

时间:2023-03-20 01:44:53 科技观察

本文介绍了关系型数据库的设计思想:在SQL中,一切都是关系。计算中有很多伟大的设计理念和思想,例如:在Unix中,一切皆文件。在面向对象的编程语言中,一切都是对象。关系型数据库也有自己的设计理念:在SQL中,一切都是关系。关系模型关系模型(Relationalmodel)是由E.F.Codd博士在1970年提出的,基于集合论中关系的概念;现实世界中的实体对象和它们之间的联系都由关系来表示。我们在数据库系统中看到的关系是一个二维表(Table),它由行(Row)和列(Column)组成。因此,也可以说关系表是数据行的集合。关系模型由三部分组成:数据结构、关系操作和完整性约束。关系模型中的数据结构是关系表,包括基表、派生表(查询结果)和虚表(视图)。常用的关系操作包括增删改查(CRUD),使用SQL语言。其中查询操作最为复杂,包括Selection、Projection、Union、Intersection、Exception、Cartesianproduct。完整性约束用于维护数据完整性或满足业务约束的需要,包括实体完整性(主键约束)、参照完整性(外键约束)和用户自定义完整性(非空约束、唯一性约束、校验约束和默认值)。我们今天的主题是关系操作语言,或SQL。本文使用的示例数据来自https://blog.csdn.net/horses/article/details/86518676。面向集合的SQL(结构化查询语言)是操作关系数据库的标准语言。SQL非常接近英文,使用起来也非常简单。它在设计之初就考虑到了非技术人员的需求。通常,我们只需要解释想要的结果(What),而将数据处理过程(How)留给数据库管理系统。所以,SQL才是真正的人的编程语言!下面详细分析一下关系的各种操作语句;目的是让大家明白SQL是一种面向集合的编程语言,它的操作对象是集合,操作的结果也是一个集合。在关系数据库中,关系、表和集合通常表示相同的概念。下面的SELECT是一个简单的查询语句:SELECTEmployee_id,first_name,last_name,hire_dateFROMemployees;它的作用是从employees表中查询员工信息。显然,我们都知道FROM之后是一个表(关系,集合)。不仅如此,整个查询语句的结果也是一张表。因此,我们可以将上面的查询用作表:SELECT*FROM(SELECTemployee_id,first_name,last_name,hire_dateFROMemployees)t;括号中的查询语句称为派生表,我们给它起一个别名t。另外,整个查询结果是一张表;这意味着我们可以继续嵌套,尽管这样做很无聊。再看一个PostgreSQL的例子:--PostgreSQLSELECT*FROMupper('sql');|upper||--------||SQL|upper()是一个大写转换函数。它出现在FROM子句中,意味着它的结果也是一个表,只是一个1行1列的特殊表。SELECT子句用于指定要查询的字段,可以包含表达式、函数值等。SELECT在关系运算中被称为Projection,看下面的示意图应该更容易理解。除了SELECT,还有一些常用的SQL子句。WHERE用于指定数据过滤的条件,在关系操作中称为Selection。示意图如下:ORDERBY用于对查询结果进行排序。示意图如下:总之,SQL可以进行各种数据操作,如过滤、分组、排序、数量限制等;所有这些操作的对象都是关系表,结果也是关系表。在这些关系操作中,有一个比较特殊的,就是分组。GROUPBY分组(GROUPBY)操作不同于其他关系操作,因为它改变了关系的结构。看下面的例子:SELECTdepartment_id,count(*),first_nameFROMemployeesGROUPBYdepartment_id;这条语句的目的是按部门统计员工人数,但是存在语法错误,即查询列表中不能出现first_name。原因是如果按部门分组,每个部门包含多个员工;无法确定需要显示哪个员工的姓名是一个逻辑错误。因此,GROUPBY改变了集合元素(数据行)的结构,创建了一个全新的关系。分组操作示意图如下:尽管如此,GROUPBY的结果仍然是一个集合。UNIONSQL面向集合的特性最明显的体现就是UNION(并运算)、INTERSECT(交运算)和EXCEPT/MINUS(差运算)。这些集合运算符的作用是将两个集合合并为一个集合,因此需要满足以下条件:两边集合中字段的个数和顺序必须相同;两侧集合中对应字段的类型必须匹配或兼容。具体来说,UNION和UNIONALL用于计算两个集合的并集,返回出现在第一个查询结果或第二个查询结果中的数据。它们的区别在于,UNION排除了结果中的重复数据,而UNIONALL保留了重复数据。下面是UNION运算的示意图:INTERSECT运算符用于返回两个集合的公共部分,即第一次查询结果和第二次查询结果中同时出现的数据,而排除结果中的重复数据。INTERSECT运算示意图如下:EXCEPT或MINUS运算符用于返回两个集合的差集,即在第一个查询结果中出现而在第二个查询结果中没有出现的记录,排除重复的结果中的数据。EXCEPT运算符的示意图如下:另外,DISTINCT运算符用于消除重复数据,即排除集合中的重复元素。SQL中的关系概念来源于数学中的集合论,所以UNION、INTERSECT、EXCEPT分别来源于集合论中的并(∪\cup∪)、交(∩\cap∩)和差(?\setminus?)运算.需要注意的是,集合论中的集合是不允许重复数据的,而SQL是允许的。因此,SQL中的集合也称为multiset;集合论中的multisets和collections是无序的,但是SQL可以通过ORDERBY子句对查询结果进行排序。JOIN在SQL中,不仅实体对象存储在关系表中,对象之间的关系也存储在关系表中。因此,当我们想要获取这些相关数据时,就需要使用另外一个操作:连接查询(JOIN)。常见的SQL连接查询类型有内连接、外连接、交叉连接等,其中外连接又可分为左外连接、右外连接和全外连接。InnerJoin返回满足连接条件的两个表中的数据。innerjoin的原理如下图所示:LeftOuterJoin返回左表的所有数据;对于右表,返回满足连接条件数据的数据;如果没有则返回null。左外连接的原理如下图所示:右外连接(RightOuterJoin)返回右表的所有数据;对于左表,返回满足join条件的数据,不满足则返回null值。右外连接和左外连接可以互换,下面两个是等价的:t1RIGHTJOINt2t2LEFTJOINt1全外连接(FullOuterJoin)相当于左外连接加右外连接,返回左表和右表的所有数据同时;两个表中不满足连接条件的数据返回NULL。fullouterjoin的原理如下图所示:crossjoin也叫CartesianProduct。两张表的交叉连接相当于将一张表的所有行与另一张表的所有行进行合并,结果的个数为两个表的行数的乘积。交叉连接的原理如下图所示:其他类型的连接还有半连接(SEMIJOIN)和反连接(ANTIJOIN)。集合操作将两个集合合并为一个更大或更小的集合;连接查询将两个集合转换为更大或更小的集合,同时获得更大的元素(更多列)。在很多情况下,集合操作可以通过连接查询来实现,例如:SELECTdepartment_idFROMdepartmentsUNIONSELECTdepartment_idFROMemployees;相当于:SELECTCOALESCE(d.department_id,e.department_id)FROMdepartmentsdFULLJOINemployeesON(e.department_id=d.department_id);我们介绍了很多查询例子,接下来我们来看其他的数据操作。DMLDML代表DataManipulationLanguage,即插入、更新和删除。下面是一个插入语句的例子:CREATETABLEtest(idint);--MySQL,SQLServer,etc.INSERTINTOtest(id)VALUES(1),(2),(3);--OracleINSERTITOtest(id)(SELECT1ASidFROMDUALUNIONALLSELECT2FROMDUALUNIONALLSELECT3FROMDUAL);我们通过INSERT语句插入了3条记录,或者插入了包含3条记录的关系表。因为,UNIONALL返回一个关系表。VALUES也指定了一个关系表,在SQLServer和PostgreSQL中支持如下语句:SELECT*FROM(VALUES(1),(2),(3))test(id);前面我们说过,FROM之后是一张关系表,所以这里的VALUES是一样的。由于我们经常插入单条记录,所以没有意识到操作实际上是以表为单位进行的。同样,UPDATE和DELETE语句也是以关系表为单位的操作;只是我们习惯于更新一行数据或删除几条记录。