SQL query return data from multiple tables
我想知道以下内容:
- 如何从我的数据库中的多个表中获取数据?
- 有哪些方法可以做到这一点?
- 什么是联合和工会,它们之间有什么不同?
- 我应该在什么时候使用每一个?
我计划在我的(例如-php)应用程序中使用它,但不想对数据库运行多个查询,我必须在一个查询中从多个表中获取数据,有哪些选项?
注意:我写这篇文章是因为我希望能够链接到一个写得很好的关于我在php队列中经常遇到的许多问题的指南,所以我可以在发布答案时链接到这个指南以获得进一步的详细信息。
答案包括以下内容:
第1部分-接头和活接头
这个答案包括:好的。
- 使用内部联接联接两个或多个表(有关其他信息,请参阅维基百科条目)
- 如何使用联合查询
- 左、右外部联接(这个stackoverflow答案非常适合描述联接类型)
- Intersect查询(以及如果数据库不支持它们,如何复制它们)——这是SQL Server的一个函数(请参阅信息),也是我最初编写这整件事情的部分原因。
- 子查询-它们是什么,可以在哪里使用,以及要注意什么
- 笛卡尔加入了阿卡-哦,不幸!
有多种方法可以从数据库中的多个表中检索数据。在这个答案中,我将使用ansi-92连接语法。这可能与其他许多使用旧的ANSI-89语法的教程不同(如果您习惯89,可能看起来不太直观,但我只能说是尝试一下),因为当查询变得更复杂时,更容易理解。为什么要用它呢?是否有绩效提升?简短的答案是否定的,但一旦你习惯了,它就更容易阅读了。使用此语法阅读其他人编写的查询更容易。好的。
我还将使用一个小型停车场的概念,它有一个数据库来跟踪它有哪些可用的汽车。老板已经雇用了你作为他的IT计算机人员,希望你能把他要求的数据放在他的头上。好的。
我已经创建了许多查找表,最终表将使用这些查找表。这将为我们提供一个合理的工作模式。首先,我将针对具有以下结构的示例数据库运行查询。我将试着思考在开始时犯下的常见错误,并解释它们出了什么问题——当然也将展示如何纠正它们。好的。
第一张表只是一个颜色列表,以便我们知道我们在停车场有什么颜色。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | mysql> create table colors(id int(3) not null auto_increment primary key, -> color varchar(15), paint varchar(10)); Query OK, 0 rows affected (0.01 sec) mysql> show columns from colors; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | int(3) | NO | PRI | NULL | auto_increment | | color | varchar(15) | YES | | NULL | | | paint | varchar(10) | YES | | NULL | | +-------+-------------+------+-----+---------+----------------+ 3 rows in set (0.01 sec) mysql> insert into colors (color, paint) values ('Red', 'Metallic'), -> ('Green', 'Gloss'), ('Blue', 'Metallic'), -> ('White' 'Gloss'), ('Black' 'Gloss'); Query OK, 5 rows affected (0.00 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> select * from colors; +----+-------+----------+ | id | color | paint | +----+-------+----------+ | 1 | Red | Metallic | | 2 | Green | Gloss | | 3 | Blue | Metallic | | 4 | White | Gloss | | 5 | Black | Gloss | +----+-------+----------+ 5 rows in set (0.00 sec) |
品牌表确定了停车场可能出售的汽车的不同品牌。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | mysql> create table brands (id int(3) not null auto_increment primary key, -> brand varchar(15)); Query OK, 0 rows affected (0.01 sec) mysql> show columns from brands; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | int(3) | NO | PRI | NULL | auto_increment | | brand | varchar(15) | YES | | NULL | | +-------+-------------+------+-----+---------+----------------+ 2 rows in set (0.01 sec) mysql> insert into brands (brand) values ('Ford'), ('Toyota'), -> ('Nissan'), ('Smart'), ('BMW'); Query OK, 5 rows affected (0.00 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> select * from brands; +----+--------+ | id | brand | +----+--------+ | 1 | Ford | | 2 | Toyota | | 3 | Nissan | | 4 | Smart | | 5 | BMW | +----+--------+ 5 rows in set (0.00 sec) |
模型表将涵盖不同类型的汽车,使用不同的汽车类型比使用实际的汽车模型更简单。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | mysql> create table models (id int(3) not null auto_increment primary key, -> model varchar(15)); Query OK, 0 rows affected (0.01 sec) mysql> show columns from models; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | int(3) | NO | PRI | NULL | auto_increment | | model | varchar(15) | YES | | NULL | | +-------+-------------+------+-----+---------+----------------+ 2 rows in set (0.00 sec) mysql> insert into models (model) values ('Sports'), ('Sedan'), ('4WD'), ('Luxury'); Query OK, 4 rows affected (0.00 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> select * from models; +----+--------+ | id | model | +----+--------+ | 1 | Sports | | 2 | Sedan | | 3 | 4WD | | 4 | Luxury | +----+--------+ 4 rows in set (0.00 sec) |
最后,把所有其他的桌子都绑起来,把所有的东西都绑在一起。ID字段实际上是用于识别汽车的唯一批号。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | mysql> create table cars (id int(3) not null auto_increment primary key, -> color int(3), brand int(3), model int(3)); Query OK, 0 rows affected (0.01 sec) mysql> show columns from cars; +-------+--------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+--------+------+-----+---------+----------------+ | id | int(3) | NO | PRI | NULL | auto_increment | | color | int(3) | YES | | NULL | | | brand | int(3) | YES | | NULL | | | model | int(3) | YES | | NULL | | +-------+--------+------+-----+---------+----------------+ 4 rows in set (0.00 sec) mysql> insert into cars (color, brand, model) values (1,2,1), (3,1,2), (5,3,1), -> (4,4,2), (2,2,3), (3,5,4), (4,1,3), (2,2,1), (5,2,3), (4,5,1); Query OK, 10 rows affected (0.00 sec) Records: 10 Duplicates: 0 Warnings: 0 mysql> select * from cars; +----+-------+-------+-------+ | id | color | brand | model | +----+-------+-------+-------+ | 1 | 1 | 2 | 1 | | 2 | 3 | 1 | 2 | | 3 | 5 | 3 | 1 | | 4 | 4 | 4 | 2 | | 5 | 2 | 2 | 3 | | 6 | 3 | 5 | 4 | | 7 | 4 | 1 | 3 | | 8 | 2 | 2 | 1 | | 9 | 5 | 2 | 3 | | 10 | 4 | 5 | 1 | +----+-------+-------+-------+ 10 rows in set (0.00 sec) |
这将为我们提供足够的数据(我希望)来覆盖下面不同类型联接的示例,并提供足够的数据使它们值得使用。好的。
因此,进入到它的沙砾,老板想知道所有的跑车,他拥有的ID。好的。
这是一个简单的两表联接。我们有一个表,用于标识模型和其中包含可用库存的表。如您所见,
所以这个查询看起来不错,对吧?我们已经标识了这两个表并包含了我们需要的信息,并且使用了一个正确标识要联接的列的联接。好的。
号
哦,不!第一个查询出错!是的,而且是李子。您可以看到,查询确实得到了正确的列,但其中一些列存在于这两个表中,因此数据库对我们所指的实际列和位置感到困惑。有两种解决方案可以解决这个问题。第一种方法简单易行,我们可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | select cars.ID, models.model from cars join models on cars.model=models.ID +----+--------+ | ID | model | +----+--------+ | 1 | Sports | | 3 | Sports | | 8 | Sports | | 10 | Sports | | 2 | Sedan | | 4 | Sedan | | 5 | 4WD | | 7 | 4WD | | 9 | 4WD | | 6 | Luxury | +----+--------+ 10 rows in set (0.00 sec) |
另一种可能更常用,称为表别名。本例中的表有好的简单名称和短的简单名称,但键入类似于
。
现在,回到请求。正如你所看到的,我们有我们需要的信息,但我们也有没有被要求的信息,所以我们需要在声明中包含一个WHERE子句,以便只获得被要求的跑车。由于我更喜欢表别名方法,而不是一次又一次地使用表名,因此从现在开始我将坚持使用它。好的。
显然,我们需要在查询中添加一个WHERE子句。我们可以通过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
答对了!老板很高兴。当然,作为一个老板,他从不满足于他的要求,他看了看信息,然后说我也想要颜色。好的。
好吧,我们已经编写了查询的一部分,但是我们需要使用第三个表,即颜色。现在,我们的主要信息表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
。
该死的,尽管表被正确地联接并且相关的列被链接,但是我们忘记了从刚刚链接的新表中提取实际信息。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | select a.ID, b.model, c.color from cars a join models b on a.model=b.ID join colors c on a.color=c.ID where b.ID=1 +----+--------+-------+ | ID | model | color | +----+--------+-------+ | 1 | Sports | Red | | 8 | Sports | Green | | 10 | Sports | White | | 3 | Sports | Black | +----+--------+-------+ 4 rows in set (0.00 sec) |
好吧,那是我们背后的老板。现在,更详细地解释一下其中的一些。如您所见,我们语句中的
以这种方式继续链接越来越多的表是完全可能的。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
号
虽然我忘记在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
可以看到,上面的查询不仅将联接的表链接到主
因此,为了给出笛卡尔连接的示例,让我们运行以下查询:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | select a.ID, b.model from cars a join models b +----+--------+ | ID | model | +----+--------+ | 1 | Sports | | 1 | Sedan | | 1 | 4WD | | 1 | Luxury | | 2 | Sports | | 2 | Sedan | | 2 | 4WD | | 2 | Luxury | | 3 | Sports | | 3 | Sedan | | 3 | 4WD | | 3 | Luxury | | 4 | Sports | | 4 | Sedan | | 4 | 4WD | | 4 | Luxury | | 5 | Sports | | 5 | Sedan | | 5 | 4WD | | 5 | Luxury | | 6 | Sports | | 6 | Sedan | | 6 | 4WD | | 6 | Luxury | | 7 | Sports | | 7 | Sedan | | 7 | 4WD | | 7 | Luxury | | 8 | Sports | | 8 | Sedan | | 8 | 4WD | | 8 | Luxury | | 9 | Sports | | 9 | Sedan | | 9 | 4WD | | 9 | Luxury | | 10 | Sports | | 10 | Sedan | | 10 | 4WD | | 10 | Luxury | +----+--------+ 40 rows in set (0.00 sec) |
。
天哪,真难看。然而,就数据库而言,这正是所要求的。在查询中,我们向
好吧,所以老板回来了,他又想要更多的信息。我想要同样的列表,但也包括4个单词。好的。
然而,这给了我们一个很好的借口,让我们看看实现这一目标的两种不同方法。我们可以向这样的WHERE子句添加另一个条件:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
虽然上面的工作非常好,但是让我们换个角度来看,这是一个很好的理由来说明
我们知道,以下各项将退还所有跑车:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
号
下面将返回所有4wd:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
。
因此,通过在它们之间添加一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | select a.ID, b.model, c.color from cars a join models b on a.model=b.ID join colors c on a.color=c.ID join brands d on a.brand=d.ID where b.ID=1 union all select a.ID, b.model, c.color from cars a join models b on a.model=b.ID join colors c on a.color=c.ID join brands d on a.brand=d.ID where b.ID=3 +----+--------+-------+ | ID | model | color | +----+--------+-------+ | 1 | Sports | Red | | 8 | Sports | Green | | 10 | Sports | White | | 3 | Sports | Black | | 5 | 4WD | Green | | 7 | 4WD | White | | 9 | 4WD | Black | +----+--------+-------+ 7 rows in set (0.00 sec) |
如您所见,首先返回第一个查询的结果,然后返回第二个查询的结果。好的。
在本例中,只使用第一个查询当然要容易得多,但是对于特定的情况,
- 第一个查询中的列类型必须与下面每个其他查询中的列类型匹配。
- 第一个查询中列的名称将用于标识整个结果集。
- 每个查询中的列数必须相同。
现在,您可能想知道使用
在这张纸条上,这里可能值得注意一些附加的纸条。好的。
- 如果我们想要订购结果,我们可以使用
order by ,但是您不能再使用别名了。在上面的查询中,附加一个order by a.ID 将导致一个错误——就结果而言,该列称为ID ,而不是a.ID ,即使两个查询中使用了相同的别名。 - 我们只能有一个
order by 语句,它必须是最后一个语句。
对于下一个示例,我将向我们的表中添加一些额外的行。好的。
我已将
好吧,老板又回来了,汪汪叫着说-*我要清点一下我们每个品牌的数量和里面的车数!`-典型的情况是,我们只需进入讨论的一个有趣的部分,老板想要更多的工作。好的。
好的,所以我们要做的第一件事就是得到一份完整的可能品牌的清单。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
现在,当我们把这个加入到我们的cars表中时,我们得到了以下结果:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
号
这当然是个问题——我们没有看到任何关于可爱的
这是因为联接在两个表中都查找匹配的行。由于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
现在我们有了这个,我们可以添加一个可爱的聚合函数来获取一个计数,让老板暂时离开我们。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | select a.brand, count(b.id) as countOfBrand from brands a left outer join cars b on a.ID=b.brand group by a.brand +--------+--------------+ | brand | countOfBrand | +--------+--------------+ | BMW | 2 | | Ford | 2 | | Holden | 0 | | Nissan | 1 | | Smart | 1 | | Toyota | 5 | +--------+--------------+ 6 rows in set (0.00 sec) |
。
这样,老板就躲开了。好的。
现在,为了更详细地解释这一点,外部连接可以是
有些数据库将允许使用
现在,我可能已经考虑到了这一点,您可能想知道是否可以在查询中合并联接类型——答案是肯定的,您完全可以。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | select b.brand, c.color, count(a.id) as countOfBrand from cars a right outer join brands b on b.ID=a.brand join colors c on a.color=c.ID group by a.brand, c.color +--------+-------+--------------+ | brand | color | countOfBrand | +--------+-------+--------------+ | Ford | Blue | 1 | | Ford | White | 1 | | Toyota | Black | 1 | | Toyota | Green | 2 | | Toyota | Red | 1 | | Nissan | Black | 1 | | Smart | White | 1 | | BMW | Blue | 1 | | BMW | White | 1 | +--------+-------+--------------+ 9 rows in set (0.00 sec) |
那么,为什么这不是预期的结果呢?这是因为尽管我们选择了从汽车到品牌的外部连接,但它并没有在颜色连接中指定,所以特定的连接只会返回两个表中匹配的结果。好的。
下面是一个可以得到我们期望的结果的查询:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | select a.brand, c.color, count(b.id) as countOfBrand from brands a left outer join cars b on a.ID=b.brand left outer join colors c on b.color=c.ID group by a.brand, c.color +--------+-------+--------------+ | brand | color | countOfBrand | +--------+-------+--------------+ | BMW | Blue | 1 | | BMW | White | 1 | | Ford | Blue | 1 | | Ford | White | 1 | | Holden | NULL | 0 | | Nissan | Black | 1 | | Smart | White | 1 | | Toyota | NULL | 1 | | Toyota | Black | 1 | | Toyota | Green | 2 | | Toyota | Red | 1 | +--------+-------+--------------+ 11 rows in set (0.00 sec) |
号
正如我们所看到的,我们在查询中有两个外部联接,结果如预期的那样通过。好的。
现在,您询问的其他类型的连接如何?交叉口怎么办?好的。
不是所有的数据库都支持
交集是一种与上述
一个简单的例子如下:好的。
虽然普通的
现在,如果您的数据库不支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
。
如果您希望使用一个本身不支持交叉查询的数据库跨两个不同的表执行交叉,则需要在表的每一列上创建一个联接。好的。好啊。
好的,我发现这篇文章很有趣,我想分享一下我在创建查询方面的一些知识。谢谢你的吹毛求疵。其他可能读到这篇文章并可能觉得我错了的人有101%的自由编辑和批评我的答案。(老实说,我非常感谢纠正我的错误。)
我将在
给定此架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | CREATE TABLE MovieList ( ID INT, MovieName VARCHAR(25), CONSTRAINT ml_pk PRIMARY KEY (ID), CONSTRAINT ml_uq UNIQUE (MovieName) ); INSERT INTO MovieList VALUES (1, 'American Pie'); INSERT INTO MovieList VALUES (2, 'The Notebook'); INSERT INTO MovieList VALUES (3, 'Discovery Channel: Africa'); INSERT INTO MovieList VALUES (4, 'Mr. Bean'); INSERT INTO MovieList VALUES (5, 'Expendables 2'); CREATE TABLE CategoryList ( MovieID INT, CategoryName VARCHAR(25), CONSTRAINT cl_uq UNIQUE(MovieID, CategoryName), CONSTRAINT cl_fk FOREIGN KEY (MovieID) REFERENCES MovieList(ID) ); INSERT INTO CategoryList VALUES (1, 'Comedy'); INSERT INTO CategoryList VALUES (1, 'Romance'); INSERT INTO CategoryList VALUES (2, 'Romance'); INSERT INTO CategoryList VALUES (2, 'Drama'); INSERT INTO CategoryList VALUES (3, 'Documentary'); INSERT INTO CategoryList VALUES (4, 'Comedy'); INSERT INTO CategoryList VALUES (5, 'Comedy'); INSERT INTO CategoryList VALUES (5, 'Action'); |
问题
找到至少属于
解决方案
这个问题有时很棘手。似乎这样的查询就是答案:
1 2 3 4 5 6 |
号sqlfiddle演示
这绝对是错误的,因为它不会产生任何结果。对此的解释是,每行只有一个有效的
1 2 3 4 5 |
sqlfiddle演示
结果仍然不正确,因为它与
1 2 3 4 5 6 7 |
。sqlfiddle演示(答案)
- 关系除法的SQL
2号技巧(每项最多记录)
给定的模式,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | CREATE TABLE Software ( ID INT, SoftwareName VARCHAR(25), Descriptions VARCHAR(150), CONSTRAINT sw_pk PRIMARY KEY (ID), CONSTRAINT sw_uq UNIQUE (SoftwareName) ); INSERT INTO Software VALUES (1,'PaintMe','used for photo editing'); INSERT INTO Software VALUES (2,'World Map','contains map of different places of the world'); INSERT INTO Software VALUES (3,'Dictionary','contains description, synonym, antonym of the words'); CREATE TABLE VersionList ( SoftwareID INT, VersionNo INT, DateReleased DATE, CONSTRAINT sw_uq UNIQUE (SoftwareID, VersionNo), CONSTRAINT sw_fk FOREIGN KEY (SOftwareID) REFERENCES Software(ID) ); INSERT INTO VersionList VALUES (3, 2, '2009-12-01'); INSERT INTO VersionList VALUES (3, 1, '2009-11-01'); INSERT INTO VersionList VALUES (3, 3, '2010-01-01'); INSERT INTO VersionList VALUES (2, 2, '2010-12-01'); INSERT INTO VersionList VALUES (2, 1, '2009-12-01'); INSERT INTO VersionList VALUES (1, 3, '2011-12-01'); INSERT INTO VersionList VALUES (1, 2, '2010-12-01'); INSERT INTO VersionList VALUES (1, 1, '2009-12-01'); INSERT INTO VersionList VALUES (1, 4, '2012-12-01'); |
问题
在每个软件上查找最新版本。显示以下列:
解决方案
一些SQL开发人员错误地使用了
1 2 3 4 5 6 7 |
。sqlfiddle演示
(大多数RDBMS在这方面会产生语法错误,因为没有在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | SELECT a.SoftwareName, a.Descriptions, b.LatestVersion, c.DateReleased FROM Software a INNER JOIN ( SELECT SoftwareID, MAX(VersionNO) LatestVersion FROM VersionList GROUP BY SoftwareID ) b ON a.ID = b.SoftwareID INNER JOIN VersionList c ON c.SoftwareID = b.SoftwareID AND c.VersionNO = b.LatestVersion GROUP BY a.ID ORDER BY a.ID |
。sqlfiddle演示(答案)
就是这样。我一想起
更新1
技巧3(在两个ID之间找到最新记录)给定架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | CREATE TABLE userList ( ID INT, NAME VARCHAR(20), CONSTRAINT us_pk PRIMARY KEY (ID), CONSTRAINT us_uq UNIQUE (NAME) ); INSERT INTO userList VALUES (1, 'Fluffeh'); INSERT INTO userList VALUES (2, 'John Woo'); INSERT INTO userList VALUES (3, 'hims056'); CREATE TABLE CONVERSATION ( ID INT, FROM_ID INT, TO_ID INT, MESSAGE VARCHAR(250), DeliveryDate DATE ); INSERT INTO CONVERSATION VALUES (1, 1, 2, 'hi john', '2012-01-01'); INSERT INTO CONVERSATION VALUES (2, 2, 1, 'hello fluff', '2012-01-02'); INSERT INTO CONVERSATION VALUES (3, 1, 3, 'hey hims', '2012-01-03'); INSERT INTO CONVERSATION VALUES (4, 1, 3, 'please reply', '2012-01-04'); INSERT INTO CONVERSATION VALUES (5, 3, 1, 'how are you?', '2012-01-05'); INSERT INTO CONVERSATION VALUES (6, 3, 2, 'sample message!', '2012-01-05'); |
问题
查找两个用户之间的最新对话。
解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | SELECT b.Name SenderName, c.Name RecipientName, a.Message, a.DeliveryDate FROM Conversation a INNER JOIN userList b ON a.From_ID = b.ID INNER JOIN userList c ON a.To_ID = c.ID WHERE (LEAST(a.FROM_ID, a.TO_ID), GREATEST(a.FROM_ID, a.TO_ID), DeliveryDate) IN ( SELECT LEAST(FROM_ID, TO_ID) minFROM, GREATEST(FROM_ID, TO_ID) maxTo, MAX(DeliveryDate) maxDate FROM Conversation GROUP BY minFROM, maxTo ) |
。sqlfiddle演示
第2部分-子查询
好吧,现在老板又闯进来了——我想要一份我们所有汽车的品牌清单,以及我们拥有的品牌总数!好的。
这是一个很好的机会来使用我们的SQL产品包中的下一个技巧-子查询。如果您不熟悉这个术语,子查询就是在另一个查询中运行的查询。有许多不同的方法可以使用它们。好的。
对于我们的请求,首先让我们将一个简单的查询放在一起,列出每辆车和品牌:好的。
现在,如果我们想简单地按品牌分类,我们当然可以写下:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
号
所以,我们应该能够简单地将count函数添加到原始查询中,对吗?好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | select a.ID, b.brand, count(a.ID) as countCars from cars a join brands b on a.brand=b.ID group by a.ID, b.brand +----+--------+-----------+ | ID | brand | countCars | +----+--------+-----------+ | 1 | Toyota | 1 | | 2 | Ford | 1 | | 3 | Nissan | 1 | | 4 | Smart | 1 | | 5 | Toyota | 1 | | 6 | BMW | 1 | | 7 | Ford | 1 | | 8 | Toyota | 1 | | 9 | Toyota | 1 | | 10 | BMW | 1 | | 11 | Toyota | 1 | +----+--------+-----------+ 11 rows in set (0.00 sec) |
可悲的是,不,我们做不到。原因是,当我们添加汽车ID(A.ID列)时,我们必须将其添加到group by中-所以现在,当count函数工作时,每个ID只有一个匹配的ID。好的。
这就是我们可以使用子查询的地方——事实上,我们可以执行两种完全不同的子查询,它们将返回我们需要的相同结果。第一种方法是简单地将子查询放在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | select a.ID, b.brand, ( select count(c.ID) from cars c where a.brand=c.brand ) as countCars from cars a join brands b on a.brand=b.ID +----+--------+-----------+ | ID | brand | countCars | +----+--------+-----------+ | 2 | Ford | 2 | | 7 | Ford | 2 | | 1 | Toyota | 5 | | 5 | Toyota | 5 | | 8 | Toyota | 5 | | 9 | Toyota | 5 | | 11 | Toyota | 5 | | 3 | Nissan | 1 | | 4 | Smart | 1 | | 6 | BMW | 2 | | 10 | BMW | 2 | +----+--------+-----------+ 11 rows in set (0.00 sec) |
。
砰的一声!这对我们有好处。但是,如果您注意到,对于我们返回的每一行数据,都必须运行此子查询。即使在这个小例子中,我们只有五个不同品牌的汽车,但是子查询运行了十一次,因为我们有十一行返回的数据。因此,在这种情况下,它似乎不是最有效的代码编写方法。好的。
对于不同的方法,让我们运行一个子查询并假设它是一个表:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | select a.ID, b.brand, d.countCars from cars a join brands b on a.brand=b.ID join ( select c.brand, count(c.ID) as countCars from cars c group by c.brand ) d on a.brand=d.brand +----+--------+-----------+ | ID | brand | countCars | +----+--------+-----------+ | 1 | Toyota | 5 | | 2 | Ford | 2 | | 3 | Nissan | 1 | | 4 | Smart | 1 | | 5 | Toyota | 5 | | 6 | BMW | 2 | | 7 | Ford | 2 | | 8 | Toyota | 5 | | 9 | Toyota | 5 | | 10 | BMW | 2 | | 11 | Toyota | 5 | +----+--------+-----------+ 11 rows in set (0.00 sec) |
好的,所以我们有相同的结果(顺序略有不同——看起来数据库想要返回按照我们这次选择的第一列排序的结果)——但是正确的数字是相同的。好的。
那么,这两者之间的区别是什么?我们什么时候应该使用每种类型的子查询?首先,让我们确保了解第二个查询是如何工作的。我们在查询的
现在,回顾第一个子查询,也有一些限制。因为我们将数据拉回到一行中,所以只能拉回一行数据。查询的
所以,在我们继续之前,让我们快速了解一下在哪里可以使用子查询。我们可以在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
这将返回包含名称中字母
现在,我们可以在WHERE子句中使用此查询的结果,如下所示:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | select a.ID, b.brand from cars a join brands b on a.brand=b.ID where a.brand in ( select ID from brands where brand like '%o%' ) +----+--------+ | ID | brand | +----+--------+ | 2 | Ford | | 7 | Ford | | 1 | Toyota | | 5 | Toyota | | 8 | Toyota | | 9 | Toyota | | 11 | Toyota | +----+--------+ 7 rows in set (0.00 sec) |
号
如您所见,即使子查询返回三个品牌ID,我们的cars表中只有两个条目。好的。
在这种情况下,为了进一步了解细节,子查询的工作方式就像我们编写了以下代码一样:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
同样,您可以看到子查询和手动输入在从数据库返回时是如何更改行的顺序的。好的。
当我们讨论子查询时,让我们看看如何处理子查询:好的。
- 您可以将子查询放在另一个子查询中,依此类推。有一个限制取决于您的数据库,但缺少一些疯狂和疯狂的程序员的递归函数,大多数人永远不会达到这个限制。
- 您可以在一个查询中放入许多子查询,其中一些子查询在
select 子句中,一些子查询在from 子句中,另一些子查询在where 子句中,只需记住,您放入的每个子查询都会使您的查询更加复杂,执行起来可能需要更长的时间。
如果您需要编写一些有效的代码,可以通过多种方式编写查询,并查看(通过计时或使用解释计划)哪种查询是获得结果的最佳查询。第一种有效的方法并不总是最好的方法。好的。好啊。
第3部分-技巧和有效代码mysql in()效率
我想我会增加一些额外的小费和技巧。
我看到的一个问题是,如何从两个表中得到不匹配的行,我认为最普遍接受的答案如下(基于我们的汽车和品牌表——霍尔登被列为一个品牌,但并未出现在汽车表中):
是的,它会起作用的。
1 2 3 4 5 6 |
号
但是,在某些数据库中,它是无效的。这里有一个链接指向一个关于堆栈溢出的问题,如果你想了解细节,这里有一篇非常深入的文章。
简短的答案是,如果乐观者不能有效地处理它,那么使用如下查询获取不匹配的行可能会更好:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
用子查询中的相同表更新表
啊,另一个老的,但古蒂-旧的,你不能指定目标表'品牌'更新在从子句。
MySQL不允许您在同一个表上运行带有子select的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
。
所以,我们不能这样做,嗯?嗯,不完全是。有一个令人惊讶的大量用户不知道的秘密解决方案-尽管它确实包括一些你需要注意的黑客。
您可以将子查询粘贴到另一个子查询中,这会在两个查询之间留出足够的间隙,以便它工作。但是,请注意,将查询固定在事务中可能最安全——这将防止在查询运行时对表进行任何其他更改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
可以在from关键字中使用多个查询的概念。让我举一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 | SELECT DISTINCT e.id,e.name,d.name,lap.lappy LAPTOP_MAKE,c_loc.cnty COUNTY FROM ( SELECT c.id cnty,l.name FROM county c, location l WHERE c.id=l.county_id AND l.end_Date IS NOT NULL ) c_loc, emp e INNER JOIN dept d ON e.deptno =d.id LEFT JOIN ( SELECT l.id lappy, c.name cmpy FROM laptop l, company c WHERE l.make = c.name ) lap ON e.cmpy_id=lap.cmpy |
。
您可以使用任意多的表。在需要的地方使用外部联接和联合,即使在表子查询中也是如此。
这是一种非常简单的方法,可以涉及到与表和字段一样多的内容。
希望它能在你看书的时候找到桌子:
J小提琴
1 2 3 4 5 6 7 8 | mysql> show columns from colors; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | int(3) | NO | PRI | NULL | auto_increment | | color | varchar(15) | YES | | NULL | | | paint | varchar(10) | YES | | NULL | | +-------+-------------+------+-----+---------+----------------+ |