跳转到内容

结构化查询语言/数据查询语言

100% developed
来自维基教科书,开放的世界,开放的书籍

← 关系型数据库 | 数据操作语言 → 数据查询语言用于从数据库中提取数据。它不修改数据库中的任何数据。它只描述一个查询:SELECT。

SQL 数据类型

[编辑 | 编辑源代码]

每个列都有一个类型。以下是标准的 SQL 数据类型

数据类型 说明 允许的值 示例
VARCHAR(n) 最大长度为 n 的字符串 [0-9a-zA-Z]+{n} "foo"
CHAR(n) 固定长度为 n 的字符串 [0-9a-zA-Z]{n} "foo"
SMALLINT 16 位有符号整数 \-?[0-9]+ 584
INTEGER 32 位有符号整数 \-?[0-9]+ -8748
FLOAT 十进制浮点数 \-?[0-9]+[\.[0-9]+]? 48.96
NUMBER(n,[d]) n 位数(如果提到,则有 d 位小数位) \-?[0-9]+[\.[0-9]+]? 484.65
DATE 日期(YYYY-MM-DD) [0-9][0-9][0-9][0-9]\-[0-1][0-9]\-[0-3][0-9] 2009-03-24
TIME 60 分钟的时间段;一天的二十四分之一 [0-2][0-9]\:[0-5][0-9]\:[0-5][0-9] 11:24:56
TIMESTAMP 日期和时间 [0-9]+ 18648689595962
BLOB 任何二进制数据 任何

没有布尔类型。整数代替使用。

SELECT 查询

[编辑 | 编辑源代码]

SELECT 查询的完整语法如下

SELECT[ ALL| DISTINCT] <column name>[[ AS] <alias>][,[ ALL| DISTINCT] <column name>[[ AS] <alias>]]*
FROM <table>[[ AS] <alias>|[[ FULL| LEFT| RIGHT] OUTER| INNER] JOIN <table> ON <expression>]
 [, <table>[[ AS] <alias>|[[ FULL| LEFT| RIGHT] OUTER| INNER] JOIN <table> ON <expression>]]*

[WHERE <predicate>[{ AND| OR} <predicate>]*]
[GROUP BY <column name>[, <column name>]*
 [HAVING <predicate>[{ AND| OR} <predicate>]]*]
]
[ORDER BY <column name>[ ASC| DESC][, <column name>[ ASC| DESC]]*]
[FETCH FIRST <count> ROWS ONLY];

第一个查询

[编辑 | 编辑源代码]

让我们创建一个包含多个列的表格 reunion

reunion
id_reunion INTEGER
name VARCHAR(20)
description VARCHAR(255)
priority CHAR(1)
planned SMALLINT
date DATE
hour TIME
duration INTEGER
# id_office INTEGER
pdf_report BLOB

...让我们填充它

reunion
id_reunion name description priority planned date hour duration # id_office pdf_report
1 Planning 我们需要计划项目。 A 1 2008-03-24 10:30:00 60 35 48644...846348
2 Progress 我们做了什么。 C 1 2008-05-12 14:00:00 30 13 9862...15676
3 Change 我们需要在项目中改变什么。 B 1 2008-06-03 9:30:00 90 41 34876...4846548
4 Presentation 项目的展示。 D 0 2008-09-11 15:30:00 120 27
5 Reporting 对新手进行解释。 B 1 2009-03-15 14:00:00 60 7 19739...37718
6 Learning 已安装新软件版本。 B 1 2009-09-21 16:00:00 120 11 785278...37528

让我们做一个简单的查询。以下查询只返回 reunion 表的内容

  • 查询:
SELECT *
FROM reunion;
  • 结果:
|----------------------------------------------------------------------------------------------------------------------------------------------------|
|id_reunion |name         |description                                |priority |planned |date       |hour     |duration |id_office |pdf_report      |
|-----------|-------------|-------------------------------------------|---------|--------|-----------|---------|---------|----------|----------------|
|1          |Planning     |We need to plan the project.               |A        |1       |2008-03-24 |10:30:00 |60       |35        |48644...846348  |
|2          |Progress     |What we have done.                         |C        |1       |2008-05-12 |14:00:00 |30       |13        |9862...15676    |
|3          |Change       |What we need to change in the project.     |B        |1       |2008-06-03 |9:30:00  |90       |41        |34876...4846548 |
|4          |Presentation |Presentation of the project.               |D        |0       |2008-09-11 |15:30:00 |120      |27        |NULL            |
|5          |Reporting    |Explanation to the new beginner.           |B        |1       |2009-03-15 |14:00:00 |60       |7         |19739...37718   |
|6          |Learning     |A new software version has been installed. |B        |1       |2009-09-21 |16:00:00 |120      |11        |785278...37528  |
|----------------------------------------------------------------------------------------------------------------------------------------------------|

结果的形式取决于客户端应用程序。它可以作为文本输出(后端)、HTML 页面(瘦客户端)、程序对象(中间件)等返回... 语句、查询、子句(SELECT、FROM...)、指令和运算符不区分大小写,但它们通常用大写字母书写以提高可读性。

SELECT 和 FROM 子句是 SELECT 查询的两个必需子句

  • FROM : 列出查询用于返回数据的表,
  • SELECT : 列出要返回的数据。

WHERE 子句

[编辑 | 编辑源代码]

WHERE 子句不影响查询返回的列,而是影响行。它通过对行应用谓词来过滤行。谓词指定可以为真或假的条件。SQL 可以处理结果未知的条件。例如,以下查询返回优先级级别为 B 的重聚

  • 查询:
SELECT *
FROM reunion
WHERE reunion.priority = 'B';
  • 结果:
|----------------------------------------------------------------------------------------------------------------------------------------------------|
|id_reunion |name         |description                                |priority |planned |date       |hour     |duration |id_office |pdf_report      |
|-----------|-------------|-------------------------------------------|---------|--------|-----------|---------|---------|----------|----------------|
|3          |Change       |What we need to change in the project.     |B        |1       |2008-06-03 |9:30:00  |90       |41        |34876...4846548 |
|5          |Reporting    |Explanation to the new beginner.           |B        |1       |2009-03-15 |14:00:00 |60       |7         |19739...37718   |
|6          |Learning     |A new software version has been installed. |B        |1       |2009-09-21 |16:00:00 |120      |11        |785278...37528  |
|----------------------------------------------------------------------------------------------------------------------------------------------------|

如果表名不含糊,则可以省略表名。

与第二个操作数相比,第一个操作数可以是 

  • 相等 : =
  • 不同 : <>
  • 小于 : <
  • 小于或等于 : <=
  • 大于 : >
  • 大于或等于 : >=

以下查询返回优先级级别不为 B 的重聚

  • 查询:
SELECT *
FROM reunion
WHERE priority <> 'B';
  • 结果:
|----------------------------------------------------------------------------------------------------------------------------------------------------|
|id_reunion |name         |description                                |priority |planned |date       |hour     |duration |id_office |pdf_report      |
|-----------|-------------|-------------------------------------------|---------|--------|-----------|---------|---------|----------|----------------|
|1          |Planning     |We need to plan the project.               |A        |1       |2008-03-24 |10:30:00 |60       |35        |48644...846348  |
|2          |Progress     |What we have done.                         |C        |1       |2008-05-12 |14:00:00 |30       |13        |9862...15676    |
|4          |Presentation |Presentation of the project.               |D        |0       |2008-09-11 |15:30:00 |120      |27        |NULL            |
|----------------------------------------------------------------------------------------------------------------------------------------------------|

运算符

[编辑 | 编辑源代码]

WHERE 子句可以使用 AND(所有条件都必须为真)和 OR(只有一个条件需要为真)运算符包含多个条件。OR 运算符是包含性的(多个条件可以为真)。括号可以指示求值的顺序。NOT 会反转一个条件。以下查询返回优先级级别为 B 并且持续时间超过一小时 或者在 2008/05/12 发生的重聚

  • 查询:
SELECT *
FROM reunion
WHERE (priority = 'B' AND NOT duration <= 60) OR date = '2008-05-12';
  • 结果:
|----------------------------------------------------------------------------------------------------------------------------------------------------|
|id_reunion |name         |description                                |priority |planned |date       |hour     |duration |id_office |pdf_report      |
|-----------|-------------|-------------------------------------------|---------|--------|-----------|---------|---------|----------|----------------|
|2          |Progress     |What we have done.                         |C        |1       |2008-05-12 |14:00:00 |30       |13        |9862...15676    |
|3          |Change       |What we need to change in the project.     |B        |1       |2008-06-03 |9:30:00  |90       |41        |34876...4846548 |
|6          |Learning     |A new software version has been installed. |B        |1       |2009-09-21 |16:00:00 |120      |11        |785278...37528  |
|----------------------------------------------------------------------------------------------------------------------------------------------------|

LIKE 允许简化的正则表达式匹配。它可以应用于文本列(CHAR、VARCHAR 等)。

  • 字母数字字符只匹配相同的文本,
  • % 是一个通配符,匹配任何文本,
  • _ 是一个通配符,匹配任何单个字符,

以下查询返回以 "ing" 结尾且描述中包含 " the " 的重聚

  • 查询:
SELECT *
FROM reunion
WHERE name LIKE '%ing' AND description LIKE '% the %';
  • 结果:
|----------------------------------------------------------------------------------------------------------------------------------------------------|
|id_reunion |name         |description                                |priority |planned |date       |hour     |duration |id_office |pdf_report      |
|-----------|-------------|-------------------------------------------|---------|--------|-----------|---------|---------|----------|----------------|
|1          |Planning     |We need to plan the project.               |A        |1       |2008-03-24 |10:30:00 |60       |35        |48644...846348  |
|5          |Reporting    |Explanation to the new beginner.           |B        |1       |2009-03-15 |14:00:00 |60       |7         |19739...37718   |
|----------------------------------------------------------------------------------------------------------------------------------------------------|

BETWEEN 和 IN

[编辑 | 编辑源代码]

BETWEEN 匹配可以是数字、日期或时间的范围值。IN 匹配允许值的列表。以下查询返回在 2008-04-01 和 2009-04-01 之间发生的并且优先级级别为 A、B 或 D 的重聚

  • 查询:
SELECT *
FROM reunion
WHERE date BETWEEN '2008-04-01' AND '2009-04-01' AND priority IN ('A', 'B', 'D');
  • 结果:
|----------------------------------------------------------------------------------------------------------------------------------------------------|
|id_reunion |name         |description                                |priority |planned |date       |hour     |duration |id_office |pdf_report      |
|-----------|-------------|-------------------------------------------|---------|--------|-----------|---------|---------|----------|----------------|
|3          |Change       |What we need to change in the project.     |B        |1       |2008-06-03 |9:30:00  |90       |41        |34876...4846548 |
|4          |Presentation |Presentation of the project.               |D        |0       |2008-09-11 |15:30:00 |120      |27        |NULL            |
|5          |Reporting    |Explanation to the new beginner.           |B        |1       |2009-03-15 |14:00:00 |60       |7         |19739...37718   |
|----------------------------------------------------------------------------------------------------------------------------------------------------|

EXISTS 通常与子查询一起使用。如果列表(即子查询的结果集)不为空,则该谓词为真。此关键字允许使用与返回行不直接关联的数据(即它们未连接、未链接、未关联... 返回的行),因此在这种情况下无法使用连接来过滤返回的行。例如,我们想要检索所有至少有一个重聚是其他重聚的两倍长的重聚

  • 查询:
SELECT *
FROM reunion r1
WHERE EXISTS (
  SELECT r2.id_reunion
  FROM reunion r2
  WHERE r2.duration = r1.duration * 2
);
  • 结果:
|----------------------------------------------------------------------------------------------------------------------------------------------------|
|id_reunion |name         |description                                |priority |planned |date       |hour     |duration |id_office |pdf_report      |
|-----------|-------------|-------------------------------------------|---------|--------|-----------|---------|---------|----------|----------------|
|1          |Planning     |We need to plan the project.               |A        |1       |2008-03-24 |10:30:00 |60       |35        |48644...846348  |
|2          |Progress     |What we have done.                         |C        |1       |2008-05-12 |14:00:00 |30       |13        |9862...15676    |
|5          |Reporting    |Explanation to the new beginner.           |B        |1       |2009-03-15 |14:00:00 |60       |7         |19739...37718   |
|----------------------------------------------------------------------------------------------------------------------------------------------------|

此查询中使用了另一个重聚的持续时间,而这两个行之间没有连接、链接或关系。如果不使用 EXISTS,则无法完成此条件。请注意,子查询使用别名 r1,而此别名是在主查询中定义的。

EXISTS 也用于匹配缺少的数据。让我们记住 employee 表和 members 表

employee
id_employee firstname lastname phone mail
1 Big BOSS 936854270 [email protected]
2 John DOE 936854271 [email protected]
3 Linus TORVALDS 936854272 [email protected]
4 Jimmy WALES 936854273 [email protected]
5 Larry PAGE 936854274 [email protected]
6 Max THE GOOGLER 936854275 [email protected]
7 Jenny THE WIKIPEDIAN 936854276 [email protected]
members
# id_employee # id_project
3 2
2 1
4 3
5 1
2 3
6 1
7 3

以下查询返回未链接到任何项目的员工(即 members 表中没有他们关系的员工)

  • 查询:
SELECT *
FROM employees e
WHERE NOT EXISTS (
  SELECT m.id_employee
  FROM members m
  WHERE m.id_employee = e.id_employee
);
  • 结果:
|------------------------------------------------------------------|
|id_employee |firstname |lastname |phone     |mail                 |
|------------|----------|---------|----------|---------------------|
|1           |Big       |BOSS     |936854270 |[email protected] |
|------------------------------------------------------------------|

IS NULL 用于测试列是否已填充。它通常用于外键列。

FROM 子句

[edit | edit source]

FROM 子句定义用于查询的表,但它也可以联接表。JOIN 创建一个包含两个表列的超级表,用于查询。为了解释联接是什么,我们考虑两个没有主键或外键的古老表

table_1
common_value specific_value_1
red 9999
grey 6666
white 0000
purple 7777
purple 2222
black 8888
table_2
common_value specific_value_2
green HHHHHH
yellow PPPPPP
black FFFFFF
red OOOOOO
red LLLLLL
blue RRRRRR

我们希望将来自不同表列的值关联起来,这些值匹配每个表中给定列的值。

FULL OUTER JOIN

[edit | edit source]

联接是通过将一个表上的列与另一个表上的列进行匹配来实现的。在 FULL OUTER JOIN 之后,对于给定值(red),对于一个表上具有此值的给定行([ red | 9999 ]),将为另一个表上匹配的每一行创建一个行([ red | OOOOOO ] 和 [ red | LLLLLL ])。如果一个值仅存在于一个表中,则会创建一个行,并使用 NULL 列将其补全。

FROM table_1 FULL OUTER JOIN table_2 ON table_1.common_value = table_2.common_value
common_value specific_value_1 specific_value_2
red 9999 OOOOOO
red 9999 LLLLLL
grey 6666 NULL
white 0000 NULL
purple 7777 NULL
purple 2222 NULL
black 8888 FFFFFF
green NULL HHHHHH
yellow NULL PPPPPP
blue NULL RRRRRR

RIGHT OUTER JOIN

[edit | edit source]

RIGHT OUTER JOIN 类似于 FULL OUTER JOIN,但它不会为左表中不存在的值创建行。

FROM table_1 RIGHT OUTER JOIN table_2 ON table_1.common_value = table_2.common_value
common_value specific_value_1 specific_value_2
red 9999 OOOOOO
red 9999 LLLLLL
black 8888 FFFFFF
green NULL HHHHHH
yellow NULL PPPPPP
blue NULL RRRRRR

LEFT OUTER JOIN

[edit | edit source]

LEFT OUTER JOIN 类似于 FULL OUTER JOIN,但它不会为右表中不存在的值创建行。

FROM table_1 LEFT OUTER JOIN table_2 ON table_1.common_value = table_2.common_value
common_value specific_value_1 specific_value_2
red 9999 OOOOOO
red 9999 LLLLLL
grey 6666 NULL
white 0000 NULL
purple 7777 NULL
purple 2222 NULL
black 8888 FFFFFF

INNER JOIN

[edit | edit source]

INNER JOIN 类似于 FULL OUTER JOIN,但它仅为左表和右表中都存在的值创建行。

FROM table_1 INNER JOIN table_2 ON table_1.common_value = table_2.common_value
common_value specific_value_1 specific_value_2
red 9999 OOOOOO
red 9999 LLLLLL
black 8888 FFFFFF

别名

[edit | edit source]

FROM 子句可以声明多个表,用,隔开,并且可以使用 AS 关键字为表名定义别名,这允许用户使用相同的表进行多次联接。以下查询等同于上面的 INNER JOIN

  • 查询:
SELECT *
FROM table_1 AS t1, table_2 AS t2
WHERE t1.common_value = t2.common_value

AS 关键字可以省略。

SELECT 子句

[edit | edit source]

SELECT 子句不影响查询处理的数据,而是影响返回给用户的数据。* 返回联接和过滤后的所有处理数据。否则,SELECT 子句将列出用,隔开的表达式。

表达式可以是表名、表名和用点号隔开的列名,或者如果它不含糊则可以是简单的列名。SELECT 子句还允许使用求值表达式,如加法、减法、串联等。表达式后可以使用 AS 关键字后跟别名。AS 关键字可以省略。

以下是一个例子

  • 查询:
SELECT reunion.id_reunion, concat(name, ' : ', reunion.description) n, priority AS p, planned * 10 AS plan, duration + 10 AS reunion_length
FROM reunion;
  • 结果:
|-------------------------------------------------------------------------------------------|
|id_reunion |n                                                     |p |plan |reunion_length |
|-----------|------------------------------------------------------|--|-----|---------------|
|1          |Planning : We need to plan the project.               |A |10   |70             |
|2          |Progress : What we have done.                         |C |10   |40             |
|3          |Change : What we need to change in the project.       |B |10   |100            |
|4          |Presentation : Presentation of the project.           |D |0    |130            |
|5          |Reporting : Explanation to the new beginner.          |B |10   |70             |
|6          |Learning : A new software version has been install... |B |10   |130            |
|-------------------------------------------------------------------------------------------|

表达式也可以是以下聚合函数

  • count(*):返回行的数量。
  • max(<column_name>):列的最大值。
  • min(<column_name>):列的最小值。

以下是一个新的例子

  • 查询:
SELECT count(*) * 10 AS c, max(date) AS latest_date, min(reunion.date) oldest_date
FROM reunion;
  • 结果:
|-----------------------------|
|c  |latest_date |oldest_date |
|---|------------|------------|
|60 |2009-09-21  |2008-03-24  |
|-----------------------------|

ORDER BY 子句

[edit | edit source]

ORDER BY 子句根据一个或多个列对查询返回的行进行排序。排序使用第一个提到的列进行。第二个列用于对第一列中具有相同值的行进行排序,依此类推。可以在每个列之后添加 ASC 或 DESC 关键字。ASC 表示升序排序。DESC 表示降序排序。默认值为降序排序。让我们执行两个简单的请求,第一个只按一个列排序,第二个按两个列排序

  • 查询:
SELECT *
FROM reunion
ORDER BY priority ASC;
  • 结果:
|----------------------------------------------------------------------------------------------------------------------------------------------------|
|id_reunion |name         |description                                |priority |planned |date       |hour     |duration |id_office |pdf_report      |
|-----------|-------------|-------------------------------------------|---------|--------|-----------|---------|---------|----------|----------------|
|1          |Planning     |We need to plan the project.               |A        |1       |2008-03-24 |10:30:00 |60       |35        |48644...846348  |
|3          |Change       |What we need to change in the project.     |B        |1       |2008-06-03 |9:30:00  |90       |41        |34876...4846548 |
|5          |Reporting    |Explanation to the new beginner.           |B        |1       |2009-03-15 |14:00:00 |60       |7         |19739...37718   |
|6          |Learning     |A new software version has been installed. |B        |1       |2009-09-21 |16:00:00 |120      |11        |785278...37528  |
|2          |Progress     |What we have done.                         |C        |1       |2008-05-12 |14:00:00 |30       |13        |9862...15676    |
|4          |Presentation |Presentation of the project.               |D        |0       |2008-09-11 |15:30:00 |120      |27        |NULL            |
|----------------------------------------------------------------------------------------------------------------------------------------------------|
  • 查询:
SELECT *
FROM reunion
ORDER BY priority ASC, duration DESC;
  • 结果:
|----------------------------------------------------------------------------------------------------------------------------------------------------|
|id_reunion |name         |description                                |priority |planned |date       |hour     |duration |id_office |pdf_report      |
|-----------|-------------|-------------------------------------------|---------|--------|-----------|---------|---------|----------|----------------|
|1          |Planning     |We need to plan the project.               |A        |1       |2008-03-24 |10:30:00 |60       |35        |48644...846348  |
|6          |Learning     |A new software version has been installed. |B        |1       |2009-09-21 |16:00:00 |120      |11        |785278...37528  |
|3          |Change       |What we need to change in the project.     |B        |1       |2008-06-03 |9:30:00  |90       |41        |34876...4846548 |
|5          |Reporting    |Explanation to the new beginner.           |B        |1       |2009-03-15 |14:00:00 |60       |7         |19739...37718   |
|2          |Progress     |What we have done.                         |C        |1       |2008-05-12 |14:00:00 |30       |13        |9862...15676    |
|4          |Presentation |Presentation of the project.               |D        |0       |2008-09-11 |15:30:00 |120      |27        |NULL            |
|----------------------------------------------------------------------------------------------------------------------------------------------------|

GROUP BY 子句

[edit | edit source]

GROUP BY 子句用于聚合操作。它将行收集到组中,例如,所有在给定列中具有相同值的行。在将行收集到组中之后,任何聚合操作都将应用于每个组,而不是一个唯一的大的行组。因此,聚合操作将返回与组数量一样多的结果。组可以使用具有给定列中相同值的,或具有多个给定列中相同值的组合的所有行来形成。例如,我们想知道每个优先级类型的聚会数量

  • 查询:
SELECT count(*) as number, priority
FROM reunion
GROUP BY priority;
  • 结果:
|-----------------|
|number |priority |
|-------|---------|
|1      |A        |
|3      |B        |
|1      |C        |
|1      |D        |
|-----------------|

由于 GROUP BY 子句,聚合函数 count(*) 不返回全局计数,而是返回每个优先级级别的计数(A、B、C 和 D)。

  • 查询:
SELECT count(*) as number, planned, duration
FROM reunion
GROUP BY planned, duration;
  • 结果:
|--------------------------|
|number |planned |duration |
|-------|--------|---------|
|1      |0       |120      |
|1      |1       |30       |
|2      |1       |60       |
|1      |1       |90       |
|1      |1       |120      |
|--------------------------|

请注意,planned 列有四个值为 1 的组,duration 列有两个值为 120 的组。但是,您可以看到没有两个列中具有相同值的组合的组。

HAVING 子句

[edit | edit source]

HAVING 子句与 GROUP BY 子句一起使用。HAVING 子句包含一个谓词,并从返回的行中删除谓词为假的组。例如,我们只想检索至少有两个具有相同优先级级别的聚会的优先级

  • 查询:
SELECT priority
FROM reunion
GROUP BY priority
HAVING count(*) > 1;
  • 结果:
|---------|
|priority |
|---------|
|B        |
|---------|

FETCH FIRST 子句

[edit | edit source]

FETCH FIRST 子句用于限制返回行的数量。仅返回第一行。返回行的数量是在子句中指示的数量。

  • 查询:
SELECT *
FROM reunion
FETCH FIRST 4 ROWS ONLY;
  • 结果:
|----------------------------------------------------------------------------------------------------------------------------------------------------|
|id_reunion |name         |description                                |priority |planned |date       |hour     |duration |id_office |pdf_report      |
|-----------|-------------|-------------------------------------------|---------|--------|-----------|---------|---------|----------|----------------|
|1          |Planning     |We need to plan the project.               |A        |1       |2008-03-24 |10:30:00 |60       |35        |48644...846348  |
|2          |Progress     |What we have done.                         |C        |1       |2008-05-12 |14:00:00 |30       |13        |9862...15676    |
|3          |Change       |What we need to change in the project.     |B        |1       |2008-06-03 |9:30:00  |90       |41        |34876...4846548 |
|4          |Presentation |Presentation of the project.               |D        |0       |2008-09-11 |15:30:00 |120      |27        |NULL            |
|----------------------------------------------------------------------------------------------------------------------------------------------------|

此子句通常用于不返回测试中无用的行或提高性能。

现在您可以探索现有数据库中的所有数据。

SQL 函数

[edit | edit source]
  • COUNT
  • AVG
  • MIN
  • MAX
  • SUM

例如

SELECT '''COUNT(*)''' FROM reunion

返回表 reunion 中的行数。

---

  • 另请参见:[[1]]


华夏公益教科书