--组合查询
--多数SQL查询都只包含一个或多个表中返回数据的单条SELECT语句。但是,SQL也允许执行多个查询(多条SELECT语句),并将结果作为单个结果集返回。这些组合查询通常称为是并(union)或复合查询(compound query)。
--在单个查询中从不同的表类似返回结构数据
--对单个表执行多个查询,按单个查询返回数据
--组合查询和多个WHERE条件 多数情况下,组合相同表的两个查询完成的工作与具有多个WHERE子句的单条查询完成的工作相同。换句话说,任何具有多个WHERE子句的SELECT语句都可以作为一个组合查询给出,下面例子可以看到:
--14.2 创建组合查询
--可用UNION操作符来组合多条SQL查询
--14.2.1使用UNION 所需做的只是给出每条SELECT语句,在各条语句之间放上关键字UNION.
--SELECT cust_name, cust_contact, cust_email
--FROM Customers
--WHERE cust_state IN ('IL', 'IN', 'MI');
--SELECT cust_name, cust_contact, cust_email
--FROM Customers
--WHERE cust_name = 'Fun4All';
--SELECT cust_name, cust_contact, cust_email
--FROM Customers
--WHERE cust_state IN ('IL', 'IN', 'MI')
--UNION
--SELECT cust_name, cust_contact, cust_email
--FROM Customers
--WHERE cust_name = 'Fun4All';
----也可以使用多条WHERE子句而不是使用UNION的相同查询:
--SELECT cust_name, cust_contact, cust_email
--FROM Customers
--WHERE cust_state IN ('IL', 'IN', 'MI')
--OR cust_name = 'Fun4All';
--SELECT cust_name, cust_contact, cust_email
--FROM Customers
--WHERE cust_state IN ('IL', 'IN', 'MI')
--UNION
--SELECT cust_name, cust_contact --使用 UNION、INTERSECT 或 EXCEPT 运算符合并的所有查询必须在其目标列表中有相同数目的表达式。
--FROM Customers
--WHERE cust_name = 'Fun4All';
--在这个简单的例子中,使用UNION可能比使用WHERE子句更为复杂。但对于更复杂的过滤条件,或者从多个表(而不是单个表)中检索数据的情形,使用UNION可能会使处理更简单。
--UNION的限制 对于使用UNION组合的SELECT语句数目,不存在标准的SQL限制。
--性能问题 理论上使用多条WHERE子句条件或UNION应该没有实际的差别,但是可以性能上会有所不同。
--14.2.2 NUION规则
--UNION必须有两台或两台以上的SELECT语句组成,语句之间用关键字UNION分隔。
--UNION中的每个查询必须包含相同的列,表达式或聚集函数(不过各个列不需要以相同的次序列出)。
--列数据类型必须兼容:类型不必完全相同,但必须使DBMS可以隐含地转换的类型(例如:不同的数值类型或不同的日期类型)
--14.2.3 包含或取消重复的行
--UNION从查询结果集中自动去除了重复的行(它的行为与单条SELECT语句中使用多个WHERE子句条件一样)
--这个UNION默认行为。如果想返回所有匹配行,可使用UNION ALL而不是UNION.
--SELECT cust_name, cust_contact, cust_email
--FROM Customers
--WHERE cust_state IN ('IL', 'IN', 'MI')
--UNION ALL
--SELECT cust_name, cust_contact, cust_email
--FROM Customers
--WHERE cust_name = 'Fun4All'; --重复的行也进行的输出
--使用UNION ALL,DBMS不取消 重复的行。
--UNION与WHERE UNION几乎总是完成与多个WHERE条件相同的工作。UNION ALL为UNION的一种形式,它完成WHERE子句完成不了的工作。如果确实需要每个条件的匹配行全部出现(包括重复行),则必须使用UNIONA ALL而不是WHERE。
--14.2.4 对组合查询结果排序
--在用UNION组合查询时,只能使用一条ORDER BY子句,它必须出现在最后一条SELECT语句之后。
--SELECT cust_name, cust_contact, cust_email
--FROM Customers
--WHERE cust_state IN ('IL', 'IN', 'MI')
--UNION
--SELECT cust_name, cust_contact, cust_email
--FROM Customers
--WHERE cust_name = 'Fun4All'
--ORDER BY cust_name, cust_contact;
--SELECT cust_name, cust_contact, cust_email --结果为什么不一致
--FROM Customers
--WHERE cust_state IN ('IL', 'IN', 'MI')
--UNION
--SELECT cust_name, cust_email, cust_contact --每条 SELECT 语句中的列的顺序必须相同。
--FROM Customers
--WHERE cust_name = 'Fun4All'
--ORDER BY cust_name, cust_contact;
--14.3 小结
--UNION,可以把多条查询的结果作为一条组合查询返回,不管它们的结果中包含还是不包含重复。使用UNION可极大地简化复杂地WHERE子句,简化从多个表中检索数据的工作。
--15 插入数据
--15.1.1 插入完整的行
--把数据插入表中的最简单的方法时使用基本的INSERT语法,它要求指定表名和被插入到新行中的值。
--INSERT INTO Customers
--VALUES('1000000006',
-- 'Toy Land',
-- '123 Any Street',
-- 'New Tork',
-- 'NY',
-- '11111',
-- 'USA',
-- 'NULL',
-- 'NULL');
--存储到每个表列的中的数据在VALUES子句中给出,对每个列必须提供一个值。如果某个列没有值(如上面的cust_contact, cust_email列),应该使用NULL值(假定表允许对该列指定空值)。各个列必须以它们在表定义中出现的次序填充。
--INTO关键字 在某些SQL实现中,跟在INSERT之后的INTO关键字时可选的。
--虽然这种语法很简单,但并不安全,应该尽量避免使用。上面的SQL语句高度依赖于表中的列的定义次序,并且还依赖于其次序容易获得的信息。即使可得到这种次序信息,也不能保证下一次表结构变动后各个列保持完全相同的次序。因此,很不安全的,难免会出问题。
--编写INSERT语句的更安全(不过更繁琐)的方法如下:
--INSERT INTO Customers(cust_id,
-- cust_name,
-- cust_address,
-- cust_city,
-- cust_state,
-- cust_zip,
-- cust_country,
-- cust_contact,
-- cust_email)
--VALUES('1000000006',
-- 'Toy Land',
-- '123 Any Street',
-- 'New York',
-- 'NY',
-- '11111',
-- 'USA',
-- 'NULL',
-- 'NULL');
--此例子完成与前一个INSERT语句完全相同的工作,但在表明后的括号里明确地给出了列名。在插入行时,DBMS将用VALUES列表中的相应值填入列表中的对应值。
--因为提供了列名,VALUES必须以其指定的次序匹配指定的列名,不一定按各个列出现在实际表中的次序。其优点是,即使表的结构改变,此INREST语句仍然能正确工作。
--总是使用列的列表 一般不要使用没有明确给出列的列表的INSERT语句。使用列的列表能使SQL代码继续发挥作用,即使表结构发生了变化。
--仔细地给出值 不管使用哪种INSERT语法,都必须给出数目正确的值。如果不提供列名,则必须给每个表列提供一个值。如果提供列名,则必须对每个列出的列给出一个值。如果不这样,将产生一条错误消息,对应的行插入不成功。
--15.1.2 插入部分行
--正如所述,使用INSERT的推荐方法是明确给出表的别名。使用这种语法,还可以省略列。这表示可以只给某些列提供值,给其他列不提供值。
--INSERT INTO Customers(cust_id,
-- cust_name,
-- cust_address,
-- cust_city,
-- cust_state,
-- cust_zip,
-- cust_country)
--VALUES('1000000006',
-- 'Toy Land',
-- '123 Any Street',
-- 'New York',
-- 'NY',
-- '11111',
-- 'USA');
--前面给出的例子中,对两个列cust_contact, cust_email没有提供值。这表示没必要将它们包含在INSERT语句中。因此,这里的INSERT语句省略了这两个列和对应的值。
--省略列 如果表的定义允许,则可以在INSERT操作中省略某些列。省略的列必须满足以下某各条件:
--该列定义为允许NULL值(无值或空值)。
--在表定义中给出默认值。这表示如果不给出值,将使用默认值。
--如果对表中不允许NULL值且没有默认值的列不给出值,则DBMS将产生一条错误信息,并且相应的行插入不成功。
--15.1.3 插入检索出的数据
--INSERT一般用来给表插入一个指定列值的行。但是,INSERT还存在另一种形式,可以利用它将一条SELECT语句的结果插入表中。这就是所谓的INSERT SELECT,顾名思义,它是由一条INSERT语句和一条SELECT语句组成的。
--新例子的说明 从CustNew表中读取数据,故首先创建和填充CustNew表。在填充CustNew时,不应该使用已经在Customers中使用过的cust_id(如果主键值重复,后续的INSERNT操作将会失败)。
--INSERT INTO Customers(cust_id,
-- cust_contact,
-- cust_email,
-- cust_address,
-- cust_city,
-- cust_state,
-- cust_zip,
-- cust_country)
--SELECT cust_id,
-- cust_contact,
-- cust_email,
-- cust_address,
-- cust_city,
-- cust_state,
-- cust_zip,
-- cust_country
--FROM CustNew;
--INSERT SELECT的别名 为简单起见,这个例子在INSERT和SELECT语句中使用了相同的列名。但是,不一定要求列名匹配。事实上,DBMS甚至不关心SELECT返回的列名。它使用的时是列的位置,因此SELECT中的第一列(不管其列名)将用来填充表列中指定的第一个列。
--INSERT SELECT中SELECT的语句可包含WHERE子句以过滤插入的数据。
--插入多行 INSERT通常只插入一行。为了插入多行,必须执行多个INSERT语句。INSERT SELECT是个例外,它可以用单条INSERT插入多行,不管SELECT语句返回多少行,都将被INSERT插入。
--15.2 从一个表复制到另一个表
--有一种不使用INSERT语句的数据插入。为了将一个表的内容复制到一个全新的表(在运行中创建的表),可使用SELECT INTO语句。
--与INSERT SELECT增补数据到一个已经存在的表不同,SELECT INTO将复制数据到一个新表(有的DBMS可以覆盖已经存在的表,依赖于具体的DBMS)。
--INSERT SELECT与SELECT INTO 它们之间的一个重要差别是新者导出数据,而后置导入表。
--SELECT * --要想只复制列的子集,可明确地给出列名而不是使用*通配符。 如果没有该表,则会自动创建。
--INTO CustCopy
--FROM Customers;
--在使用SELECT INTO时,有一些需要知道的东西:
--如何SELECT选项和子句都可以使用,包括WHERE和GROUP BY。
--可利用联结从多个表插入数据
--不管从多个表中检索数据,数据都只能插入到单个表中。
--进行表的复制 SELECT INTO时试验新SQL语句前,做表复制的很好的工具,不会影响到实际的数据。
--15.3 小结
--如何用INSERT SELECT从其它表中导入表,如何用SELECT INT将行到处到一个新表。