热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

简单十步让你全面理解SQL_MySQL

很多程序员认为SQL是一头难以驯服的野兽。它是为数不多的声明性语言之一,也因为这样,其展示了完全不同于其他的表现形式、命令式语言、 面向对象语言甚至函数式编程语言(虽然有些人觉得SQL 还是有些类似功能)。我每天都写SQL,我的开源软件JOOQ中也包含SQL。因此我觉得有必要为还在为此苦苦挣扎的你呈现SQL的优美!下面的教程面向于:
  很多程序员认为SQL是一头难以驯服的野兽。它是为数不多的声明性语言之一,也因为这样,其展示了完全不同于其他的表现形式、命令式语言、 面向对象语言甚至函数式编程语言(虽然有些人觉得SQL 还是有些类似功能)。

  我每天都写SQL,我的开源软件JOOQ中也包含SQL。因此我觉得有必要为还在为此苦苦挣扎的你呈现SQL的优美!下面的教程面向于:

  • 已经使用过但没有完全理解SQL的读者

  • 已经差不多了解SQL但从未真正考虑过它的语法的读者

  • 想要指导他人学习SQL的读者

  本教程将重点介绍SELECT 语句。其他 DML 语句将在另一个教程中在做介绍。接下来就是…

  1、SQL是声明性语言

  首先你需要思考的是,声明性。你唯一需要做的只是声明你想获得结果的性质,而不需要考虑你的计算机怎么算出这些结果的。

SELECT first_name, last_name FROM employees WHERE salary > 100000

  这很容易理解,你无须关心员工的身份记录从哪来,你只需要知道谁有着不错的薪水。

  从中我们学到了什么呢?

  那么如果它是这样的简单,会出现什么问题吗?问题就是我们大多数人会直观地认为这是命令式编程。如:“机器,做这,再做那,但在这之前,如果这和那都发生错误,那么会运行一个检测”。这包括在变量中存储临时的编写循环、迭代、调用函数,等等结果。

  把那些都忘了吧,想想怎么去声明,而不是怎么告诉机器去计算。

  2、SQL语法不是“有序的”

  常见的混乱源于一个简单的事实,SQL语法元素并不会按照它们的执行方式排序。语法顺序如下:

  • SELECT [DISTINCT]

  • FROM

  • WHERE

  • GROUP BY

  • HAVING

  • UNION

  • ORDER BY

  为简单起见,并没有列出所有SQL语句。这个语法顺序与逻辑顺序基本上不同,执行顺序如下:

  • FROM

  • WHERE

  • GROUP BY

  • HAVING

  • SELECT

  • DISTINCT

  • UNION

  • ORDER BY

  这有三点需要注意:

  1、第一句是FROM,而不是SELECT。首先是将数据从磁盘加载到内存中,以便对这些数据进行操作。

  2、SELECT是在其他大多数语句后执行,最重要的是,在FROM和GROUP BY之后。重要的是要理解当你觉得你可以从WHERE语句中引用你定义在SELECT语句当中的时候,。以下是不可行的:

SELECT A.x + A.y AS z

FROM A

WHERE z = 10 -- z is not available here!

  如果你想重用z,您有两种选择。要么重复表达式:

SELECT A.x + A.y AS z

FROM A

WHERE (A.x + A.y) = 10

  或者你使用派生表、公用表表达式或视图来避免代码重复。请参阅示例进一步的分析:

  3、在语法和逻辑顺序里,UNION都是放在ORDER BY之前,很多人认为每个UNION子查询都可以进行排序,但根据SQL标准和大多数的SQL方言,并不是真的可行。虽然一些方言允许子查询或派生表排序,但却不能保证这种顺序能在UNION操作后保留。

  需要注意的是,并不是所有的数据库都以相同的形式实现,例如规则2并不完全适用于MySQL,PostgreSQL,和SQLite上

  从中我们学到了什么呢?

  要时刻记住SQL语句的语法顺序和逻辑顺序来避免常见的错误。如果你能明白这其中的区别,就能明确知道为什么有些可以执行有些则不能。

  如果能设计一种在语法顺序上实际又体现了逻辑顺序的语言就更好了,因为它是在微软的LINQ上实现的。

  3、SQL是关于数据表引用的

  因为语法顺序和逻辑顺序的差异,大多数初学者可能会误认为SQL中列的值是第一重要的。其实并非如此,最重要的是数据表引用。

  该SQL标准定义了FROM语句,如下:

 ::= FROM  [ {  
}... ]

  ROM语句的"output"是所有表引用的结合程度组合表引用。让我们慢慢地消化这些。

FROM a, b

  上述产生一个a+b度的组合表引用,如果a有3列和b有5列,那么"输出表"将有8(3+5)列。

  包含在这个组合表引用的记录是交叉乘积/笛卡儿积的axb。换句话说,每一条a记录都会与每一条b记录相对应。如果a有3个记录和b有5条记录,然后上面的组合表引用将产生15条记录(3×5)。

  在WHERE语句筛选后,GROUP BY语句中"output"是"fed"/"piped",它已转成新的"output",我们会稍后再去处理。

  如果我们从关系代数/集合论的角度来看待这些东西,一个SQL表是一个关系或一组元素组合。每个SQL语句将改变一个或几个关系,来产生新的关系。

  从中我们学到了什么呢?

  一直从数据表引用角度去思考,有助于理解数据怎样通过你的sql语句流水作业的

  4、SQL数据表引用可以相当强大

  表引用是相当强大的东西。举个简单的例子,JOIN关键字其实不是SELECT语句的一部分,但却是"special"表引用的一部分。连接表,在SQL标准中有定义(简化的):

::=
| |

  如果我们又拿之前的例子来分析:

FROM a, b

  a可以作为一个连接表,如:

a1 JOIN a2 ON a1.id = a2.id

  这扩展到前一个表达式,我们会得到:

FROM a1 JOIN a2 ON a1.id = a2.id, b

  虽然结合了数据表引用语法与连接表语法的逗号分隔表让人很无语,但你肯定还会这样做的。结果,结合数据表引用将有a1+a2+b度。

  派生表甚至比连接表更强大,我们接下来将会说到。

  从中我们学到了什么呢?

  要时时刻刻考虑表引用,重要的是这不仅让你理解数据怎样通过你的sql语句流水作业的,它还将帮助你了解复杂表引用是如何构造的。

  而且,重要的是,了解JOIN是构造连接表的关键字。不是的SELECT语句的一部分。某些数据库允许JOIN在插入、更新、删除中使用。

  5、应使用SQL JOIN的表,而不是以逗号分隔表

  前面,我们已经看到这语句:

FROM a, b

  高级SQL开发人员可能会告诉你,最好不要使用逗号分隔的列表,并且一直完整的表达你的JOINs。这将有助于改进你的SQL语句的可读性从而防止错误出现。

  一个非常常见的错误是忘记某处连接谓词。思考以下内容:

FROM a, b, c, d, e, f, g, h

WHERE a.a1 = b.bx

AND a.a2 = c.c1

AND d.d1 = b.bc

-- etc...

  使用join来查询表的语法

  • 更安全,你可以把连接谓词与连接表放一起,从而防止错误。

  • 更富于表现力,你可以区分外部连接,内部连接,等等。

  从中我们学到了什么呢?

  使用JOIN,并且永远不在FROM语句中使用逗号分隔表引用。

  6、SQL的不同类型的连接操作

  连接操作基本上有五种

  • EQUI JOIN

  • SEMI JOIN

  • ANTI JOIN

  • CROSS JOIN

  • DIVISION

  这些术语通常用于关系代数。对上述概念,如果他们存在,SQL会使用不同的术语。让我们仔细看看:

  EQUI JOIN(同等连接)

  这是最常见的JOIN操作。它有两个子操作:

  • INNER JOIN(或者只是JOIN)

  • OUTER JOIN(可以再次拆分为LEFT, RIGHT,FULL OUTER JOIN)

  例子是其中的区别最好的解释:

-- This table reference contains authors and their books.

-- There is one record for each book and its author.

-- authors without books are NOT included

author JOIN book ON author.id = book.author_id



-- This table reference contains authors and their books

-- There is one record for each book and its author.

-- ... OR there is an "empty" record for authors without books

-- ("empty" meaning that all book columns are NULL)

author LEFT OUTER JOIN book ON author.id = book.author_id

  SEMI JOIN(半连接)

  这种关系的概念在SQL中用两种方式表达:使用IN谓词或使用EXISTS谓语。"Semi"是指在拉丁语中的"half"。这种类型的连接用于连接只有"half"的表引用。再次考虑上述加入的作者和书。让我们想象,我们想要作者/书的组合,但只是那些作者实际上也有书。然后我们可以这样写:

-- Using IN

FROM author

WHERE author.id IN (SELECT book.author_id FROM book)

-- Using EXISTS

FROM author

WHERE EXISTS (SELECT 1 FROM book WHERE book.author_id = author.id)

  虽然不能肯定你到底是更加喜欢IN还是EXISTS,而且也没有规则说明,但可以这样说:

  • IN往往比EXISTS更具可读性

  • EXISTS往往比IN更富表现力(如它更容易表达复杂的半连接)

  • 一般情况下性能上没有太大的差异,但,在某些数据库可能会有巨大的性能差异。

  因为INNER JOIN有可能只产生有书的作者,因为很多初学者可能认为他们可以使用DISTINCT删除重复项。他们认为他们可以表达一个像这样的半联接:

-- Find only those authors who also have books

SELECT DISTINCT first_name, last_name

FROM author

  这是非常不好的做法,原因有二:

  • 它非常慢,因为该数据库有很多数据加载到内存中,只是要再删除重复项。

  • 它不完全正确,即使在这个简单的示例中它产生了正确的结果。但是,一旦你JOIN更多的表引用,,你将很难从你的结果中正确删除重复项。

  更多的关于DISTINCT滥用的问题,可以访问这里的博客。

  ANTI JOIN(反连接)

  这个关系的概念跟半连接刚好相反。您可以简单地通过将 NOT 关键字添加到IN 或 EXISTS中生成它。在下例中,我们选择那些没有任何书籍的作者:

-- Using IN

FROM author

WHERE author.id NOT IN (SELECT book.author_id FROM book)



-- Using EXISTS

FROM author

WHERE NOT EXISTS (SELECT 1 FROM book WHERE book.author_id = author.id)

  同样的规则对性能、可读性、表现力都适用。然而,当使用NOT IN时对NULLs会有一个小警告,这个问题有点超出本教程范围。

  CROSS JOIN(交叉连接)

  结合第一个表中的内容和第二个表中的内容,引用两个join表交叉生成一个新的东西。我们之前已经看到,这可以在FROM语句中通过逗号分隔表引用来实现。在你确实需要的情况下,可以在SQL语句中明确地写一个CROSS JOIN。

-- Combine every author with every book

author CROSS JOIN book

  DIVISION(除法)

  关系分割就是一只真正的由自己亲自喂养的野兽。简而言之,如果JOIN是乘法,那么除法就是JOIN的反义词。在SQL中,除法关系难以表达清楚。由于这是一个初学者的教程,解释这个问题超出了我们的教程范围。当然如果你求知欲爆棚,那么就看这里,这里还有这里。

  从中我们学到了什么呢?

  让我们把前面讲到的内容再次牢记于心。SQL是表引用。连接表是相当复杂的表引用。但关系表述和SQL表述还是有点区别的,并不是所有的关系连接操作都是正规的SQL连接操作。对关系理论有一点实践与认识,你就可以选择JOIN正确的关系类型并能将其转化为正确的SQL。

  7、SQL的派生表就像表的变量

  前文,我们已经了解到SQL是一种声明性语言,因此不会有变量。(虽然在一些SQL语句中可能会存在)但你可以写这样的变量。那些野兽一般的表被称为派生表。

  派生表只不过是包含在括号里的子查询。

-- A derived table

FROM (SELECT * FROM author)

  需要注意的是,一些SQL方言要求派生表有一个关联的名字(也被称为别名)。

-- A derived table with an alias

FROM (SELECT * FROM author) a

  当你想规避由SQL子句逻辑排序造成的问题时,你会发现派生表可以用帅呆了来形容。例如,如果你想在SELECT和WHERE子句中重用一个列表达式,只写(Oracle方言):

-- Get authors' first and last names, and their age in days

SELECT first_name, last_name, age

FROM (

SELECT first_name, last_name, current_date - date_of_birth age

FROM author

)

-- If the age is greater than 10000 days

WHERE age > 10000

  注意,一些数据库和SQL:1999标准里已将派生表带到下一级别,,引入公共表表达式。这将允许你在单一的SQL SELECT中重复使用相同的派生表。上面的查询将转化为类似这样的:

WITH a AS (

SELECT first_name, last_name, current_date - date_of_birth age

FROM author

)

SELECT *

FROM a

WHERE age > 10000

  很明显,对广泛重用的常见SQL子查询,你也可以灌输具体"a"到一个独立视图中。想要了解更多就看这里。

  从中我们学到了什么呢?

  再温习一遍,SQL主要是关于表引用,而不是列。好好利用这些表引用。不要害怕写派生表或其他复杂的表引用。

  8、SQL GROUP BY转换之前的表引用

  让我们重新考虑我们之前的FROM语句:

FROM a, b

  现在,让我们来应用一个GROUP BY语句到上述组合表引用

GROUP BY A.x, A.y, B.z

  这会产生一个只有其余三个列(!)的新的表引用。让我们再消化一遍。如果你用了GROUP BY,那么你在所有后续逻辑条款-包括选择中减少可用列的数量。这就是为什么你只可以从SELECT语句中的GROUP BY语句引用列语法的原因。

  • 请注意,其他列仍然可能可作为聚合函数的参数:
SELECT A.x, A.y, SUM(A.z) 
FROM A 
GROUP BY A.x, A.y
  • 值得注意并很不幸的是,MySQL不遵守这一标准,只会造成混乱。不要陷入MySQL的把戏。GROUP BY转换表引用,因此你可以只引用列也引用GROUPBY语句。

从中我们学到了什么呢?

GROUP BY,在表引用上操作,将它们转换成一个新表。

  9、SQL SELECT在关系代数中被称为投影

  当它在关系代数中使用时,我个人比较喜欢用"投影"一词中。一旦你生成你的表引用,过滤,转换它,你可以一步将它投影到另一个表中。SELECT语句就像一个投影机。表函数利用行值表达式将之前构造的表引用的每个记录转化为最终结果。

  在SELECT语句中,你终于可以在列上操作,创建复杂的列表达式作为记录/行的一部分。

  有很多关于可用的表达式,函数性质等的特殊规则。最重要的是,你应该记住这些:

  1、你只能使用从“output”表引用产生的列引用

  2、如果你有GROUP BY语句,你只可能从该语句或聚合函数引用列

  3、当你没有GROUP BY语句时,你可以用窗口函数替代聚合函数

  4、如果你没有GROUP BY语句,你就不能将聚合函数与非聚合函数结合起来

  5、这有一些关于在聚合函数包装常规函数的规则,反之亦然

  6、还有…

  嗯,这有很多复杂的规则。他们可以填补另一个教程。例如,之所以你不能将聚合函数与非聚合函数结合起来投影到没有GROUP BY的SELECT语句中是因为:

  1、凭直觉,没有任何意义。

  2、对一个SQL初学者来说,直觉还是毫无帮助的,语法规则则可以。SQL:1999年引入了分组集,SQL:2003引入了空分组集GROUP BY()。每当存在没有显式GROUP BY语句的聚合函数时就会应用隐式的空分组集(规则2)。因此,最初关于逻辑顺序的那个规则就不完全正确了,SELECT的投影也会影响前面的逻辑结果,但语法语句GROUP BY却不受影响。

是不是有点迷糊?其实我也是,让我们看一些简单的吧。

  从中我们学到了什么呢?

  在SQL语句中,SELECT语句可能是最复杂的语句之一,即使它看起来是那么的简单。所有其他语句只不过是从这个到另一个表引用的的输送管道。通过将它们完全转化,后期地对它们应用一些规则,SELECT语句完完全全地搅乱了这些表引用的美。

  为了了解SQL,重要的是要理解其他的一切,都要尝试在SELECT之前解决。即便SELECT在语法顺序中排第一的语句,也应该将它放到最后。

  10.相对简单一点的SQL DISTINCT,UNION,ORDER BY,和OFFSET

  看完复杂的SELECT之后,我们看回一些简单的东西。

  • 集合运算(DISTINCT和UNION)

  • 排序操作(ORDER BY,OFFSET..FETCH)

  集合运算

  集合运算在除了表其实没有其他东西的“集”上操作。嗯,差不多是这样,从概念上讲,它们还是很容易理解的

  • DISTINCT投影后删除重复项。

  • UNION求并集,删除重复项。

  • UNION ALL求并集,保留重复项。

  • EXCEPT求差集(在第一个子查询结果中删除第二个子查询中也含有的记录删除),删除重复项。

  • INTERSECT求交集(保留所有子查询都含有的记录),删除重复项。

  所有这些删除重复项通常是没有意义的,很多时候,当你想要连接子查询时,你应该使用UNION ALL。

  排序操作

  排序不是一个关系特征,它是SQL仅有的特征。在你的SQL语句中,它被应用在语法排序和逻辑排序之后。保证可以通过索引访问记录的唯一可靠方法是使用ORDER BY a和OFFSET..FETCH。所有其他的排序总是任意的或随机的,即使它看起来像是可再现的。

  OFFSET..FETCH是唯一的语法变体。其他变体包括MySQL'和PostgreSQL的LIMIT..OFFSET,或者SQL Server和Sybase的TOP..START AT(这里)。

  让我们开始应用吧

  跟其他每个语言一样,要掌握SQL语言需要大量的实践。上述10个简单的步骤将让你每天编写SQL时更有意义。另一方面,你也可以从常见的错误中学习到更多。下面的两篇文章列出许多Java(和其他)开发者写SQL时常见的错误:

  · 10 Common Mistakes Java Developers Make when Writing SQL

  · 10 More Common Mistakes Java Developers Make when Writing SQL

  英文来源:10 Easy Steps to a Complete Understanding of SQL

推荐阅读
  • 推荐一个ASP的内容管理框架(ASP Nuke)的优势和适用场景
    本文推荐了一个ASP的内容管理框架ASP Nuke,并介绍了其主要功能和特点。ASP Nuke支持文章新闻管理、投票、论坛等主要内容,并可以自定义模块。最新版本为0.8,虽然目前仍处于Alpha状态,但作者表示会继续更新完善。文章还分析了使用ASP的原因,包括ASP相对较小、易于部署和较简单等优势,适用于建立门户、网站的组织和小公司等场景。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了如何在MySQL中将零值替换为先前的非零值的方法,包括使用内联查询和更新查询。同时还提供了选择正确值的方法。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • 本文介绍了adg架构设置在企业数据治理中的应用。随着信息技术的发展,企业IT系统的快速发展使得数据成为企业业务增长的新动力,但同时也带来了数据冗余、数据难发现、效率低下、资源消耗等问题。本文讨论了企业面临的几类尖锐问题,并提出了解决方案,包括确保库表结构与系统测试版本一致、避免数据冗余、快速定位问题等。此外,本文还探讨了adg架构在大版本升级、上云服务和微服务治理方面的应用。通过本文的介绍,读者可以了解到adg架构设置的重要性及其在企业数据治理中的应用。 ... [详细]
  • 本文详细介绍了云服务器API接口的概念和作用,以及如何使用API接口管理云上资源和开发应用程序。通过创建实例API、调整实例配置API、关闭实例API和退还实例API等功能,可以实现云服务器的创建、配置修改和销毁等操作。对于想要学习云服务器API接口的人来说,本文提供了详细的入门指南和使用方法。如果想进一步了解相关知识或阅读更多相关文章,请关注编程笔记行业资讯频道。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 本文由编程笔记小编整理,介绍了PHP中的MySQL函数库及其常用函数,包括mysql_connect、mysql_error、mysql_select_db、mysql_query、mysql_affected_row、mysql_close等。希望对读者有一定的参考价值。 ... [详细]
  • 本文介绍了2019年上半年内蒙古计算机软考考试的报名通知和考试时间。考试报名时间为3月1日至3月23日,考试时间为2019年5月25日。考试分为高级、中级和初级三个级别,涵盖了多个专业资格。报名采取网上报名和网上缴费的方式进行,报考人员可登录内蒙古人事考试信息网进行报名。详细内容请点击查看。 ... [详细]
  • 众筹商城与传统商城的区别及php众筹网站的程序源码
    本文介绍了众筹商城与传统商城的区别,包括所售产品和玩法不同以及运营方式不同。同时还提到了php众筹网站的程序源码和方维众筹的安装和环境问题。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 从高级程序员到CTO的4次能力跃迁!如何选择适合的技术负责人?
    本文讲解了从高级程序员到CTO的4次能力跃迁,以及如何选择适合的技术负责人。在初创期、发展期、成熟期的每个阶段,创业公司需要不同级别的技术负责人来实现复杂功能、解决技术难题、提高交付效率和质量。高级程序员的职责是实现复杂功能、编写核心代码、处理线上bug、解决技术难题。而技术经理则需要提高交付效率和质量。 ... [详细]
author-avatar
kanlikanliti_627
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有