关于mysql:SQL查询从多个表返回数据

SQL query return data from multiple tables

我想知道以下内容:

  • 如何从我的数据库中的多个表中获取数据?
  • 有哪些方法可以做到这一点?
  • 什么是联合和工会,它们之间有什么不同?
  • 我应该在什么时候使用每一个?

我计划在我的(例如-php)应用程序中使用它,但不想对数据库运行多个查询,我必须在一个查询中从多个表中获取数据,有哪些选项?

注意:我写这篇文章是因为我希望能够链接到一个写得很好的关于我在php队列中经常遇到的许多问题的指南,所以我可以在发布答案时链接到这个指南以获得进一步的详细信息。

答案包括以下内容:

  • 第1部分-接头和活接头
  • 第2部分-子查询
  • 第3部分-技巧和有效代码
  • 第4部分-FROM子句中的子查询
  • 第5部分-约翰的花招

  • 第1部分-接头和活接头

    这个答案包括:好的。

  • 第1部分
    • 使用内部联接联接两个或多个表(有关其他信息,请参阅维基百科条目)
    • 如何使用联合查询
    • 左、右外部联接(这个stackoverflow答案非常适合描述联接类型)
    • Intersect查询(以及如果数据库不支持它们,如何复制它们)——这是SQL Server的一个函数(请参阅信息),也是我最初编写这整件事情的部分原因。
  • 第2部分
    • 子查询-它们是什么,可以在哪里使用,以及要注意什么
    • 笛卡尔加入了阿卡-哦,不幸!
  • 有多种方法可以从数据库中的多个表中检索数据。在这个答案中,我将使用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。好的。

    这是一个简单的两表联接。我们有一个表,用于标识模型和其中包含可用库存的表。如您所见,cars表的model列中的数据与我们拥有的cars表的models列有关。现在,我们知道models表对于Sports有一个id为1,所以让我们编写连接。好的。

    1
    2
    3
    4
    5
    6
    7
    select
        ID,
        model
    from
        cars
            join models
                on model=ID

    所以这个查询看起来不错,对吧?我们已经标识了这两个表并包含了我们需要的信息,并且使用了一个正确标识要联接的列的联接。好的。

    1
    ERROR 1052 (23000): Column 'ID' in field list is ambiguous

    哦,不!第一个查询出错!是的,而且是李子。您可以看到,查询确实得到了正确的列,但其中一些列存在于这两个表中,因此数据库对我们所指的实际列和位置感到困惑。有两种解决方案可以解决这个问题。第一种方法简单易行,我们可以使用tableName.columnName来告诉数据库我们的确切含义,如下所示:好的。

    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)

    另一种可能更常用,称为表别名。本例中的表有好的简单名称和短的简单名称,但键入类似于KPI_DAILY_SALES_BY_DEPARTMENT的内容可能会很快变旧,因此一种简单的方法是将表命名为如下:好的。

    1
    2
    3
    4
    5
    6
    7
    select
        a.ID,
        b.model
    from
        cars a
            join models b
                on a.model=b.ID

    现在,回到请求。正如你所看到的,我们有我们需要的信息,但我们也有没有被要求的信息,所以我们需要在声明中包含一个WHERE子句,以便只获得被要求的跑车。由于我更喜欢表别名方法,而不是一次又一次地使用表名,因此从现在开始我将坚持使用它。好的。

    显然,我们需要在查询中添加一个WHERE子句。我们可以通过ID=1model='Sports'来识别跑车。由于ID是索引的,主键(而且它的类型也比较少),所以让我们在查询中使用它。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    select
        a.ID,
        b.model
    from
        cars a
            join models b
                on a.model=b.ID
    where
        b.ID=1

    +----+--------+
    | ID | model  |
    +----+--------+
    |  1 | Sports |
    |  3 | Sports |
    |  8 | Sports |
    | 10 | Sports |
    +----+--------+
    4 rows in set (0.00 sec)

    答对了!老板很高兴。当然,作为一个老板,他从不满足于他的要求,他看了看信息,然后说我也想要颜色。好的。

    好吧,我们已经编写了查询的一部分,但是我们需要使用第三个表,即颜色。现在,我们的主要信息表cars存储了汽车颜色ID,并链接回颜色ID列。因此,以与原始表类似的方式,我们可以加入第三个表:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    select
        a.ID,
        b.model
    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  |
    +----+--------+
    |  1 | Sports |
    |  3 | Sports |
    |  8 | Sports |
    | 10 | Sports |
    +----+--------+
    4 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
    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)

    好吧,那是我们背后的老板。现在,更详细地解释一下其中的一些。如您所见,我们语句中的from子句链接了我们的主表(我经常使用包含信息的表,而不是查找或维度表)。查询在所有表都切换的情况下也能正常工作,但是当我们在几个月后回到这个查询来读取它时就没什么意义了,所以通常最好尝试编写一个好的、易于理解的查询——直观地布局它,使用好的缩进,以便尽可能清晰地显示所有内容。如果你继续教别人,试着在他们的问题中灌输这些特征——特别是当你要对他们进行故障排除时。好的。

    以这种方式继续链接越来越多的表是完全可能的。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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

    虽然我忘记在join语句中加入一个表,但这里有一个示例。如果models表具有特定品牌的型号,因此也有一个称为brand的列,该列链接回ID字段上的brands表,则可以这样做:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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
                and b.brand=d.ID
    where
        b.ID=1

    可以看到,上面的查询不仅将联接的表链接到主cars表,还指定了已经联接的表之间的联接。如果不这样做,结果被称为笛卡尔连接——也就是说DBA代表坏的连接。笛卡尔连接(cartesian join)是返回行的连接,因为信息不会告诉数据库如何限制结果,所以查询返回符合条件的所有行。好的。

    因此,为了给出笛卡尔连接的示例,让我们运行以下查询:好的。

    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)

    天哪,真难看。然而,就数据库而言,这正是所要求的。在查询中,我们向cars索要ID,向models索要model。但是,因为我们没有指定如何联接表,所以数据库已经将第一个表中的每一行与第二个表中的每一行进行了匹配。好的。

    好吧,所以老板回来了,他又想要更多的信息。我想要同样的列表,但也包括4个单词。好的。

    然而,这给了我们一个很好的借口,让我们看看实现这一目标的两种不同方法。我们可以向这样的WHERE子句添加另一个条件:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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
        or b.ID=3

    虽然上面的工作非常好,但是让我们换个角度来看,这是一个很好的理由来说明union查询是如何工作的。好的。

    我们知道,以下各项将退还所有跑车:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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

    下面将返回所有4wd:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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

    因此,通过在它们之间添加一个union all子句,第二个查询的结果将附加到第一个查询的结果中。好的。

    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)

    如您所见,首先返回第一个查询的结果,然后返回第二个查询的结果。好的。

    在本例中,只使用第一个查询当然要容易得多,但是对于特定的情况,union查询可能非常好。它们是从不容易连接在一起的表中返回特定结果的一种很好的方法,或者对于完全不相关的表来说。不过,还有一些规则要遵循。好的。

    • 第一个查询中的列类型必须与下面每个其他查询中的列类型匹配。
    • 第一个查询中列的名称将用于标识整个结果集。
    • 每个查询中的列数必须相同。

    现在,您可能想知道使用unionunion all有什么区别。union查询将删除重复项,而union all查询将不删除重复项。这确实意味着在使用unionover union all时会有一个小的性能冲击,但是结果可能是值得的——不过我不会在这方面猜测这类事情。好的。

    在这张纸条上,这里可能值得注意一些附加的纸条。好的。

    • 如果我们想要订购结果,我们可以使用order by,但是您不能再使用别名了。在上面的查询中,附加一个order by a.ID将导致一个错误——就结果而言,该列称为ID,而不是a.ID,即使两个查询中使用了相同的别名。
    • 我们只能有一个order by语句,它必须是最后一个语句。

    对于下一个示例,我将向我们的表中添加一些额外的行。好的。

    我已将Holden添加到品牌表中。我还在cars中添加了一行,该行的color值为12,在颜色表中没有引用。好的。

    好吧,老板又回来了,汪汪叫着说-*我要清点一下我们每个品牌的数量和里面的车数!`-典型的情况是,我们只需进入讨论的一个有趣的部分,老板想要更多的工作。好的。

    好的,所以我们要做的第一件事就是得到一份完整的可能品牌的清单。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    select
        a.brand
    from
        brands a

    +--------+
    | brand  |
    +--------+
    | Ford   |
    | Toyota |
    | Nissan |
    | Smart  |
    | BMW    |
    | Holden |
    +--------+
    6 rows in set (0.00 sec)

    现在,当我们把这个加入到我们的cars表中时,我们得到了以下结果:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    select
        a.brand
    from
        brands a
            join cars b
                on a.ID=b.brand
    group by
        a.brand

    +--------+
    | brand  |
    +--------+
    | BMW    |
    | Ford   |
    | Nissan |
    | Smart  |
    | Toyota |
    +--------+
    5 rows in set (0.00 sec)

    这当然是个问题——我们没有看到任何关于可爱的Holden品牌的报道。好的。

    这是因为联接在两个表中都查找匹配的行。由于Holden型车辆中没有数据,因此不会返回数据。在这里我们可以使用outer联接。这将返回一个表中的所有结果,无论它们是否在另一个表中匹配:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    select
        a.brand
    from
        brands a
            left outer join cars b
                on a.ID=b.brand
    group by
        a.brand

    +--------+
    | brand  |
    +--------+
    | BMW    |
    | Ford   |
    | Holden |
    | Nissan |
    | Smart  |
    | Toyota |
    +--------+
    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
    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)

    这样,老板就躲开了。好的。

    现在,为了更详细地解释这一点,外部连接可以是leftright类型。左边或右边定义了完全包含哪个表。left outer join将包括左侧表中的所有行,而(您猜对了)right outer join将右侧表中的所有结果放入结果中。好的。

    有些数据库将允许使用full outer join,这将从两个表中返回结果(无论是否匹配),但并非所有数据库都支持这一点。好的。

    现在,我可能已经考虑到了这一点,您可能想知道是否可以在查询中合并联接类型——答案是肯定的,您完全可以。好的。

    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)

    正如我们所看到的,我们在查询中有两个外部联接,结果如预期的那样通过。好的。

    现在,您询问的其他类型的连接如何?交叉口怎么办?好的。

    不是所有的数据库都支持intersection,但是几乎所有的数据库都允许您通过一个连接(或者至少是一个结构良好的WHERE语句)创建一个交叉点。好的。

    交集是一种与上述union有点相似的连接类型,但不同之处在于它只返回联合所联接的各个查询之间相同(我的意思是相同)的数据行。只返回在各个方面都相同的行。好的。

    一个简单的例子如下:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    select
        *
    from
        colors
    where
        ID>2
    intersect
    select
        *
    from
        colors
    where
        id<4

    虽然普通的union查询将返回表的所有行(第一个查询返回ID>2上的任何内容,第二个查询返回ID<4上的任何内容,这将导致一个完整的集合),但是交叉查询只返回与id=3匹配的行,因为它满足这两个条件。好的。

    现在,如果您的数据库不支持intersect查询,则可以通过以下查询轻松解决上述问题:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    select
        a.ID,
        a.color,
        a.paint
    from
        colors a
            join colors b
                on a.ID=b.ID
    where
        a.ID>2
        and b.ID<4

    +----+-------+----------+
    | ID | color | paint    |
    +----+-------+----------+
    |  3 | Blue  | Metallic |
    +----+-------+----------+
    1 row in set (0.00 sec)

    如果您希望使用一个本身不支持交叉查询的数据库跨两个不同的表执行交叉,则需要在表的每一列上创建一个联接。好的。好啊。


    好的,我发现这篇文章很有趣,我想分享一下我在创建查询方面的一些知识。谢谢你的吹毛求疵。其他可能读到这篇文章并可能觉得我错了的人有101%的自由编辑和批评我的答案。(老实说,我非常感谢纠正我的错误。)

    我将在MySQL标签中发布一些常见问题。

    技巧1(符合多种条件的行)

    给定此架构

    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');

    问题

    找到至少属于ComedyRomance两类的所有电影。

    解决方案

    这个问题有时很棘手。似乎这样的查询就是答案:

    1
    2
    3
    4
    5
    6
    SELECT  DISTINCT a.MovieName
    FROM    MovieList a
            INNER JOIN CategoryList b
                ON a.ID = b.MovieID
    WHERE   b.CategoryName = 'Comedy' AND
            b.CategoryName = 'Romance'

    号sqlfiddle演示

    这绝对是错误的,因为它不会产生任何结果。对此的解释是,每行只有一个有效的CategoryName值。例如,第一个条件返回true,第二个条件始终为false。因此,通过使用AND操作符,这两个条件都应该是真的;否则,它将是假的。另一个查询是这样的,

    1
    2
    3
    4
    5
    SELECT  DISTINCT a.MovieName
    FROM    MovieList a
            INNER JOIN CategoryList b
                ON a.ID = b.MovieID
    WHERE   b.CategoryName IN ('Comedy','Romance')

    sqlfiddle演示

    结果仍然不正确,因为它与CategoryName上至少有一个匹配的记录相匹配。真正的解决方案是计算每部电影的记录实例数。实例数应与条件中提供的值总数匹配。

    1
    2
    3
    4
    5
    6
    7
    SELECT  a.MovieName
    FROM    MovieList a
            INNER JOIN CategoryList b
                ON a.ID = b.MovieID
    WHERE   b.CategoryName IN ('Comedy','Romance')
    GROUP BY a.MovieName
    HAVING COUNT(*) = 2

    。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');

    问题

    在每个软件上查找最新版本。显示以下列:SoftwareNameDescriptionsLatestVersion(来自versionno列)、DateReleased

    解决方案

    一些SQL开发人员错误地使用了MAX()聚合函数。他们倾向于这样创造,

    1
    2
    3
    4
    5
    6
    7
    SELECT  a.SoftwareName, a.Descriptions,
            MAX(b.VersionNo) AS LatestVersion, b.DateReleased
    FROM    Software a
            INNER JOIN VersionList b
                ON a.ID = b.SoftwareID
    GROUP BY a.ID
    ORDER BY a.ID

    。sqlfiddle演示

    (大多数RDBMS在这方面会产生语法错误,因为没有在group by子句中指定某些非聚合列),结果在每个软件上生成正确的LatestVersion,但显然DateReleased是不正确的。MySQL还不支持Window FunctionsCommon Table Expression,正如一些RDBMS已经支持的那样。解决这个问题的方法是创建一个subquery,它在每个软件上获得单个的最大versionNo,然后在其他表中加入。

    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演示(答案)

    就是这样。我一想起MySQL标签上的任何其他常见问题,我就会再发布一个。感谢您阅读这篇小文章。我希望你至少能从中得到一些知识。

    更新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
    select
        a.ID,
        b.brand
    from
        cars a
            join brands b
                on a.brand=b.ID

    现在,如果我们想简单地按品牌分类,我们当然可以写下:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    select
        b.brand,
        count(a.ID) as countCars
    from
        cars a
            join brands b
                on a.brand=b.ID
    group by
        b.brand

    +--------+-----------+
    | brand  | countCars |
    +--------+-----------+
    | BMW    |         2 |
    | Ford   |         2 |
    | Nissan |         1 |
    | Smart  |         1 |
    | Toyota |         5 |
    +--------+-----------+

    所以,我们应该能够简单地将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。好的。

    这就是我们可以使用子查询的地方——事实上,我们可以执行两种完全不同的子查询,它们将返回我们需要的相同结果。第一种方法是简单地将子查询放在select子句中。这意味着每次我们得到一行数据时,子查询都会运行,得到一列数据,然后将其弹出到我们的数据行中。好的。

    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)

    好的,所以我们有相同的结果(顺序略有不同——看起来数据库想要返回按照我们这次选择的第一列排序的结果)——但是正确的数字是相同的。好的。

    那么,这两者之间的区别是什么?我们什么时候应该使用每种类型的子查询?首先,让我们确保了解第二个查询是如何工作的。我们在查询的from子句中选择了两个表,然后编写了一个查询并告诉数据库它实际上是一个表,而数据库对此非常满意。使用这种方法可能会有一些好处(以及一些局限性)。最重要的是,这个子查询运行了一次。如果我们的数据库包含大量的数据,那么很可能比第一种方法有很大的改进。但是,当我们将它用作表时,我们必须引入额外的数据行,以便它们实际上可以连接回我们的数据行。如果要在上面的查询中使用简单的连接,我们还必须确保有足够的数据行。如果您还记得,联接将只回拉在联接两侧具有匹配数据的行。如果我们不小心,如果子查询中没有匹配的行,这可能会导致有效数据没有从cars表返回。好的。

    现在,回顾第一个子查询,也有一些限制。因为我们将数据拉回到一行中,所以只能拉回一行数据。查询的select子句中使用的子查询通常只使用聚合函数,如sumcountmax或其他类似的聚合函数。他们不必这样做,但这往往是他们的写作方式。好的。

    所以,在我们继续之前,让我们快速了解一下在哪里可以使用子查询。我们可以在where子句中使用它-现在,这个例子有点做作,就像在我们的数据库中一样,有更好的方法来获取以下数据,但是我们只举一个例子来看看:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    select
        ID,
        brand
    from
        brands
    where
        brand like '%o%'

    +----+--------+
    | ID | brand  |
    +----+--------+
    |  1 | Ford   |
    |  2 | Toyota |
    |  6 | Holden |
    +----+--------+
    3 rows in set (0.00 sec)

    这将返回包含名称中字母o的品牌ID和品牌名称列表(第二列仅用于显示品牌)。好的。

    现在,我们可以在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
        a.ID,
        b.brand
    from
        cars a
            join brands b
                on a.brand=b.ID
    where
        a.brand in (1,2,6)

    +----+--------+
    | ID | brand  |
    +----+--------+
    |  1 | Toyota |
    |  2 | Ford   |
    |  5 | Toyota |
    |  7 | Ford   |
    |  8 | Toyota |
    |  9 | Toyota |
    | 11 | Toyota |
    +----+--------+
    7 rows in set (0.00 sec)

    同样,您可以看到子查询和手动输入在从数据库返回时是如何更改行的顺序的。好的。

    当我们讨论子查询时,让我们看看如何处理子查询:好的。

    • 您可以将子查询放在另一个子查询中,依此类推。有一个限制取决于您的数据库,但缺少一些疯狂和疯狂的程序员的递归函数,大多数人永远不会达到这个限制。
    • 您可以在一个查询中放入许多子查询,其中一些子查询在select子句中,一些子查询在from子句中,另一些子查询在where子句中,只需记住,您放入的每个子查询都会使您的查询更加复杂,执行起来可能需要更长的时间。

    如果您需要编写一些有效的代码,可以通过多种方式编写查询,并查看(通过计时或使用解释计划)哪种查询是获得结果的最佳查询。第一种有效的方法并不总是最好的方法。好的。好啊。


    第3部分-技巧和有效代码mysql in()效率

    我想我会增加一些额外的小费和技巧。

    我看到的一个问题是,如何从两个表中得到不匹配的行,我认为最普遍接受的答案如下(基于我们的汽车和品牌表——霍尔登被列为一个品牌,但并未出现在汽车表中):

    1
    2
    3
    4
    5
    6
    7
    select
        a.ID,
        a.brand
    from
        brands a
    where
        a.ID not in(select brand from cars)

    是的,它会起作用的。

    1
    2
    3
    4
    5
    6
    +----+--------+
    | ID | brand  |
    +----+--------+
    |  6 | Holden |
    +----+--------+
    1 row in set (0.00 sec)

    但是,在某些数据库中,它是无效的。这里有一个链接指向一个关于堆栈溢出的问题,如果你想了解细节,这里有一篇非常深入的文章。

    简短的答案是,如果乐观者不能有效地处理它,那么使用如下查询获取不匹配的行可能会更好:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    select
        a.brand
    from
        brands a
            left join cars b
                on a.id=b.brand
    where
        b.brand is null

    +--------+
    | brand  |
    +--------+
    | Holden |
    +--------+
    1 row in set (0.00 sec)

    用子查询中的相同表更新表

    啊,另一个老的,但古蒂-旧的,你不能指定目标表'品牌'更新在从子句。

    MySQL不允许您在同一个表上运行带有子select的update...查询。现在,你可能在想,为什么不直接把它放在WHERE子句里?但是,如果您只想用max()日期更新包含大量其他行的行,该怎么办?在WHERE子句中不能完全这样做。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    update
        brands
    set
        brand='Holden'
    where
        id=
            (select
                id
            from
                brands
            where
                id=6);
    ERROR 1093 (HY000): You can't specify target table 'brands'
    for update in FROM clause

    所以,我们不能这样做,嗯?嗯,不完全是。有一个令人惊讶的大量用户不知道的秘密解决方案-尽管它确实包括一些你需要注意的黑客。

    您可以将子查询粘贴到另一个子查询中,这会在两个查询之间留出足够的间隙,以便它工作。但是,请注意,将查询固定在事务中可能最安全——这将防止在查询运行时对表进行任何其他更改。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    update
        brands
    set
        brand='Holden'
    where id=
        (select
            id
        from
            (select
                id
            from
                brands
            where
                id=6
            )
        as updateTable);

    Query OK, 0 rows affected (0.02 sec)
    Rows matched: 1  Changed: 0  Warnings: 0


    可以在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    |                |
    +-------+-------------+------+-----+---------+----------------+