如何使用SQL系列 之 如何在SQL中使用WHERE条件语句

引言

结构化查询语言 (SQL)语句中,WHERE子句限制了给定操作会影响哪些行。它们通过定义特定的条件(称为搜索条件)来实现这一点,每一行都必须满足这些条件才能受到操作的影响。

本指南将介绍WHERE子句中使用的通用语法。它还将概述如何在单个WHERE子句中组合多个搜索条件谓词以更细粒度的方式过滤数据,以及如何使用NOT操作符排除而不是包含满足给定搜索条件的行。

虽然本指南在示例中只使用SELECT语句,但这里解释的概念可以用于许多SQL操作。事实上,WHERE子句是UPDATEDELETE操作的关键组成部分。

前期准备

为了学习本指南,你需要一台运行某种使用SQL的关系数据库管理系统(RDBMS)的计算机。

注意:请注意,许多RDBMS使用它们自己独特的SQL实现。虽然本教程中概述的命令适用于大多数RDBMS,但如果你在MySQL以外的系统上测试它们,确切的语法或输出可能会有所不同。

你还需要一个装载了一些示例数据的数据库和表,可以在其中练习使用相关命令。

连接到MySQL并设置一个示例数据库

如果SQL数据库系统运行在远程服务器上,请从本地设备SSH到服务器:

ssh sammy@your_server_ip

然后打开MySQL服务器提示符,将==sammy==替换为你的MySQL用户账户的名称:

mysql -u sammy -p

创建一个名为where_db的数据库:

CREATE DATABASE where_db;

如果数据库成功创建,您将收到这样的输出:

OutputQuery OK, 1 row affected (0.01 sec)

要选择where_db数据库,运行以下USE语句:

USE where_db;
OutputDatabase changed

选择where_db后,在其中创建一个表。

为了理解本指南中使用的示例,假设您在当地的高尔夫球场经营高尔夫联赛。你决定跟踪联盟球员参加郊游时的个人表现信息。为此,您决定将信息存储在SQL数据库中。

你决定这个表需要6列:

  • name:每个高尔夫球手的名字,使用varchar数据类型表示,不超过20个字符
  • rounds_played:每个高尔夫球手打完的总回合数,用int数据类型表示
  • best:每位高尔夫球手在单次比赛中的最佳或最低得分,也用int表示。
  • worst:每位高尔夫球手在单次比赛中的最差或最高得分,同样用int表示
  • average:每个高尔夫球手在他们打过的几轮中得分的近似平均值。这一列将保存decimal类型的值,限制为最多4位,其中一位在小数点的右侧
  • wins:每个高尔夫球手在比赛小组中得分最低的轮数,用int类型表示

运行下面的CREATE TABLE语句来创建一个名为golfers的表,它包含以下6列:

CREATE TABLE golfers (
name varchar(20),
rounds_played int,
best int,
worst int,
average decimal (4,1),
wins int
);

然后加载包含一些示例数据的golfers表。运行下面的INSERT INTO操作来添加代表7名联赛高尔夫球手的7行数据:

INSERT INTO golfers
VALUES
('George', 22, 68, 103, 84.6, 3),
('Pat', 25, 65, 74, 68.7, 9),
('Grady', 11, 78, 118, 97.6, 0),
('Diane', 23, 70, 92, 78.8, 1),
('Calvin', NULL, 63, 76, 68.5, 7),
('Rose', NULL, 69, 84, 76.7, 4),
('Raymond', 18, 67, 92, 81.3, 1);

请注意,其中两行rounds_played的值是NULL。出于本教程的目的,假设这些高尔夫球手没有报告他们打了多少轮,因此这些值被记录为NULL

你可能还注意到,每个高尔夫球手的best值小于他们的worst值。这是因为,在常见的高尔夫规则中,高尔夫球手的得分是由他们将球打入球场每个洞所需的击球次数决定的,总击球次数最少的人就是获胜者。因此,与大多数其他运动不同,高尔夫球手的最佳分数会比最差分数

有了这些,你就可以开始学习如何在SQL中使用WHERE子句了。

使用WHERE子句过滤数据

在SQL中,语句是任何发送到数据库系统的操作,它将执行某种任务,如创建表,插入或删除数据,或更改列或表的结构。SQL语句是由各种子句,由特定的关键字和他们需要的信息。

正如引言中提到的,WHERE子句允许你过滤掉受SQL操作影响的某些行数据。在查询中,WHERE之后跟上查询条件,如以下示例:

SELECT columns_to_query
FROM table_to_query
WHERE search_condition;

WHERE关键字后面是一个搜索条件。一个搜索条件是由一个或多个谓词或表达式组成的集合,这些谓词或表达式可以计算一个或多个值表达式并返回“true”、“false”或“unknown”的结果。注意,在搜索条件只包含一个谓词的情况下,术语“搜索条件”和“谓词”是同义词。

WHERE子句搜索条件中的谓词可以有多种形式,但它们通常遵循以下语法:

. . .
WHERE column_name OPERATOR value_expression
. . .

在SQL中,值表达式——有时也称为标量表达式——是任何返回单个值的表达式。值表达式可以是字面量,比如字符串或数值,也可以是数学表达式。不过,大多数情况下,在WHERE子句的搜索条件中,至少有一个值表达式是列名。

当运行包含WHERE子句的SQL查询时,数据库管理系统将对FROM子句定义的逻辑表中的每一行应用搜索条件。然后,它将只返回所有谓词在搜索条件中求值为“true”的行。

为了说明这个想法,运行下面的查询。这将返回golfers表的name列中的所有值:

SELECT name
FROM golfers
WHERE (2 + 2) = 4;

这个查询包含一个WHERE子句,但它没有指定列名,而是使用(2 + 2)作为第一个值表达式,并测试它是否等于第二个值表达式4。因为(2 + 2) 总是等于4,所以对于每一行,这个搜索条件都计算为true。因此,结果集中的每一行都会被返回:

Output+---------+
| name    |
+---------+
| George  |
| Pat     |
| Grady   |
| Diane   |
| Calvin  |
| Rose    |
| Raymond |
+---------+
7 rows in set (0.01 sec)

这个WHERE子句不是很有用,因为它总是求值为“true”并总是返回表中的每一行。如前所述,在WHERE子句搜索条件中,通常至少使用一个列名作为值表达式。当运行查询时,数据库系统将依次对每一行应用搜索条件。通过在搜索条件中提供列名作为值表达式,你告诉DBMS使用该列中每行的值作为迭代搜索条件时的值表达式。

以下查询的WHERE子句对每一行应用了比前一个示例更排他的搜索条件。它将返回任何wins列值等于1的行中的namewins值:

SELECT name, wins
FROM golfers
WHERE wins = 1;

只有两名高尔夫球手刚好赢了一轮,因此这个查询只返回这两行:

Output+---------+------+
| name    | wins |
+---------+------+
| Diane   |    1 |
| Raymond |    1 |
+---------+------+
2 rows in set (0.01 sec)

前面的例子使用等号(=)测试是否两个值表达式是等价的,但您使用的运营商取决于您想要使用什么类型的谓词过滤结果集。

SQL标准定义了18种类型的谓词,尽管并不是所有的SQL实现都包含这些谓词。下面是5种最常用的谓词类型,以及对每种类型及其使用的操作符的简要说明。

比较

比较谓词使用比较运算符比较一个值(在查询,通常值在一个指定的列)。这6个比较运算符是:

  • =:测试两个值是否相等
SELECT name
FROM golfers
WHERE name = 'George';
Output+--------+
| name   |
+--------+
| George |
+--------+
1 row in set (0.00 sec)
  • <>:测试两个值是否相等
SELECT name, wins
FROM golfers
WHERE wins <> 1;
Output+--------+------+
| name   | wins |
+--------+------+
| George |    3 |
| Pat    |    9 |
| Grady  |    0 |
| Calvin |    7 |
| Rose   |    4 |
+--------+------+
5 rows in set (0.00 sec)
  • <:测试第一个值是否小于第二个值
SELECT name, wins
FROM golfers
WHERE wins < 1;
Output+-------+------+
| name  | wins |
+-------+------+
| Grady |    0 |
+-------+------+
1 row in set (0.00 sec)
  • >:测试第一个值是否大于第二个值
SELECT name, wins
FROM golfers
WHERE wins > 1;
Output+--------+------+
| name   | wins |
+--------+------+
| George |    3 |
| Pat    |    9 |
| Calvin |    7 |
| Rose   |    4 |
+--------+------+
4 rows in set (0.00 sec)
  • <=:测试第一个值是否小于或等于第二个
SELECT name, wins
FROM golfers
WHERE wins <= 1;
Output+---------+------+
| name    | wins |
+---------+------+
| Grady   |    0 |
| Diane   |    1 |
| Raymond |    1 |
+---------+------+
3 rows in set (0.00 sec)
  • >=:测试第一个值是否大于或等于第二个值
SELECT name, wins
FROM golfers
WHERE wins >= 1;
Output+---------+------+
| name    | wins |
+---------+------+
| George  |    3 |
| Pat     |    9 |
| Diane   |    1 |
| Calvin  |    7 |
| Rose    |    4 |
| Raymond |    1 |
+---------+------+
6 rows in set (0.00 sec)

Null(IS NULL

使用IS NULL操作符的谓词测试给定列中的值是否为NULL。如果是,则谓词的计算结果为“true”,结果集中就包含了这一行:

SELECT name, rounds_played
FROM golfers
WHERE rounds_played IS NULL;
Output+--------+---------------+
| name   | rounds_played |
+--------+---------------+
| Calvin |          NULL |
| Rose   |          NULL |
+--------+---------------+
2 rows in set (0.00 sec)

区间(BETWEEN

范围谓词使用BETWEEN操作符来测试指定的列值是否落在两个值表达式之间:

SELECT name, best
FROM golfers
WHERE best BETWEEN 67 AND 73;
Output+---------+------+
| name    | best |
+---------+------+
| George  |   68 |
| Diane   |   70 |
| Rose    |   69 |
| Raymond |   67 |
+---------+------+
4 rows in set (0.00 sec)

包含(IN

成员谓词使用IN操作符来测试一个值是否是给定集合的成员:

SELECT name, best
FROM golfers
WHERE best IN (65, 67, 69, 71);
Output+---------+------+
| name    | best |
+---------+------+
| Pat     |   65 |
| Rose    |   69 |
| Raymond |   67 |
+---------+------+
3 rows in set (0.00 sec)

通配符(%_

模式匹配谓词使用LIKE操作符来测试一个值是否匹配包含一个或多个通配符的字符串模式,也称为通配符。SQL定义了两个通配符,%_:

  • _: 下划线表示单个未知字符
SELECT name, rounds_played
FROM golfers
WHERE rounds_played LIKE '2_';
Output+--------+---------------+
| name   | rounds_played |
+--------+---------------+
| George |            22 |
| Pat    |            25 |
| Diane  |            23 |
+--------+---------------+
3 rows in set (0.00 sec)
  • %: 百分比符号表示零个或多个未知字符
SELECT name, rounds_played
FROM golfers
WHERE name LIKE 'G%';
Output+--------+---------------+
| name   | rounds_played |
+--------+---------------+
| George |            22 |
| Grady  |            11 |
+--------+---------------+
2 rows in set (0.00 sec)

用AND和OR组合多个查询条件

有时候,你需要比使用单个搜索条件谓词的WHERE子句提供的更细粒度的过滤结果。另一方面,也可能有满足多个搜索条件之一的行在结果集中是可接受的。在这种情况下,你可以编写包含多个谓词的WHERE子句,分别使用ANDOR操作符。

要开始使用这些操作符,请运行以下查询,它从golfers表的namebestworstaverage列中返回值。它的WHERE子句包含两个查询条件,由AND分隔:

SELECT name, best, worst, average
FROM golfers
WHERE best < 70 AND worst < 96;

第一个谓词测试每一行的best值是否小于70,而第二个测试每一行的worst价值是否小于96。如果任何一个测试的结果为“false”,则该行不会返回到结果集中:

Output+---------+------+-------+---------+
| name    | best | worst | average |
+---------+------+-------+---------+
| Pat     |   65 |    74 |    68.7 |
| Calvin  |   63 |    76 |    68.5 |
| Rose    |   69 |    84 |    76.7 |
| Raymond |   67 |    92 |    81.3 |
+---------+------+-------+---------+
4 rows in set (0.00 sec)

接下来,运行以下查询。这与前面的例子相同,只是它用OR操作符而不是AND来分隔两个谓词:

SELECT name, best, worst, average
FROM golfers
WHERE best < 70 OR worst < 96;

因为只有一个谓词的求值为“true”才能返回一行,所以这个结果集比前面的例子多了两行:

Output+---------+------+-------+---------+
| name    | best | worst | average |
+---------+------+-------+---------+
| George  |   68 |   103 |    84.6 |
| Pat     |   65 |    74 |    68.7 |
| Diane   |   70 |    92 |    78.8 |
| Calvin  |   63 |    76 |    68.5 |
| Rose    |   69 |    84 |    76.7 |
| Raymond |   67 |    92 |    81.3 |
+---------+------+-------+---------+
6 rows in set (0.00 sec)

你可以在一个WHERE子句中包含任意数量的谓词,只要你用正确的语法将它们组合起来。然而,随着搜索条件变得越来越复杂,预测它们将过滤哪些数据变得越来越困难。

需要注意的是,数据库系统通常优先考虑AND操作符。这意味着任何由AND操作符分隔的谓词(或在两个以上谓词的情况下的操作符)都被视为一个单独的、隔离的搜索条件,在WHERE子句中的任何其他谓词之前进行测试。

为了说明问题,运行下面的查询,它从nameaverageworstrounds_played列中返回满足WHERE子句中定义的搜索条件的任何行:

SELECT name, average, worst, rounds_played
FROM golfers 
WHERE average < 85 OR worst < 95 AND rounds_played BETWEEN 19 AND 23;

这个查询首先测试由AND操作符分隔的谓词是否worst < 95rounds_played BETWEEN 19 AND 23——在当前迭代中,这两个值都为“true”。如果是,那么该行将出现在结果集中。但是如果任何一个求值为“false”,查询将检查当前行的average值是否小于85。如果是,则返回该行:

Output+---------+---------+-------+---------------+
| name    | average | worst | rounds_played |
+---------+---------+-------+---------------+
| George  |    84.6 |   103 |            22 |
| Pat     |    68.7 |    74 |            25 |
| Diane   |    78.8 |    92 |            23 |
| Calvin  |    68.5 |    76 |          NULL |
| Rose    |    76.7 |    84 |          NULL |
| Raymond |    81.3 |    92 |            18 |
+---------+---------+-------+---------------+
6 rows in set (0.00 sec)

您可以通过将两个或多个查询条件包装在括号中来确定它们的优先级。下面的示例与前一个相同,但它封装了average < 85worst < 95查询条件,用OR操作符分隔,放在括号中:

SELECT name, average, worst, rounds_played
FROM golfers
WHERE (average < 85 OR worst < 95) AND rounds_played BETWEEN 19 AND 23;

因为前两个谓词被圆括号包围,后续的AND操作符将它们视为一个离散的搜索条件,必须求值为“true”。如果这两个谓词- average < 85worst < 95——求值为“false”,那么整个搜索条件求值为“false”,查询会立即从结果集中删除这一行,然后再继续求值下一行。

然而,如果前两个谓词中的任何一个求值为“true”,查询就会测试给定高尔夫球手的rounds_played值是否在19到23之间。如果是,则在结果集中返回这一行:

Output+--------+---------+-------+---------------+
| name   | average | worst | rounds_played |
+--------+---------+-------+---------------+
| George |    84.6 |   103 |            22 |
| Diane  |    78.8 |    92 |            23 |
+--------+---------+-------+---------------+
2 rows in set (0.00 sec)

结果表明,通过优先级的谓词集和包装在括号,否则相同的查询可以返回结果集明显不同。

尽管并不总是需要这样做,但建议在单个搜索条件中组合两个以上谓词时始终使用括号。这样做有助于使查询更可读、更容易理解。

NOT排除结果

到目前为止,本指南的所有示例都集中在如何使用WHERE子句编写查询,在结果集中只包含满足指定搜索条件的行。然而,你可以通过在WHERE子句中包含NOT操作符来编写排除特定行的查询。

范围、会员和模式匹配谓词的条款,包括NOT操作符通常遵循这一语法:

. . .
WHERE column_name NOT OPERATOR value_expression
. . .

为了说明这一点,运行下面的查询。这将返回golfers表的name列的值,但其WHERE子句中的NOT操作符将导致DBMS排除所有匹配通配符模式的行:

SELECT name
FROM golfers
WHERE name NOT LIKE 'R%';
Output+--------+
| name   |
+--------+
| George |
| Pat    |
| Grady  |
| Diane  |
| Calvin |
+--------+
5 rows in set (0.00 sec)

当将NOT操作符添加到IS NULL谓词时,情况略有不同。在这种情况下,你可以将NOT放在ISNULL之间,如下面的例子所示。这个查询返回所有rounds_played值不为Null的高尔夫球手的namerounds_played值:

SELECT name, rounds_played
FROM golfers
WHERE rounds_played IS NOT NULL;
Output+---------+---------------+
| name    | rounds_played |
+---------+---------------+
| George  |            22 |
| Pat     |            25 |
| Grady   |            11 |
| Diane   |            23 |
| Raymond |            18 |
+---------+---------------+
5 rows in set (0.00 sec)

你也可以将NOT操作符紧跟在WHERE关键字之后。如果你根据是否满足多个搜索条件来排除行,这很有用,如下面的示例查询返回golfers的nameaveragebestwins值:

SELECT name, average, best, wins
FROM golfers
WHERE NOT (average < 80 AND best < 70) OR wins = 9;
Output+---------+---------+------+------+
| name    | average | best | wins |
+---------+---------+------+------+
| George  |    84.6 |   68 |    3 |
| Pat     |    68.7 |   65 |    9 |
| Grady   |    97.6 |   78 |    0 |
| Diane   |    78.8 |   70 |    1 |
| Raymond |    81.3 |   67 |    1 |
+---------+---------+------+------+
5 rows in set (0.00 sec)

请注意结果集的第二行。帕特的“平均”分数不到80分,她的“最佳”分数也不到70分。然而,她的行仍然包含在结果集中,因为NOT操作符只是对括号内的搜索条件求反。

回想一下,当您将由ANDOR分隔的多个谓词包装在括号中时,SQL将优先考虑这些谓词,并将它们视为单个隔离的搜索条件。因此,NOT操作符只排除基于前两个谓词的行: average < 80best < 70。但是它包含基于第三个谓词的行,wins = 9

你可以重写这个查询,根据第三个条件和前两个条件将它们都包含在括号中,如下所示:

SELECT name, average, best, wins
FROM golfers
WHERE NOT ((average < 80 AND best < 70) OR wins = 9);
Output+---------+---------+------+------+
| name    | average | best | wins |
+---------+---------+------+------+
| George  |    84.6 |   68 |    3 |
| Grady   |    97.6 |   78 |    0 |
| Diane   |    78.8 |   70 |    1 |
| Raymond |    81.3 |   67 |    1 |
+---------+---------+------+------+
4 rows in set (0.00 sec)

根据SQL实现的不同,如果你在比较操作符之前包含NOT,数据库系统可能会认为查询语法无效。举个例子,试着运行这个查询:

SELECT name
FROM golfers
WHERE name NOT = 'Grady';

对于MySQL及其衍生版本,这将导致错误:

OutputERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '= 'Grady'' at line 1

这个错误的原因是NOT操作符通常不与比较操作符(=<><<<=>>=)一起使用,因为你可以通过将一个比较操作符替换为另一个比较操作符来实现相反的效果,该比较操作符将返回第一个比较操作符将排除的行。例如,你可以将等价运算符(=)替换为不等价运算符(<>)。

总结

通过阅读本指南,你学习了如何编写WHERE子句,使查询只返回满足指定条件的行。你还学习了如何在单个查询中组合多个谓词和搜索条件,以及如何使用NOT关键字从结果集中排除信息。

虽然这里显示的命令在大多数关系型数据库上都可以工作,但请注意,每个SQL数据库都使用自己独特的语言实现。关于每个命令及其所有选项的更完整描述,读者可以查阅相应DBMS的官方文档。

你可能感兴趣的:(SQL,mysql,数据库,sql)