unvdb基础之sql语句
SQL基本语句使用及解释
SELECT查询、检索语句
此语句可从单个或多个表中检索数据,经常使用
单个表检索数据:
语法:
第一种
SELECT
user_id
FROM
table_name
[other_clauses];
第二种
SELECT user_id FROM table_name [other_clauses];
以上两种并没有区别,但是本人觉得第一种SQL语句更清晰一点,但是本人习惯使用第二种。
此语法中,SELECT 和 FROM 是关键字, 意思是 选择 和 从。
其中, user_id 表明要查找那个字段,而 table_name 是从那个数据表中查找。
字段可以自己定义查找的范围,还有全表扫描**(但是不建议使用)** 如下:
SELECT user_id,name,age FROM table_name [other_clauses];
#以下全表扫描
SELECT * FROM table_name [other_clauses];
而 [other_clauses] 是表示SELECT子句的占位符,子句相当于附加条件。
以下是子句的解释:
DISTINCT
运算符用于从结果集中选择不重复的行。
ORDER BY
子句用于对查询结果集中的行进行排序。
WHERE
子句用于根据指定的条件过滤行。
LIMIT
和FETCH
子句用于从查询结果中选择一定数量(或百分比)的行比如前几行后几行。
GROUP BY
可以根据一个或多个列的值将查询结果划分为多个组。
HAVING
用于在GROUP BY子句之后对分组进行过滤。就是把过滤出的分组按要求在进行过滤。
使用诸如 INNER JOIN
, LEFT JOIN
, FULL OUTER JOIN
, CROSS JOIN
之类的与其他表连接。
使用 UNION
, INTERSECT
和 EXCEPT
执行集合运算。其中的大致意思是
UNION
操作会合并两个集合,并去除重复的元素,返回一个包含两个集合所有不重复元素的新集合。
INTERSECT
操作会返回两个集合中共有的元素,即返回一个包含两个集合共有元素的新集合。
EXCEPT
操作会从第一个集合中移除与第二个集合中相同的元素,返回一个包含第一个集合中不属于第二个集合的元素的新集合。
详细的子句使用方法将在之后提及。
unvdb SELECT语句实际使用
单列查询
SELECT user_id FROM table_name;
user_id
---------
1
2
3
4
5
6
(6 行记录)
多列查询
此实例大致意思就是可以查看自己想看的列
SELECT user_id,name,age,locked, created_at FROM table_name;
user_id | name | age | locked | created_at
---------+-------+-----+--------+----------------------------
1 | huaji | 18 | t | 2023-07-14 14:28:41.628464
2 | huaj | 18 | t | 2023-07-14 14:29:04.38336
3 | huaj2 | 18 | t | 2023-07-14 14:29:23.121366
4 | huaj2 | 18 | t | 2023-07-14 14:29:42.07722
5 | huaj3 | 18 | t | 2023-07-14 14:29:54.351195
6 | huaj6 | 18 | f | 2023-07-14 14:35:31.500747
(6 行记录)
全表扫描
此实例是全表扫描所有列 ,其中(*)代表 所有的意思。
SELECT * FROM table_name ;
user_id | name | age | locked | created_at
---------+-------+-----+--------+----------------------------
1 | huaji | 18 | t | 2023-07-14 14:28:41.628464
2 | huaj | 18 | t | 2023-07-14 14:29:04.38336
3 | huaj2 | 18 | t | 2023-07-14 14:29:23.121366
4 | huaj2 | 18 | t | 2023-07-14 14:29:42.075722
5 | huaj3 | 18 | t | 2023-07-14 14:29:54.351195
6 | huaj6 | 18 | f | 2023-07-14 14:35:31.500747
(6 行记录)
此查询语句不建议使用,由于生产环境下表的数据量大,不像自己的实验或者测试环境下比较少,这样使用的话会加大性能的损耗,占用更多的资源从而使自己的应用性能下降。
表达式查询
表达式的使用范围很广以下是一个简单的表达式检索。
SELECT name||''||created_at FROM huaji ;
?column?
---------------------------------
huaji2023-07-14 14:28:41.628464
huaj2023-07-14 14:29:04.38336
huaj22023-07-14 14:29:23.121366
huaj22023-07-14 14:29:42.075722
huaj32023-07-14 14:29:54.351195
huaj62023-07-14 14:35:31.500747
(6 行记录)
在SQL中,双竖线(||)用于字符串的拼接操作。在查询中|| ‘’ || 用于将 name、空字符串(’’)和 created_at这三个字符串拼接在一起,形成一个完整的姓名。查询结果会返回每个客户的完整姓名和时间。
SELECT语句可以进行计算表达示例
如果只想要单纯的计算一个表达式,可以省略 SELECT
语句中的 FROM
子句。 以下 SELECT
语句用来计算 5 * 3
的结果:
test_1=# SELECT 5*3;
?column?
----------
15
(1 row)
WHERE的用法与示例
WHERE子句通常用与过滤返回的行,使最后的结果限定在我们定义的条件中。
WHERE子句的概述及例子
以下三个示例中的query_condition
(查询条件)代表WHERE子句的占位符。
要返回满足指定条件的行,请按如下语法使用带有 WHERE
子句的 SELECT
语句:
SELECT columns_list
FROM table_name
WHERE query_condition;
要更新满足指定条件的行,请按如下语法使用带有 WHERE
子句的 UPDATA
语句:
UPDATA table_name
SET column_name = value1, ...
WHERE query_condition;
要删除满足指定条件的行,请按如下语法使用带有 WHERE
子句的 DELETE
语句:
DELETE FROM table_name
WHERE query_condition;
WHERE
子句中的 query_condition
(查询条件)为布尔表达式。布尔表达式的结果必须为 true
, false
, 或者 NULL
。它可以是一个布尔表达式或使用 AND
和 OR
运算符的布尔表达式的组合。
WHERE
子句使用 query_condition
来过滤表中的行:
只有导致
query_condition
评估为真的行才会包含在SELECT
语句返回的结果集中。只有导致
query_condition
评估为真的行才会被UPDATE
语句更新。只有导致
query_condition
评估为真的行才会被DELETE
语句删除。
以下是WHERE子句的示例
运算符的使用灵活可以根据自己的想法使用
下面的子句用于查询locked
为f
的用户
SELECT name,locked FROM huaji WHERE locked = 'f';
name | locked
-------+--------
huaj6 | f
(1 row)
在 WHERE
子句中使用 AND
运算符
AND(和)
AND代表组合两个布尔表达式来检索id
和name
为6和huaji6的用户
此语句一旦有一个条件未检索到则不反回数据。
SELECT user_id,name FROM huaji WHERE user_id = '6' AND name = 'huaj6';
user_id | name
---------+-------
6 | huaj6
(1 row)
在 WHERE
子句中使用 OR
运算符
OR(或)
OR
运算符检索user_id
为 1
或 name
为huaji3
的用户
此语句一个条件未检索到不影响后一个条件的执行检索。
SELECT user_id,name FROM huaji WHERE user_id = '1' OR name = 'huaj3';
user_id | name
---------+-------
1 | huaji
5 | huaj3
(2 rows)
在 WHERE
子句中使用 IN
运算符
IN
运算符检索列表中一个你给出的字符串范围中的数据。
SELECT user_id,name,age FROM huaji WHERE age IN (18);
user_id | name | age
---------+-------+-----
1 | huaji | 18
2 | huaj | 18
3 | huaj2 | 18
4 | huaj2 | 18
5 | huaj3 | 18
6 | huaj6 | 18
(6 rows)
在 WHERE
子句中使用 LIKE
运算符
LIKE
检索指定模式匹配的字符串。
test_1=# SELECT name,age FROM huaji WHERE name LIKE 'h%';
name | age
-------+-----
huaji | 18
huaj | 18
huaj2 | 18
huaj2 | 18
huaj3 | 18
huaj6 | 18
(6 rows)
这个是查找以h开头的用户
SELECT name,age FROM huaji WHERE name LIKE '%6';
name | age
-------+-----
huaj6 | 18
(1 row)
这个是查询以6结尾的用户
通配符 %
可以匹配任意长度的任意字符,但是必须有这个字符串开头或结尾的用户否则不反回数据。
在 WHERE
子句中使用 BETWEEN
运算符
BETWEEN (之间)
VETWEEN
运算符检索名字以h
开头包含
SELECT name,LENGTH(name) changdu FROM huaji WHERE name LIKE'h%'ANDLENGTH(name) BETWEEN 4 AND 5 ORDER BY changdu;
name | changdu
-------+---------
huaj | 4
huaji | 5
huaj2 | 5
huaj2 | 5
huaj3 | 5
huaj6 | 5
(6 rows)
在这个例子中,我们使用了获取输入字符串的字符数的 LENGTH()
函数。
在 WHERE
子句中使用不等运算符 (<>
)运算符
此示例查找名字以 hua
开头而locked不以 f
开头的客户
SELECT name,locked FROM huaji WHERE name LIKE 'hua%' AND locked <> 'f';
name | locked
-------+--------
huaji | t
huaj | t
huaj2 | t
huaj2 | t
huaj3 | t
(5 rows)
您可以使用 !=
运算符代替 <>
运算符,因为它们是等效的。
以下是可以在WHERE子句中使用的运算符
=
: 相等>
: 大于<
: 小于>=
: 大于或等于<=
: 小于或等于<>
: 不相等!=
: 不相等,等同于<>
AND
: 逻辑与运算符OR
: 逻辑或运算符IN
: 如果值与列表中的任何值匹配,则返回 trueBETWEEN
: 如果一个值在一个值范围之间,则返回 trueLIKE
: 如果值与模式匹配,则返回 trueIS NULL
: 如果值为 NULL,则返回 trueNOT
: 否定其他运算符的结果
ORDER BY 运算符用法与示例
ORDER BY
子句用来对 SELECT
语句返回的结果集排序。如果没有为 SELECT
语句指定 ORDER BY
子句,SELECT
语句返回的结果集是按照数据库默认的规则排序。
ORDER BY
子句的使用示例
SELECT column1, column2, ... FROM table_name [WHERE clause] ORDER BY column1 [ASC|DESC],[column2 [ASC|DESC],...][NULLS FIRST | NULLS LAST];
说明:
您可以为
ORDER BY
子句指定一个或多个列或者表达式。ASC
代表升序,DESC
代表降序。这是可选的,默认值是ASC
。当指定多个排序表达式时,首先按照前面的表达式排序,其次按照后面的列排序。
NULLS FIRST
和NULLS LAST
用来指定对 null 值排序规则:NULLS FIRST
: null 值在非 null 值之前。NULLS LAST
: null 值在非 null 值之后。
默认情况下,nuvdb采用升序排序时采用
NULLS LAST
,降序排序时采用NULLS FIRST
。 也就是说,unvdb 默认 null 值比非 null 值大。
ORDER BY 排序规则说明
下面说明多种情况下的排序规则:
ORDER BY column ASC;
此
ORDER BY
子句对结果集按column
列的值升序排序。ORDER BY column DESC;
此
ORDER BY
子句对结果集按column
列的值降序排序。ORDER BY column;
此
ORDER BY
子句对结果集按column
列的值升序排序。这个语句等效于:ORDER BY column ASC;
。ORDER BY column1, column2;
此
ORDER BY
子句对结果集先按column1
列的值升序排序,然后再按column2
列的值升序排序。也就是说主排序按
column1
列升序排序,在主排序的基础上,那些column1
列值相同的行,再按column2
列升序排序。ORDER BY column1 DESC, column2;
此
ORDER BY
子句对结果集先按column1
列的值降序排序,然后再按按column2
列的值升序排序。也就是说主排序按
column1
列降序排序,在主排序的基础上,那些column1
列值相同的行,再按column2
列升序排序。
unvdb ORDER BY 实例
按字段升序排序
以下 SQL 语句使用 ORDER BY
子句按员工姓氏升序进行排序。
SELECT
actor_id, first_name, last_name
FROM
actor
ORDER BY last_name;
actor_id | first_name | last_name
----------+-------------+--------------
182 | DEBBIE | AKROYD
92 | KIRSTEN | AKROYD
58 | CHRISTIAN | AKROYD
194 | MERYL | ALLEN
145 | KIM | ALLEN
118 | CUBA | ALLEN
76 | ANGELINA | ASTAIRE
112 | RUSSELL | BACALL
190 | AUDREY | BAILEY
67 | JESSICA | BAILEY
115 | HARRISON | BALE
187 | RENEE | BALL
...
(200 rows)
按字段降序排序
以下 SQL 语句使用 ORDER BY
子句按演员姓氏降序进行排序。
SELECT
actor_id, first_name, last_name
FROM
actor
ORDER BY last_name DESC;
actor_id | first_name | last_name
----------+-------------+--------------
186 | JULIA | ZELLWEGER
111 | CAMERON | ZELLWEGER
85 | MINNIE | ZELLWEGER
63 | CAMERON | WRAY
13 | UMA | WOOD
156 | FAY | WOOD
144 | ANGELA | WITHERSPOON
68 | RIP | WINSLET
147 | FAY | WINSLET
168 | WILL | WILSON
164 | HUMPHREY | WILLIS
96 | GENE | WILLIS
...
(200 rows)
按多字段排序
以下 SQL 语句使用 ORDER BY
子句先按演员姓氏升序排序,再按演员名字升序排序。
SELECT
actor_id, first_name, last_name
FROM
actor
ORDER BY last_name, first_name;
actor_id | first_name | last_name
----------+-------------+--------------
58 | CHRISTIAN | AKROYD
182 | DEBBIE | AKROYD
92 | KIRSTEN | AKROYD
118 | CUBA | ALLEN
145 | KIM | ALLEN
194 | MERYL | ALLEN
76 | ANGELINA | ASTAIRE
112 | RUSSELL | BACALL
190 | AUDREY | BAILEY
67 | JESSICA | BAILEY
115 | HARRISON | BALE
187 | RENEE | BALL
...
(200 rows)
按自定义顺序排序
有时候单纯的按照字段的值排序并不能满足要求,我们需要按照自定义的顺序的排序。比如,我们需要按照电影分级 'G', 'PG', 'PG-13', 'R', 'NC-17'
的顺序对影片进行排序。
对于这样的需求,它可以理解为按照列表中元素的索引位置进行排序。我们使用 CASE
子句函数实现它。
在以下实例中,我们使用 Sakila 示例数据库中的 film
表作为演示。
假设您要根据影片的分级按照的 'G', 'PG', 'PG-13', 'R', 'NC-17'
顺序对影片进行排序。
使用 CASE
实现自定义排序
SELECT
film_id, title, rating
FROM
film
ORDER BY CASE rating
WHEN 'G' THEN 1
WHEN 'PG' THEN 2
WHEN 'PG-13' THEN 3
WHEN 'R' THEN 4
WHEN 'NC-17' THEN 5
END;
357 | GILBERT PELICAN | G
597 | MOONWALKER FOOL | G
354 | GHOST GROUNDHOG | G
...
595 | MOON BUNCH | PG
6 | AGENT TRUMAN | PG
600 | MOTIONS DETAILS | PG
...
9 | ALABAMA DEVIL | PG-13
657 | PARADISE SABRINA | PG-13
956 | WANDA CHAMBER | PG-13
...
749 | RULES HUMAN | R
8 | AIRPORT POLLOCK | R
17 | ALONE TRIP | R
...
520 | LICENSE WEEKEND | NC-17
517 | LESSON CLEOPATRA | NC-17
114 | CAMELOT VACATION | NC-17
...
(1000 rows)
在这个例子中,我们使用 CASE
子句将电影的分级转换为一个数字。然后使用 ORDER BY
按照这个数字进行排序。
ORDER BY 和 NULL
在unvdb 中的升序排序中, NULL
值出现在非 NULL
值之前。
我们下面的实例使用以下临时数据作为演示:
SELECT 'A' AS v
UNION ALL
SELECT 'B' AS v
UNION ALL
SELECT NULL AS v
UNION ALL
SELECT '0' AS v
UNION ALL
SELECT '1' AS v;
v
--------
A
B
<null>
0
1
(5 rows)
当我们使用 ORDER BY
子句升序 ASC
排序时, NULL
值默认排在非 NULL
值的后面。如下:
SELECT 'A' AS v
UNION ALL
SELECT 'B' AS v
UNION ALL
SELECT NULL AS v
UNION ALL
SELECT '0' AS v
UNION ALL
SELECT '1' AS v
ORDER BY v;
v
--------
0
1
A
B
<null>
这里,ASC
排序采用 NULLS LAST
规则,所以 NULL
值在最后。 如果您想要改用 NULLS FIRST
,请执行以下语句:
SELECT 'A' AS v
UNION ALL
SELECT 'B' AS v
UNION ALL
SELECT NULL AS v
UNION ALL
SELECT '0' AS v
UNION ALL
SELECT '1' AS v
ORDER BY v NULLS FIRST;
v
--------
<null>
0
1
A
B
当我们使用 ORDER BY
子句降序 DESC
排序时, NULL
值排在非 NULL
值的前面。这是因为 DESC
排序默认采用 NULLS FIRST
规则。如下:
SELECT 'A' AS v
UNION ALL
SELECT 'B' AS v
UNION ALL
SELECT NULL AS v
UNION ALL
SELECT '0' AS v
UNION ALL
SELECT '1' AS v
ORDER BY v DESC;
v
--------
<null>
B
A
1
0
FETCH 用法与示例
在 unvdb 您可以在SELECT
语句中使用 FETCH
子句来限定返回的行数,降低系统资源的消耗。
而FETCH
和 LIMIT
子句作用相同,但是FETCH
是SQL标准(正规军),而LIMIT
子句是不标准的(杂牌军)
FETCH语法
要使用 FETCH
子句限制返回的行数,按照以下语法使用:
FETCH { FIRST | NEXT } [ rows_count ] { ROW | ROWS } ONLY
可以使用
FIRST
和NEXT
中的任意一个,他们含义相同。可以使用
ROW
和ROWS
中的任意一个,他们含义相同。rows_count
是要限制的行数,即返回的最大的行数。 这个是可选的,默认值为1rows_count
必须为一个大于0的整数
完整语法
SELECT name FROM table_name [other_clauses] FETCH FIRST 1 ROWS ONLY;
name
-------
huaji
其中,other_clauses
是那些可以在 SELECT
语句中使用的其他子句,比如 WHERE
, ORDER BY
, OFFSET
等
此语句经常的使用方法是一同使用 ORDER BY
子句,最后查询结果是按照指定的顺序排序,简洁明了。
在一些分页的查询的应用中,需要在 SELECT
语句结合使用 OFFSET
、 子句和 FETCH
子句。如下:
SELECT column_list FROM table_name ORDER BY ... OFFSET skipped_rows FETCH FIRST rows_count ROWS ONLY;
例如:
第一页可以使用:
OFFSET 0 FETCH FIRST 10 ROWS ONLY
表示最多返回 10 行。第二页可以使用:
OFFSET 10 FETCH FIRST 10 ROWS ONLY
表示跳过第一页的 10 行后最多返回 10 行。第三页可以使用:
OFFSET 20 FETCH FIRST 10 ROWS ONLY
表示跳过前两页的 20 行后最多返回 10 行。以此类推…
FETCH 实例
FETCH 限制返回行数示例
SELECT name FROM huaji FETCH FIRST 1 ROWS ONLY;
name
-------
huaji
(1 row)
FETCH 获取前 N 行
通常,我们经常使用 FETCH
子句从表中选择具有最高或最低值的行。
例如,要获得租金最高的 10 部电影,可以按租金降序对电影进行排序,然后使用 FETCH
子句获得前 10 部电影。可以通过以下查询达到这个目的:
SELECT film_id,title,rental_rate FROM film ORDER BY rental_rate DESC, film_id FETCH FIRST 10 ROWS ONLY;
film_id | title | rental_rate
---------+----------------------+-------------
2 | ACE GOLDFINGER | 4.99
7 | AIRPLANE SIERRA | 4.99
8 | AIRPORT POLLOCK | 4.99
10 | ALADDIN CALENDAR | 4.99
13 | ALI FOREVER | 4.99
20 | AMELIE HELLFIGHTERS | 4.99
21 | AMERICAN CIRCUS | 4.99
28 | ANTHEM LUKE | 4.99
31 | APACHE DIVINE | 4.99
32 | APOCALYPSE FLAMINGOS | 4.99
这里,为了获取租金最高的 10 部影片,我们使用了 ORDER BY rental_rate DESC, film_id
子句对影片按照租金逆序排序,并使用 FETCH FIRST 10 ROWS ONLY
子句获取前 10 行。
FETCH 和 OFFSET 分页查询示例
film
表中共有 1000 行关于影片的信息。可以通过以下带有 COUNT(*)
表达式的 SELECT
语句得到验证:
SELECT COUNT(*) FROM film;
count
-------
1000
假设你有一个系统需要在前端展示所有的影片信息,将 1000 行信息全部展示在一页上并不是一个好的方案。因为这个方案如下的缺点:
数据库性能。 一个语句中返回大量的数据会带给数据库服务器更大的内存开销和 IO 消耗。
应用性能。大量的数据会导致应用占用更大的内存,甚至回导致应用卡顿、卡死。
用户的体验。用户面对大量的数据会眩晕。
一个更好的方案是分页显示所有的影片。可以很轻松地使用 带有 FETCH
和 OFFSET
子句的 SELECT
语句实现分页查询。
假设需要每页显示 10 个影片信息,那么可以使用如下的语句获取第一页的所有行:
SELECT film_id,title,release_year FROM film ORDER BY film_id FETCH FIRST 10 ROWS ONLY;
#这里,为了让所有分页的顺序一致,我们使用 ORDER BY film_id 让影片按照 film_id 排序,并使用 FETCH FIRST 10 ROWS ONLY 限制了此查询最多返回 10 行。
film_id | title | release_year
---------+------------------+--------------
1 | ACADEMY DINOSAUR | 2006
2 | ACE GOLDFINGER | 2006
3 | ADAPTATION HOLES | 2006
4 | AFFAIR PREJUDICE | 2006
5 | AFRICAN EGG | 2006
6 | AGENT TRUMAN | 2006
7 | AIRPLANE SIERRA | 2006
8 | AIRPORT POLLOCK | 2006
9 | ALABAMA DEVIL | 2006
10 | ALADDIN CALENDAR | 2006
要获取第二页要展示的10行,可以使用OFFSET 10 子句 跳过第一页的10行,再使用 FIRST 10 ROWS ONLY 限制最多返回10行。以下语句查询第二页:
SELECT film_id,title,release_year FROM film ORDER BY film_id OFFSET 10 FETCH FIRST 10 ROWS ONLY;
film_id | title | release_year
---------+---------------------+--------------
11 | ALAMO VIDEOTAPE | 2006
12 | ALASKA PHANTOM | 2006
13 | ALI FOREVER | 2006
14 | ALICE FANTASIA | 2006
15 | ALIEN CENTER | 2006
16 | ALLEY EVOLUTION | 2006
17 | ALONE TRIP | 2006
18 | ALTER VICTORY | 2006
19 | AMADEUS HOLY | 2006
20 | AMELIE HELLFIGHTERS | 2006
以此类推。
LIMIT 用法与实例
注意, LIMIT
子句虽然被很多数据库系统支持,但它不是 SQL 标准。 为了符合 SQL 标准,可以使用 FETCH
子句来完成同样的事情。
以下是LIMIT
语句的语法:
LIMIT 最大返回行数;
完整语法如下
SELECT name FROM [子句占位] LIMIT
在一些分页的查询的应用中,需要在 SELECT
语句使用 LIMIT
子句和 OFFSET
子句。如下:
SELECT name FROM table_name ORDER BY ... LIMIT 最大返回行数 OFFSET 跳过几行;
比如:
第一页可以使用:
LIMIT 10 OFFSET 0
表示最多返回 10 行。第二页可以使用:
LIMIT 10 OFFSET 10
表示跳过第一页的 10 行后最多返回 10 行。第三页可以使用:
LIMIT 10 OFFSET 20
表示跳过前两页的 20 行后最多返回 10 行。以此类推…
LIMIT限制返回行数示例
限制film
表查询时最多返回5行:
SELECT film_id,title,release_year FROM film ORDER BY film_id LIMIT 5;
film_id | title | release_year
---------+------------------+--------------
1 | ACADEMY DINOSAUR | 2006
2 | ACE GOLDFINGER | 2006
3 | ADAPTATION HOLES | 2006
4 | AFFAIR PREJUDICE | 2006
5 | AFRICAN EGG | 2006
LIMIT 获取数字最高的前 N 行
SELECT user_id,name,age FROM table_name ORDER BY user_id DESC LIMIT 3;
user_id | name | age
---------+-------+-----
6 | huaj6 | 18
5 | huaj3 | 18
4 | huaj2 | 18
分页查询
SELECT user_id,name,age FROM table_name ORDER BY user_id LIMIT 3;
此SQL语句和FETCH语句极为相似,区别就是此语句不是SQL标准而 FETCH是SQL标准语句。
3.6.OFFSET 用法与实例
可能在使用SELECT语句时要跳过指定的行数,返回剩余的行。比如,您想要获取除成绩前 10 名之外的所有人的信息。
在此可以使用OFFSET
子句跳过指定的行数。
以下是OFFSET
子句语法:
OFFSET 要跳过几行;
完整语法如下:
SELECT column_list FROM table_name [other_clauses] OFFSET skipped_rows;
other_clauses
表示可以在 SELECT
语句中使用的其他子句,比如 WHERE
, ORDER BY
, LIMIT
等
在一些分页的查询的应用中,您需要在 SELECT
语句使用 LIMIT OR FETCH
子句和 OFFSET
子句。如下:
第一页可以使用:
LIMIT OR FETCH 10 OFFSET 0
表示最多返回 10 行。第二页可以使用:
LIMIT OR FETCH 10 OFFSET 10
表示跳过第一页的 10 行后最多返回 10 行。第三页可以使用:
LIMIT OR FETCH 10 OFFSET 20
表示跳过前两页的 20 行后最多返回 10 行。以此类推…
OFFSET示例
SELECT film_id,title,release_year FROM film ORDER BY film_id OFFSET 995;
film_id | title | release_year
---------+-------------------+--------------
996 | YOUNG LANGUAGE | 2006
997 | YOUTH KICK | 2006
998 | ZHIVAGO CORE | 2006
999 | ZOOLANDER FICTION | 2006
1000 | ZORRO ARK | 2006
此语句结合LIMIT OR FETCH
使用。
DISTINCT 用法与实例
DISTINCT
语句用于 SELECT
语句中,以使其返回一个没有重复行的结果。
DISTINCT
语法
以下是DISTINCT的使用语法:
SELECT DISTINCT column1 [, column2, ...] FROM table_name;
关键字
DISTINCT
要在SELECT
后指定。在关键字
DISTINCT
后指定需要评估是否重复的列。多个列名需要使用逗号
,
间隔。如果指定了多个列名, unvdb将根据这些列的值的组合后的值评估是否重复。您可以使用
DISTINCT *
来对所有的列进行评估重复。
我们还提供了 DISTINCT ON (expression)
使用以下语法保留每组重复项的“第一”行
SELECT DISTINCT ON (column1) column_alias,column2 FROM table_name ORDER BY column1,column2;
请注意, DISTINCT ON
表达式必须与 ORDER BY
子句中最左边的表达式相匹配。
DISTINCT 示例
要从 film
表中检索所有的同学的评级,请使用以下语句:
SELECT DISTINCT rating FROM film;
rating
--------
A
B
C
D
E
(5 rows)
这里,为了找到所有的同学评级,我们使用了 DISTINCT rating
,让每个评级只在结果集中出现一次
要从 film
表中检索所有的同学评级和分数的组合,请使用以下语句:
SELECT DISTINCT rating,mark FROM film ORDER BY rating;
rating | mark
--------+---------
E | 500
E | 512
E | 533
D | 560
D | 589
D | 591
C | 610
C | 633
C | 650
B | 671
B | 687
B | 693
A | 712
A | 720
A | 721
如果您想返回每组评级的第一项,请使用以下带有 DISTINCT ON
的语句:
SELECT DISTINCT ON (rating) rating,film_id,title FROM film ORDER BY rating, film_id DESC;
rating | film_id | title
--------+---------+------------------
E | 2 | ACE GOLDFINGER
D | 1 | ACADEMY DINOSAUR
C | 7 | AIRPLANE SIERRA
B | 8 | AIRPORT POLLOCK
A | 3 | ADAPTATION HOLES
DISTINCT 与 NULL
当 DISTINCT
遇到 NULL
值时,只保留一个 NULL
值。因为 DISTINCT
认为所有的 NULL
值都是相同的,这与字段的类型无关。
例如下面的 SQL 返回多行 NULL
记录:
SELECT NULL nullable_col
UNION ALL
SELECT NULL nullable_col
UNION ALL
SELECT NULL nullable_col;
nullable_col
--------------
<null>
<null>
<null>
(3 rows)
这里,我们拥有 3 行,其中每行的 nullable_col
列的值都为 NULL
。
当使用 DISTINCT
之后:
SELECT
DISTINCT nullable_col
FROM
(
SELECT NULL nullable_col
UNION ALL
SELECT NULL nullable_col
UNION ALL
SELECT NULL nullable_col
) t;
nullable_col
--------------
<null>
(1 row)
本例使用 UNION ALL
模拟包含多个 NULL
值的记录集
DISTINCT
子句的用法要点如下:
SELECT DISTINCT
返回一个没有重复行的结果集。DISTINCT
后面可以指定一个列或者多个列,也可以用*
。DISTINCT
将所有的NULL
视为相等的,并只保留一个。DISTINCT ON
子句用来返回每组重复值的第一个行。
列别名
unvdb 允许您为 SELECT
返回的列指定别名,以提高可读性和使用的便利性。
列别名语法
要在 SELECT
语句为指定的列分配别名,请使用以下的语法:
SELECT expr AS alias_name [FROM table_name];
这里:
expr
是一个表达式或者列名。alias_name
是expr
列的别名。SELECT
语句返回的结果集中将使用别名。如果列别名中包含空格,请使用双引号
"
将列别名包围起来。AS
是一个关键字,它是可选的。您可以省略它。
也可以设置多个列别名:
SELECT expr1 AS alias_name1,expr2 AS alias_name2,... [FROM table_name];
列别名示例
要从 actor
表中检索演员的名字和姓氏,请使用以下语句:
SELECT
first_name,
last_name
FROM actor
LIMIT 10;
first_name | last_name
------------+--------------
PENELOPE | GUINESS
NICK | WAHLBERG
ED | CHASE
JENNIFER | DAVIS
JOHNNY | LOLLOBRIGIDA
BETTE | NICHOLSON
GRACE | MOSTEL
MATTHEW | JOHANSSON
JOE | SWANK
CHRISTIAN | GABLE
要为 last_name
指定一个别名 surname
,请使用如下语句:
SELECT
first_name,
last_name AS surname
FROM
actor
LIMIT 10;
first_name | surname
------------+--------------
PENELOPE | GUINESS
NICK | WAHLBERG
ED | CHASE
JENNIFER | DAVIS
JOHNNY | LOLLOBRIGIDA
BETTE | NICHOLSON
GRACE | MOSTEL
MATTHEW | JOHANSSON
JOE | SWANK
CHRISTIAN | GABLE
这里, 结果集中的列名 last_name
已经被替换为 surname
。
为表达式示例 分配列别名
要从 actor
表中检索演员的全名,请使用以下语句:
SELECT
first_name || ' ' || last_name
FROM
actor
LIMIT 10;
?column?
---------------------
PENELOPE GUINESS
NICK WAHLBERG
ED CHASE
JENNIFER DAVIS
JOHNNY LOLLOBRIGIDA
BETTE NICHOLSON
GRACE MOSTEL
MATTHEW JOHANSSON
JOE SWANK
CHRISTIAN GABLE
这里,我们使用了 ||
操作符连接两个字符串。 表达式 first_name || ' ' || last_name
将 first_name
, 空格 和 last_name
连接在一起。
我看可以看到,表达式输出的列名为 ?column?
,这是没有意义的。为了让表达式的输出列名更易读,我们需要为表达式指定一个列名,例如 full_name
:
要解决此问题,您可以为表达式分配 first_name || ' ' || last_name
一个列别名,例如 full_name
:
SELECT
first_name || ' ' || last_name full_name
FROM
actor
LIMIT 10;
full_name
---------------------
PENELOPE GUINESS
NICK WAHLBERG
ED CHASE
JENNIFER DAVIS
JOHNNY LOLLOBRIGIDA
BETTE NICHOLSON
GRACE MOSTEL
MATTHEW JOHANSSON
JOE SWANK
CHRISTIAN GABLE
包含空格的列别名
在上面的例子中,如果您要使用 Full Name
作为列别名,因为它包含了空格,请使用双引号引用起来,即:"Full Name"
。
SELECT
first_name || ' ' || last_name "Full Name"
FROM
actor
LIMIT 10;
Full Name
---------------------
PENELOPE GUINESS
NICK WAHLBERG
ED CHASE
JENNIFER DAVIS
JOHNNY LOLLOBRIGIDA
BETTE NICHOLSON
GRACE MOSTEL
MATTHEW JOHANSSON
JOE SWANK
CHRISTIAN GABLE
使用语法
expr AS alias_name
or 为列或表达式分配列别名expression AS alias_name
。该
AS
关键字是可选的。使用双引号 (”) 将包含空格的列别名括起来。
表别名
在 unvdb 中,除了可以为列指定别名,您也可以为表指定别名。使用表别名一般处于以下的目的:
表别名可以提高 SQL 语句的可读性。
表别名可以为书写 SQL 的带来便利。
表别名可以解决不同表具有相同列名时可能带来的冲突。
表别名语法
要为在 unvdb 中为表指定别名,请按照如下语法:
table_name [AS] table_alias;
table_alias
是表table_name
的别名。如果表别名中包含空格,使用双引号
"
将别名包围起来。不过大多数情况下,使用表别名是为了简化,不建议使用带有空格的表别名。AS
关键字是可选的,它可以被省略。
表别名实例
film
表存储了所有的人员信息, inventory
表中存储所有人员的存储信息。要从 film
表中查找没有存储记录的人员,请使用以下语句:
SELECT f.film_id,f.title FROM film f WHERE NOT EXISTS (SELECT 1 FROM inventory i WHERE i.film_id = f.film_id);
film_id | title
---------+------------------------
14 | ALICE FANTASIA
33 | APOLLO TEEN
36 | ARGONAUTS TOWN
38 | ARK RIDGEMONT
41 | ARSENIC INDEPENDENCE
87 | BOONDOCK BALLROOM
...
954 | WAKE JAWS
955 | WALLS ARTIST
(42 rows)
这里,我们为 film
指定了别名 f
,为 inventory
表指定了别名 i
。 请注意 EXISTS
表达式使用的子查询中的 WHERE
子句:
WHERE i.film_id = f.film_id
其中,比较来自 film
表中的 film_id
和 来自 inventory
表中 film_id
是否相等。 由于两个表使用了相同的列名 film_id
, 因此这里使用了他们别名,以标识是来自哪个表的 film_id
。
如果不使用别名,您还可以直接使用表名来引用列,如下:
WHERE inventory.film_id = film.film_id
在 unvdb 中,表别名可以简化 SQL 语句书写,增加 SQL 语句的可读性。
IN 运算符用法与实例
IN
运算符是一个布尔运算符,用来检查一个值列表是否包含一个指定的值。如果值列表包含了指定的值,则 IN
运算符返回真,否则它返回假。
IN
运算符语法:
要检查一个值是否位于一个值列表之内,请按照以下语法使用 IN
运算符:
expr IN (value1,value2,...)
或
expr IN (subquery)
expr
可以是一个字段名、值或其他的表达式(比如函数调用、运算等)。(value1, value2, ...)
是一个值列表,多个值之间使用,
分隔,并使用小括号()
将它们包围起来。value1
是具体的值,ke 比如:1
,2
,'A'
,'B'
等。subquery
是一个只返回一个列的子查询。
如果值列表或者子查询返回的值中包含了 expr
,IN
运算符返回真,否则它返回假。
IN
运算符的否定操作是 NOT IN
。
IN 与 OR 相比
IN表达式都可以使用 OR 运算符改写,如:
val IN (1,2,3)
等同于以下OR表达式
val = 1 OR val = 2 OR val=3
同理,NOT IN 表达式都可以用AND 运算符改写。
val IN (1,2,3)
等同于以下AND的表达式
val <> 1 AND val <> 2 OR val <> 3
IN 运算符实例
数据库中, actor
表存储了所有的员工的信息。
要从 actor
表中查询姓氏为 ALLEN
或 DAVIS
的所有员工,请使用以下带有 IN
运算符的语句:
SELECT * FROM actor WHERE last_name IN ('ALLEN', 'DAVIS');
您可以使用 OR
运算符改写上面的语句:
SELECT * FROM actor WHERE last_name = 'ALLEN' OR last_name = 'DAVIS';
在unvdbIN
中使用子查询
要从actor 表中检索拥有员工的数量,您可以使用带有子查询的 IN
的表达式:
SELECT COUNT(*) FROM actor WHERE actor_id IN (SELECT actor_id FROM private);
count
-------
666
BETWEEN 运算符的用法与实例
BETWEEN
运算符检查一个值是否位于一个区间之内。
假如,在一个应用系统中,您需要判断根据用户的年收入为用户划定等级。比如,要检查用户的年收入是否位于 40100 元和 120400 元之间,您可以使用以下语句:
annual_income >= 40100 AND annual_income <= 120400
这种情况下,您可以使用 BETWEEN 运算符改写上面的语句,如下:
annual_income BETWEEN 40100 AND 120400
BETWEEN 语法
要检查一个值是否位于一个值区间之内,请按照以下语法使用 BETWEEN
运算符:
expr BETWEEN low_value AND high_value;
expr
是一个表达式或者列名。low_value
是值区间的开始,high_value
是值区间的结束。如果
expr
的值大于或等于low_value
值并且小于或等于high_value
值,BETWEEN
运算符返回真,否则返回假。
BETWEEN
运算符相当于以下使用了 大于等于 (>=
) 和 小于等于 (<=
) 运算符的语句:
expr >= low_value AND expr <= high_value
通常情况下 WHERE
子句中使用 的BETWEEN
运算符语法, 如下所示:
SELECT * FROM table_name WHERE expr >= low_value AND expr <= high_value
当然,除了 INSERT
语句外, WHERE
子句同样可以用于 UPDATE
或者 DELETE
语句。
您可以使用结合 NOT
运算符和 BETWEEN
运算符以检查一个值是否不在一个指定的值区间之内:
expr NOT BETWEEN low_value AND high_value;
这相当于以下使用了 大于 (>) 和 小于 (<) 运算符的语句:
expr < low_value OR expr > high_value
要从pay
表中检索薪资在 7000 到 9000 之间的员工,请使用以下语句:
SELECT name,age FROM pay WHERE age BETWEEN 7000 AND 10000;
name | age
----------+-------
Fengfeng | 7000
Xiaoxiao | 8000
Jingjing | 8828
Mingming | 8848
Ailing | 7747
Xiaoming | 9418
Yingying | 9999
Haohao | 7744
Xiaochen | 7823
Honghong | 8648
如果要取带有特殊字符的数值请这样使用:
WHERE age BETWEEN '2023-12-1' AND '2024-1-31'; 在数值上附上单引号。
LIKE 运算符用法与实例
LIKE
运算符是一个布尔运算符,用来检查一个文本是否与指定的模式匹配。如果文本与指定的模式匹配,则 LIKE
运算符返回真,否则它返回假。
ILIKE
运算符是 LIKE
运算符的不区分大小写版本,ILIKE
运算符执行不区分大小写的匹配。
LIKE 语法
LIKE
运算符是一个双目比较运算符,需要两个操作数。 LIKE
运算符语法如下:
expr LIKE pattern
expr ILIKE pattern
expr
可以是一个字段名、值或其他的表达式(比如函数调用、运算等)。expr
应该是文本。pattern
是一个字符串模式。它支持两个通配符:%
和_
。%
匹配零或多个任意字符。_
匹配单个任意字符。如果需要匹配通配符,则需要使用
\
转义字符,如\%
和\_
。比如:
a%
匹配以字符a
开头的任意长度的字符串。%a
匹配以字符a
结尾的任意长度的字符串。%a%
匹配包含字符a
的任意长度的字符串。%a%b%
匹配同时包含字符a
和b
且a
在b
前面的任意长度的字符串。a_
匹配以字符a
开头长度为 2 字符串。_a
匹配以字符a
结尾长度为 2 字符串。
如果 expr
与 pattern
匹配,LIKE
运算符返回真,否则返回假。
NOT LIKE
是 LIKE
运算符的否定操作。
NOT ILIKE
是 ILIKE
运算符的否定操作。
LIKE 运算规则
LIKE
运算符用于检测一个文本是否指定的模式匹配。它的运算规则如下:
当 LIKE
运算符左侧的操作数与右侧的模式匹配时,返回 t
。否则,返回 f
。
SELECT
'a' LIKE 'a' "'a' LIKE 'a'",
'a' LIKE 'a%' "'a' LIKE 'a%'",
'ab' LIKE 'a%' "'ab' LIKE 'a%'",
'ab' LIKE '%a' "'ab' LIKE '%a'";
'a' LIKE 'a' | 'a' LIKE 'a%' | 'ab' LIKE 'a%' | 'ab' LIKE '%a'
--------------+---------------+----------------+----------------
t | t | t | f
SELECT
'a' LIKE 'a_' "'a' LIKE 'a_'",
'ab' LIKE 'a_' "'ab' LIKE 'a_'",
'abc' LIKE 'a_' "'abc' LIKE 'a_'";
'a' LIKE 'a_' | 'ab' LIKE 'a_' | 'abc' LIKE 'a_'
---------------+----------------+-----------------
f | t | f
如果 LIKE
运算符的两个操作数中任意一个为 NULL
时,返回 空。
SELECT
NULL LIKE 'a%' "NULL LIKE 'a%'",
'a' LIKE NULL "'a' LIKE NULL ";
NULL LIKE 'a%' | 'a' LIKE NULL
----------------+----------------
|
LIKE 实例
使用 LIKE
匹配以某些字符开头的文本
要从表中查找名字以字符 P
开头的所有人员,请使用以下带有 LIKE
运算符的语句:
SELECT * FROM table_name WHERE first_name LIKE 'P%';
actor_id | first_name | last_name | last_update
----------+------------+-----------+---------------------
1 | PENELOPE | GUINESS | 2006-02-15 04:34:33
46 | PARKER | GOLDBERG | 2006-02-15 04:34:33
54 | PENELOPE | PINKETT | 2006-02-15 04:34:33
104 | PENELOPE | CRONYN | 2006-02-15 04:34:33
120 | PENELOPE | MONROE | 2006-02-15 04:34:33
(5 rows)
这里, P%
模式表示以 P
开头的任意长度的文本。为了找到名字以字符 P
开头的所有人员,我们使用了表达式 first_name LIKE 'P%'
。
使用 LIKE
匹配以某个字符结尾的文本
要从表中查找名字以字符 S
结尾的所有人员,请使用以下带有 LIKE
运算符的语句:
SELECT * FROM table_name WHERE first_name LIKE '%s';
actor_id | first_name | last_name | last_update
----------+------------+-----------+---------------------
48 | FRANCES | DAY-LEWIS | 2006-02-15 04:34:33
84 | JAMES | PITT | 2006-02-15 04:34:33
126 | FRANCES | TOMEI | 2006-02-15 04:34:33
(3 rows)
使用 LIKE
匹配包含某些字符开头的文本
要从表中查找名字中带有字符串 AM
的所有人员,请使用以下带有 LIKE
运算符匹配的语句:
SELECT * FROM table_name WHERE first_name LIKE '%AM%';
actor_id | first_name | last_name | last_update
----------+------------+-----------+---------------------
24 | CAMERON | STREEP | 2006-02-15 04:34:33
63 | CAMERON | WRAY | 2006-02-15 04:34:33
71 | ADAM | GRANT | 2006-02-15 04:34:33
84 | JAMES | PITT | 2006-02-15 04:34:33
111 | CAMERON | ZELLWEGER | 2006-02-15 04:34:33
132 | ADAM | HOPPER | 2006-02-15 04:34:33
175 | WILLIAM | HACKMAN | 2006-02-15 04:34:33
(7 rows)
使用 LIKE
匹配单个字符
通配符 _
匹配任意单个字符。以下 SQL 语句使用 LIKE
运算符查找 first_name
以字符串 AY
结尾的且长度为 3 个字符的所有人员。
SELECT * FROM table_name WHERE first_name LIKE '_AY';
actor_id | first_name | last_name | last_update
----------+------------+-----------+---------------------
55 | FAY | KILMER | 2006-02-15 04:34:33
64 | RAY | JOHANSSON | 2006-02-15 04:34:33
147 | FAY | WINSLET | 2006-02-15 04:34:33
156 | FAY | WOOD | 2006-02-15 04:34:33
(4 rows)
IS NULL 运算符用法与实例
IS NULL
运算符检查一个值是不是 NULL
。
IS NULL
是一个布尔运算符,它检查一个值是不是 NULL
。 NULL
值是一个特殊的值,它表示什么都没有,它即不是空字符串也不是假(false)。
IS NULL 语法
IS NULL
是一个单目比较运算符,只需要一个操作数。IS NULL
运算符的语法是:
expr IS NULL
expr IS NOT NULL
expr
可以是一个字段名、一个值或者一个表达式。IS NOT NULL
是IS NULL
的否定运算。
IS NULL
和 IS NOT NULL
可以用在 SELECT
语句或者 WHERE
子句中。
IS NULL
运算规则
IS NULL
运算符左侧的操作数是 NULL
时,IS NULL
运算符返回 t
,否则返回 f
。
SELECT
NULL IS NULL "NULL IS NULL",
0 IS NULL "0 IS NULL",
1 IS NULL "1 IS NULL";
NULL IS NULL | 0 IS NULL | 1 IS NULL
--------------+-----------+-----------
t | f | f
IS NOT NULL
是 IS NULL
的否定运算。如果 IS NOT NULL
左侧的操作数不是 NULL
时,IS NOT NULL
运算符返回 t
,否则返回 f
。
SELECT
NULL IS NOT NULL "NULL IS NOT NULL",
0 IS NOT NULL "0 IS NOT NULL",
1 IS NOT NULL "1 IS NOT NULL";
NULL IS NOT NULL | 0 IS NOT NULL | 1 IS NOT NULL
------------------+---------------+---------------
f | t | t
IS NULL 实例
在 staff
表中, picture
中存储了职员的照片文件, 要从 staff
表中查询那些没有上传图片的职员, 需要检查 picture
是否为 NULL
, 请使用以下带有 IS NULL
的 SQL 语句:
SELECT first_name, last_name, picture FROM staff WHERE picture IS NULL;
first_name | last_name | picture
------------+-----------+---------
Mike | Hillyer | <null>
Jon | Stephens | <null>
要点如下:
IS NULL
和IS NOT NULL
是单目比较运算符。使用
IS NULL
运算符检查一个值是否是NULL
。IS NOT NULL
运算符是IS NULL
的否定操作。NULL IS NULL
的结果是真。
EXISTS 运算符用法与实例
EXISTS
运算符检查子查询中是否存在满足条件的行。
EXISTS
运算符用来判断一个子查询是否返回行。如果一个子查询返回了至少一个行,则 EXISTS
返回真,否则返回假。
EXISTS 语法
EXISTS
运算符用在 WHERE
子句中构造判断条件,它的用法如下:
WHERE EXISTS(subquery);
EXISTS
一般用在WHERE
子句中。EXISTS
是一个单目操作符,它需要一个子查询subquery
作为参数。如果子查询
subquery
返回了至少一行(不论行中的值是否为NULL
),则EXISTS
的计算结果为TRUE
,否则计算结果为FALSE
。EXISTS
运算时,一旦子查询找到一个匹配的行,EXISTS
运算就会返回。这对提高查询新能很有帮助。EXISTS
不关心子查询中的列的数量或者名称,它只在乎子查询是否返回行。所以在EXISTS
的子查询中,无论你是使用SELECT 1
还是SELECT *
,亦或是SELECT column_list
,都不影响EXISTS
运算的结果。NOT EXISTS
则是EXISTS
的否定操作。
EXISTS 示例
数据库中, film
表存储了所有的影片, inventory
表中存储影片的库存信息。 film
表和 inventory
表是一对多的关系,也就是说,一个影片可能有多个库存信息。
要从 film
表中查找拥有库存记录的影片数量,请使用以下语句:
SELECT film_id,title FROM film f WHERE EXISTS (SELECT 1 FROM inventory i WHERE i.film_id = f.film_id);
film_id | title
---------+-----------------------------
1 | ACADEMY DINOSAUR
2 | ACE GOLDFINGER
3 | ADAPTATION HOLES
4 | AFFAIR PREJUDICE
5 | AFRICAN EGG
6 | AGENT TRUMAN
7 | AIRPLANE SIERRA
...
999 | ZOOLANDER FICTION
1000 | ZORRO ARK
(958 rows)
这里,对于影片表中的每一个影片(也就是每一行),子查询检查 inventory
以查找该影片是否有库存记录 (i.film_id = f.film_id
)。
要从 film
表中查找没有记录的电影,请使用以下语句:
SELECT film_id,title FROM film f WHERE NOT EXISTS ( SELECT 1 FROM inventory i WHERE i.film_id = f.film_id);
film_id | title
---------+------------------------
14 | ALICE FANTASIA
33 | APOLLO TEEN
36 | ARGONAUTS TOWN
38 | ARK RIDGEMONT
41 | ARSENIC INDEPENDENCE
87 | BOONDOCK BALLROOM
...
954 | WAKE JAWS
955 | WALLS ARTIST
(42 rows)
ALL 运算符的用法与实例
有时候,您需要将一个值与一个列表中的所有值进行比较,比如:
给定一个工资值。您需要确认这个值是否高于所有员工的工资。
给定一个状态值。您需要确定这个状态值是否不等于所有状态值中的任意一个。
您可以使用 unvdbALL
运算符来解决这些问题。
unvdbALL
运算符用于将一个值与一个值列表进行比较,如果值列表中的所有值满足了指定的条件,表达式就返回真,否则表达式返回假。
ALL
语法
将一个值与一个子查询返回的所有的值进行比较,请按下面的语法使用 ALL
运算符:
comparison_operator ALL (array)
comparison_operator
是一个比较运算符,例如=
,!=
,>
,>=
,<
,<=
等。array
是一个数组或者子查询,它跟在ALL
运算符后面。子查询必须用括号括起来。如果
array
中的所有值都满足指定的条件,则表达式返回真,否则表达式返回假。
例如:
value = ALL (array)
: 如果列表中的所有的值等于value
,该表达式返回true
, 否则返回false
。value > ALL (array)
: 如果列表中的所有的值小于value
,该表达式返回true
, 否则返回false
。value < ALL (array)
: 如果列表中的所有的值大于value
,该表达式返回true
, 否则返回false
。value <> ALL (array)
: 如果列表中的所有的值不等于value
,该表达式返回true
, 否则返回false
。
您可以在 WHERE
子句中按照如下方式使用 ALL
运算符:
WHERE expr1 > ALL (subquery)
ALL 运算符实例
ALL 与数组
我们经常需要检查一个数组中的所有元素是否都满足指定的条件,比如:
要检查数组
[1, 2, 3]
中的所有元素是否都等于2
,请使用以下语句:
SELECT 2 = ALL(ARRAY[1, 2, 3]);
?column?
----------
f
要检查数组 [1, 2, 3]
中的所有元素是否都大于等于 1
,请使用以下语句:
SELECT 1 <= ALL(ARRAY[1, 2, 3]);
?column?
----------
t
要检查数组 [1, 2, 3]
中的所有元素是否都不等于 0
,请使用以下语句:
SELECT 0 <> ALL(ARRAY[1, 2, 3]);
?column?
----------
t
ALL 与子查询
下面我们提供了一些实用的例子来展示 ALL
操作符的威力.
要判断一个值 99
是否比所有的影片的租金都要高,请使用下面的语句:
SELECT 99 > ALL (SELECT rental_rate FROM film);
?column?
----------
t
要判断一个值 66
是否比所有的影片的租金都要小,请使用下面的语句:
SELECT 66 < ALL (SELECT rental_rate FROM film);
?column?
----------
f
要查找大于等于所有的租金的影片的数量,请使用下面的语句:
SELECT count(*) FROM film WHERE rental_rate >= ALL (SELECT rental_rate FROM film);
count
-------
336
ANY 运算符的用法与实例
ANY
运算符检查一个子查询返回的一组值中是否至少存在一个值满足指定的条件。
有时候,你需要检查一个列表中是否至少有一个满足指定条件的值,比如:
检查一个列表中是否包含了一个指定的值的元素。
检查一个列表中是否有一个大于或者小于一个指定值的元素。
检查一个班级的考试成绩是否有满分。
您可以使用 unvdb ANY
运算符来解决这些问题
ANY
运算符用于将一个值与一个值列表进行比较,只要值列表中有一个值满足指定的条件就返回真,否则返回假。
在 unvdb 中, SOME
操作符是 ANY
的同义词。 您可以使用 SOME
关键字代替 ANY
。
ANY
语法
要检查一个数组中是否至少存在一个满足条件的值,请按下面的语法使用 ANY
运算符:
comparison_operator ANY (array)
这里:
comparison_operator
是一个比较运算符,例如=
,!=
,>
,>=
,<
,<=
等。array
是一个数组或者子查询,它跟在ANY
运算符后面。子查询必须用括号括起来。如果
array
中至少有一个值满足指定的条件,则表达式返回真,否则表达式返回假。
例如:
value = ANY (array)
: 只要数组中有一个等于value
的值,该表达式返回true
, 否则返回false
。value > ANY (array)
: 只要数组中有一个小于value
的值,该表达式返回true
, 否则返回false
。value < ANY (array)
: 只要数组中有一个大于value
的值,该表达式返回true
, 否则返回false
。value <> ANY (array)
: 只要数组中有一个不等于value
的值,该表达式返回true
, 否则返回false
。
ANY 实例
ANY 与数组
我们经常需要检查一个数组中是否至少包含一个满足条件的元素,比如:
要检查数组
[1, 2, 3]
是否包含一个值为2
的元素,请使用以下语句:SELECT 2 = ANY(ARRAY[1, 2, 3]);
?column? ---------- t
要检查数组
[1, 2, 3]
是否包含一个值大于2
的元素,请使用以下语句:SELECT 2 < ANY(ARRAY[1, 2, 3]);
?column? ---------- t
要检查数组
[1, 2, 3]
是否包含一个值大于3
的元素,请使用以下语句:SELECT 3 < ANY(ARRAY[1, 2, 3]);
?column? ---------- f
这里,因为
[1, 2, 3]
所有元素都不大于3
,因此表达式返回了false
。要检查数组
[1, 2, 3]
是否包含一个值不等于3
的元素,请使用以下语句:SELECT 3 <> ANY(ARRAY[1, 2, 3]);
?column? ---------- t
ANY 与子查询
ANY
运算符结合子查询会产生更大的威力。
要检查是否存在租金高于 5 美元的影片,请使用下面的
ANY
语句:SELECT 5 < ANY (SELECT rental_rate FROM film);
?column? ---------- f
这里,请注意子查询:
SELECT rental_rate FROM film
它返回了所有影片你的租金。只要 5 小于所有租金的中的一个,则代表影片表中存在租金大于 5 美元的影片。
最终,表达式返回了
false
,这说明了film
表中没有租金大于 5 美元的影片。要检查是否能用 1 美元租到影片,请使用以下语句:
SELECT 1 >= ANY (SELECT rental_rate FROM film);
?column? ---------- t
要判断能否用 1 美元租到影片,就是判断是否存在租金小于等于 1 美元。
表达式返回了
false
,这说明了film
表中有租金小于等于 1 美元的影片。也就是,您可以是使用 1 美元租到影片。
GROUP BY
子句将行根据指定的字段或者表达式进行分组。
有时候,我们需要将结果集按照某个维度进行汇总。这在统计数据的时候经常用到,考虑以下的场景:
按班级求取平均成绩。
按学生汇总总分。
按年或者月份统计销售额。
按国家或者地区统计用户数量。
这些正是 GROUP BY
子句发挥作用的地方
GROUP BY
语法
GROUP BY
子句是 SELECT
语句的可选子句。要对 SELECT
语句中的行进行分组,请按照如下语法使用 GROUP BY
子句:
SELECT column1[, column2, ...], aggregate_function(ci)
FROM table
[WHERE clause]
GROUP BY column1[, column2, ...];
[HAVING clause]
column1[, column2, ...]
是分组依据的字段,至少一个字段,可以多个字段。aggregate_function(ci)
是聚合函数,用来汇总。这是可选的。 您可以会用到以下聚合函数:sum()
: 计算组内数据的总和avg()
: 计算组内数据的平均值max()
: 计算组内数据的最大值MIN()
: 计算组内数据的最小值count()
: 计算组内的行数
SELECT
后的字段必须是分组字段中的字段。WHERE
子句是可选的,用来过在分组之前过滤行。HAVING
子句是可选的,用来过滤分组数据。
GROUP BY
实例
简单的 GROUP BY 实例
我们使用 GROUP BY
子句查看 actor
表中的姓氏列表。
SELECT last_name
FROM actor
GROUP BY last_name;
last_name
--------------
AKROYD
BRIDGES
HUNT
GIBSON
ALLEN
SUVARI
HESTON
MONROE
...
WILSON
(121 rows)
本例中,使用 GROUP BY
子句按照 last_name
字段对数据进行分组。
本例的输出结果与以下使用 DISTINCT
的 SQL 输出结果完全一样:
SELECT DISTINCT last_name FROM actor;
GROUP BY 与聚合函数实例
我们使用 GROUP BY
子句和聚合函数 count()
查看 actor
表中的姓氏列表以及每个姓氏的数量。
SELECT last_name, count(*)
FROM actor
GROUP BY last_name
ORDER BY count(*) DESC;
last_name | count
--------------+-------
KILMER | 5
TEMPLE | 4
NOLTE | 4
WILLIAMS | 3
PECK | 3
HOPKINS | 3
DAVIS | 3
HARRIS | 3
DEGENERES | 3
...
CLOSE | 1
(121 rows)
本例中,执行的顺序如下:
首先使用
GROUP BY
子句按照last_name
字段对actor
表中的所有的行进行分组。也就是每个姓氏一组。然后使用聚合函数
count(*)
汇总每个姓氏的行数。最后使用
ORDER BY
子句按照count(*)
降序排列。这样,数量最多的姓氏排在最前面。
同样,如果我们想从 film
表中查找每个影片等级的影片数量,请使用如下语句:
SELECT rating, count(*)
FROM film
GROUP BY rating
ORDER BY count(*) DESC;
rating | count
--------+-------
PG-13 | 223
NC-17 | 210
R | 195
PG | 194
G | 178
(5 rows)
GROUP BY, LIMIT, 聚合函数实例
以下实例使用 GROUP BY
子句,LIMIT
子句和聚合函数 sum()
从 payment
表中查找消费金额排名前 10 位的客户。
SELECT customer_id, sum(amount) total
FROM payment
GROUP BY customer_id
ORDER BY total DESC
LIMIT 10;
customer_id | total
-------------+--------
526 | 221.55
148 | 216.54
144 | 195.58
178 | 194.61
137 | 194.61
459 | 186.62
469 | 177.60
468 | 175.61
236 | 175.58
181 | 174.66
(10 rows)
本例中,执行的顺序如下:
首先使用
GROUP BY
子句按照customer_id
字段对数据进行分组,也就是按照客户分组。然后使用聚合函数
sum(amount)
对每组中的所有行的amount
字段求和,并使用total
作为列别名。然后使用
ORDER BY
子句按照total
降序排列。最后使用
LIMIT 10
子句返回前 10 个记录行。
GROUP BY 和 HAVING 实例
以下实例使用 GROUP BY
子句,HAVING
子句和聚合函数 sum()
从 payment
表中查找总消费金额在 180 美元以上的客户。
SELECT customer_id, sum(amount) total
FROM payment
GROUP BY customer_id
HAVING sum(amount) > 180
ORDER BY total DESC;
customer_id | total
-------------+--------
526 | 221.55
148 | 216.54
144 | 195.58
178 | 194.61
137 | 194.61
459 | 186.62
(6 rows)
本例中,执行的顺序如下:
首先使用
GROUP BY
子句按照customer_id
字段对数据进行分组,也就是按照客户分组。然后使用聚合函数
sum(amount)
对每组中的所有行的amount
字段求和,并使用total
作为列别名。然后使用
HAVING
子句指定只有sum(amount)
大于180
的行才会被返回。最后使用
ORDER BY
子句按照total
降序排列。
HAVING
子句用来过滤 GROUP BY
分组的数据,需要一个逻辑表达式作为条件,其中逻辑表达式中的列名或表达式只能使用分组使用的列,表达式,或者应用于分组列或表达式的聚合函数。
以下是 GROUP BY
子句的要点:
GROUP BY
子句用于将结果集根据指定的字段或者表达式进行分组。GROUP BY
子句至少需要一个分组字段或表达式。HAVING
子句是可选的,它用来过滤分组数据。GROUP BY
子句经常用于数据统计汇总,通常使用聚合函数
HAVING 用法与实例
HAVING
子句为分组查询指定过滤条件。
在 unvdb 中, HAVING
子句用于为带有 GROUP BY
子句的分组查询指定过滤条件。
HAVING
看起来与 WHERE
相似,虽然他们都是指定过滤条件,但是他们的区别是: WHERE
子句指定的条件用于过滤表中的行,而 HAVING
子句指定的条件用于过滤分组。
HAVING 语法
unvdb HAVING
子句必须与 GROUP BY
子句一起使用。 以下是unvdbHAVING
子句典型的用法:
SELECT column1[, column2, ...], aggregate_function(ci) FROM table [WHERE clause] GROUP BY column1[, column2, ...]; HAVING clause
这里,
GROUP BY
子句用于指定用于分组的列或者表达式。HAVING
子句用来过滤GROUP BY
分组的数据,需要使用逻辑表达式作为条件,其中逻辑表达式中的列名或表达式只能使用分组使用的列,表达式,或者应用于分组列或表达式的聚合函数。您不能在
HAVING
子句中使用列别名。
HAVING 子句实例
使用带有 count 函数的 HAVING 子句示例
如果您想从 film
表中查找每个影片评级的影片数量,请使用如下语句:
SELECT rating, count(*) FROM film GROUP BY rating ORDER BY count(*) DESC;
rating | count
--------+-------
PG-13 | 223
NC-17 | 210
R | 195
PG | 194
G | 178
(5 rows)
这个实例返回了所有的影片评级的影片数量。如果您想要查找影片数量大于 200 的影片评级,就要用到 HAVING
子句,如下:
SELECT rating, count(*) FROM film GROUP BY rating HAVING count(*) > 200 ORDER BY count(*) DESC;
rating | count
--------+-------
PG-13 | 223
NC-17 | 210
(2 rows)
使用带有 SUM 函数示例的 HAVING 子句
如果您想从 payment
表中查找总消费金额在 180 美元以上的客户,请使用以下带有 GROUP BY
子句,HAVING
子句和聚合函数 sum()
的语句:
SELECT customer_id, sum(amount) total FROM payment GROUP BY customer_id HAVING sum(amount) > 180 ORDER BY total DESC;
customer_id | total
-------------+--------
526 | 221.55
148 | 216.54
144 | 195.58
178 | 194.61
137 | 194.61
459 | 186.62
(6 rows)
本例中,执行的顺序如下:
首先使用
GROUP BY
子句按照customer_id
字段对数据进行分组,也就是按照客户分组。然后使用聚合函数
sum(amount)
对每组中的所有行的amount
字段求和,并使用total
作为列别名。然后使用
HAVING
子句指定只有sum(amount)
大于180
的行才会被返回。最后使用
ORDER BY
子句按照total
降序排列。
HAVING
子句用于为带有 GROUP BY
子句的分组查询指定过滤条件。 HAVING
子句需要使用逻辑表达式作为条件,其中逻辑表达式中的列名或表达式只能使用分组使用的列,表达式,或者应用于分组列或表达式的聚合函数。
GROUPING SETS 用法与实例
GROUPING SETS
以在一次查询中生成多个维度的报表。
在 unvdb中,GROUPING SETS
是 GROUP BY
子句的参数,允许您在一次查询中生成多个维度的报表。
比如,要想在一个销售报表中即有每个年份销售额的行,又有每个部门销售额的行,您可以在 GROUP BY
子句中使用 GROUPING SETS
实现它。
GROUPING SETS
语法
下面的语法说明了如何在 GROUP BY
子句中使用 GROUPING SETS
:
SELECT ... FROM table_name [WHERE ...] GROUP BY GROUPING SETS ((group_expr_list_1)[, (group_expr_list_2), ...]);
GROUPING SETS
是GROUP BY
子句的参数,它必须在GROUP BY
关键字的后面。(group_expr_list_N)
是一个用于分组的表达式列表,其中可以包含零个或者多个列或表达式,就像在GROUP BY
子句中直接使用的那些列一样。您可以为
GROUPING SETS
指定一个或者多个分组表达式,每个分组表达式产生的结果集都将合并到最终的结果集中。(group_expr_list_N)
中没有任何列或者表达式时,即()
,所有符合条件的行都被聚合到一个分组中。
GROUP BY
实例
要从 film
表中查找每个影片评级的影片数量,请使用如下语句:
SELECT rating, count(*) FROM film GROUP BY rating ORDER BY rating;
rating | count
--------+-------
G | 178
PG | 194
PG-13 | 223
R | 195
NC-17 | 210
(5 rows)
要从 film
表中查找每个租金的影片数量,请使用如下语句:
SELECT rental_rate, count(*) FROM film GROUP BY rental_rate ORDER BY rental_rate;
rental_rate | count
-------------+-------
0.99 | 341
2.99 | 323
4.99 | 336
(3 rows)
要想在一个报表中包含上面的两个报表,您可以使用 UNION ALL
将上面的两个结果集合并起来,如下:
SELECT rating, NULL rental_rate, count(*) FROM film GROUP BY rating
UNION ALL
SELECT NULL rating, rental_rate, count(*) FROM film GROUP BY rental_rate ORDER BY rating, rental_rate;
rating | rental_rate | count
--------+-------------+-------
G | | 178 ┐
PG | | 194 │
PG-13 | | 223 │ > ranting
R | | 195 │
NC-17 | | 210 ┘
| 0.99 | 341 ┐
| 2.99 | 323 │ > rental_rate
| 4.99 | 336 ┘
(8 rows)
但是,上面使用 UNION ALL
的语句看起来很复杂。如果能用一个简单的语句做到这一切,就太棒了。unvdb GROUPING SETS
可以帮我们做到这一切:
SELECT rating, rental_rate, count(*) FROM film GROUP BY GROUPING SETS ((rating), (rental_rate)) ORDER BY rating, rental_rate;
rating | rental_rate | count
--------+-------------+-------
G | | 178 ┐
PG | | 194 │
PG-13 | | 223 │ > ranting
R | | 195 │
NC-17 | | 210 ┘
| 0.99 | 341 ┐
| 2.99 | 323 │ > rental_rate
| 4.99 | 336 ┘
(8 rows)
所以,您可以将 GROUPING SETS
简单的理解为对多个分组结果集执行了 UNION ALL
操作。
如果您想在上面的报表中添加一行以显示总影片数量,您可以在 GROUPING SETS
中是一个空的分组表达式 ()
,如下:
SELECT rating, rental_rate, count(*) FROM film GROUP BY GROUPING SETS ((rating), (rental_rate), ()) ORDER BY rating, rental_rate;
rating | rental_rate | count
--------+-------------+-------
G | | 178
PG | | 194
PG-13 | | 223
R | | 195
NC-17 | | 210
| 0.99 | 341
| 2.99 | 323
| 4.99 | 336
| | 1000
在unvdb中的 GROUP BY
子句中,您可以使用 GROUPING SETS
以在一次查询中生成多个分组查询的结果集的集合。
为了应对更加复杂的需求,unvdb 还提供了 ROLLUP
和 CUBE
以简化 GROUPING SETS
。
ROLLUP 用法与实例
在unvdb中, ROLLUP
是 GROUP BY
子句的参数,允许您在一次查询中生成多个维度的报表。 在某些特定的场景下, ROLLUP
可以简化 GROUPING SETS
。
ROLLUP
语法
下面的语法说明了如何在 GROUP BY
子句中使用 ROLLUP
SELECT ... FROM table_name [WHERE ...] GROUP BY ROLLUP (group_expr_1 [, group_expr_2, ...]);
这里, ROLLUP
是 GROUP BY
子句的参数,它必须在 GROUP BY
关键字的后面。
ROLLUP
子句都可以使用 GROUPING SETS
子句实现,比如:
ROLLUP(a, b)
等效于GROUPING SETS((a,b), (a), ())
。ROLLUP(a, b, c)
等效于GROUPING SETS((a,b,c), (a,b), (a), ())
。
ROLLUP
子句比 GROUPING SETS
子句更加的简单和易读。但是,它的的适应性不如 GROUPING SETS
广泛。
GROUP BY
实例
要从 film
表中查找每个影片评级中每个租金的影片的数量,以及每个影片评级中的影片的数量,以及所有影片的总数量,您可以使用如下带有 GROUPING SETS
的语句:
SELECT rating,rental_rate,count(*) FROM film GROUP BY GROUPING SETS ((rating, rental_rate),(rating),()) ORDER BY rating, rental_rate;
rating | rental_rate | count
--------+-------------+-------
G | 0.99 | 64 ┐
G | 2.99 | 59 │───┐
G | 4.99 | 55 ┘ │───┐
G | | 178 <───┘ │
PG | 0.99 | 62 ┐ │
PG | 2.99 | 64 │───┐ │
PG | 4.99 | 68 ┘ │───│
PG | | 194 <───┘ │
PG-13 | 0.99 | 72 ┐ │
PG-13 | 2.99 | 74 │───┐ │
PG-13 | 4.99 | 77 ┘ │───│
PG-13 | | 223 <───┘ │
R | 0.99 | 70 ┐ │
R | 2.99 | 60 │───┐ │
R | 4.99 | 65 ┘ │───│
R | | 195 <───┘ │
NC-17 | 0.99 | 73 ┐ │
NC-17 | 2.99 | 66 │───┐ │
NC-17 | 4.99 | 71 ┘ │───│
NC-17 | | 210 <───┘ │
| | 1000 <───────┘
(21 rows)
这里,我们在 GROUPING SETS
中使用了 3 个表达式,如下:
(rating, rental_rate)
: 按照影片评级和租金统计影片的数量。(rating)
: 按照影片评级统计数量。()
: 统计所有影片的数量。
在这种情况下, 这个 GROUPING SETS
可以使用 ROLLUP
来简化,如下
SELECT rating,rental_rate,count(*) FROM film GROUP BY ROLLUP (rating, rental_rate) ORDER BY rating, rental_rate;
rating | rental_rate | count
--------+-------------+-------
G | 0.99 | 64
G | 2.99 | 59
G | 4.99 | 55
G | | 178
PG | 0.99 | 62
PG | 2.99 | 64
PG | 4.99 | 68
PG | | 194
PG-13 | 0.99 | 72
PG-13 | 2.99 | 74
PG-13 | 4.99 | 77
PG-13 | | 223
R | 0.99 | 70
R | 2.99 | 60
R | 4.99 | 65
R | | 195
NC-17 | 0.99 | 73
NC-17 | 2.99 | 66
NC-17 | 4.99 | 71
NC-17 | | 210
| | 1000
(21 rows)
在上面的语句中,我们使用 ROLLUP (rating, rental_rate)
代替了 GROUPING SETS ((rating, rental_rate), (rating), ())
。这让语句更加简单,可读性更好。
在 GROUP BY
子句中,您可以在特定的场景下使用 ROLLUP
简化 GROUPING SETS
。
CUBE 用法与实例
在 unvdb 中,CUBE
是 GROUP BY
子句的参数,允许您在一次查询中生成多个维度的报表。和 ROLLUP
一样, 在某些特定的场景下, CUBE
可以简化 GROUPING SETS
。
CUBE
语法
下面的语法说明了如何在 GROUP BY
子句中使用 CUBE
:
SELECT ... FROM table_name [WHERE ...] GROUP BY CUBE (group_expr_1 [, group_expr_2, ...]);
这里, CUBE
是 GROUP BY
子句的参数,它必须在 GROUP BY
关键字的后面。
CUBE
子句都可以使用 GROUPING SETS
子句实现,比如:
CUBE(a, b)
等效于GROUPING SETS((a,b), (a), (b), ())
。CUBE(a, b, c)
等效于GROUPING SETS((a,b,c), (a,b), (a,c), (a), (b,c), (b), (c), ())
。
CUBE
子句比 GROUPING SETS
子句更加的简单和易读。但是,它的的适应性不如 GROUPING SETS
广泛。
GROUP BY
实例
要从 film
表中查找每个影片评级中每个租金的影片的数量,每个影片评级中的影片的数量,每个租金的影片的数量,以及所有影片的总数量,您可以使用如下带有 GROUPING SETS
的语句:
SELECT rating,rental_rate,count(*) FROM film GROUP BY GROUPING SETS ((rating, rental_rate),(rating),(rental_rate),()) ORDER BY rating, rental_rate;
rating | rental_rate | count
--------+-------------+-------
G | 0.99 | 64
G | 2.99 | 59
G | 4.99 | 55
G | | 178
PG | 0.99 | 62
PG | 2.99 | 64
PG | 4.99 | 68
PG | | 194
PG-13 | 0.99 | 72
PG-13 | 2.99 | 74
PG-13 | 4.99 | 77
PG-13 | | 223
R | 0.99 | 70
R | 2.99 | 60
R | 4.99 | 65
R | | 195
NC-17 | 0.99 | 73
NC-17 | 2.99 | 66
NC-17 | 4.99 | 71
NC-17 | | 210
| 0.99 | 341
| 2.99 | 323
| 4.99 | 336
| | 1000
(24 rows)
这里,我们 GROUPING SETS
使用了 3 个表达式,如下:
(rating, rental_rate)
: 按照影片评级和租金统计影片的数量。(rating)
: 按照影片评级统计数量。(rental_rate)
: 按照租金统计数量。()
: 统计所有影片的数量。
在这种情况下, 这个 GROUPING SETS
可以使用 CUBE
来简化,如下:
SELECT rating,rental_rate,count(*) FROM film GROUP BY CUBE (rating, rental_rate) ORDER BY rating, rental_rate;
rating | rental_rate | count
--------+-------------+-------
G | 0.99 | 64
G | 2.99 | 59
G | 4.99 | 55
G | | 178
PG | 0.99 | 62
PG | 2.99 | 64
PG | 4.99 | 68
PG | | 194
PG-13 | 0.99 | 72
PG-13 | 2.99 | 74
PG-13 | 4.99 | 77
PG-13 | | 223
R | 0.99 | 70
R | 2.99 | 60
R | 4.99 | 65
R | | 195
NC-17 | 0.99 | 73
NC-17 | 2.99 | 66
NC-17 | 4.99 | 71
NC-17 | | 210
| 0.99 | 341
| 2.99 | 323
| 4.99 | 336
| | 1000
(24 rows)
JOIN 连接类型和实例
本文介绍了 unvdb 中的连接语句,包括交叉连接、内连接、自然连接、左连接、右连接、全连接。
在 unvdb 中,JOIN
语句用于将数据库中的两个表或者多个表连接起来。
比如在一个学校系统中,有一个学生信息表和一个学生成绩表。这两个表通过学生 ID 字段关联起来。当我们要查询学生的成绩的时候,就需要连接两个表以查询学生信息和成绩。
unvdb 连接类型
unvdb 支持以下类型的连接:
交叉连接 (
CROSS JOIN
)内联接 (
INNER JOIN
)自然连接 (
NATURAL JOIN
)左连接/左外连接 (
LEFT [OUTER] JOIN
)右连接/右外连接 (
RIGHT [OUTER] JOIN
)全连接/全外连接 (
RIGHT [OUTER] JOIN
)
创建实例表和数据
本教程中关于表连接的实例都使用 student
和 student_score
两个表来完成。
首先,使用下面的 SQL 语句创建表 student
和 student_score
:
CREATE TABLE student (student_id INTEGER NOT NULL,name varchar(45) NOT NULL,PRIMARY KEY (student_id));
CREATE TABLE student_score (student_id INTEGER NOT NULL,subject varchar(45) NOT NULL,score INTEGER NOT NULL);
然后,分别在两个表中插入数据:
INSERT INTO student (student_id, name) VALUES (1,'Tim'),(2,'Jim'),(3,'Lucy');
INSERT INTO student_score (student_id, subject, score) VALUES (1,'English',90),(1,'Math',80),(2,'English',85),(5,'English',92);
第三,以下语句使用 SELECT
检查表中的数据:
SELECT * FROM student;
student_id | name
------------+------
1 | Tim
2 | Jim
3 | Lucy
(3 rows)
SELECT * FROM student_score;
student_id | subject | score
------------+---------+-------
1 | English | 90
1 | Math | 80
2 | English | 85
5 | English | 92
(4 rows)
注意,为了演示,我们特意使用了特殊的数据行:
student
表中student_id
为 3 的学生没有成绩。student_score
表中的最后一行的student_id
为5
,而student
表中不存在student_id
为5
的学生。
交叉连接
交叉连接返回两个集合的笛卡尔积。也就是两个表中的所有的行的所有可能的组合。这相当于内连接没有连接条件或者连接条件永远为真。
如果一个有 m
行的表和另一个有 n
行的表,它们交叉连接将返回 m * n
行。
在大多数场景下,交叉连接的结果没有意义,你需要使用 WHERE
子句过滤自己所需的数据行。
显式的交叉连接 student
和 student_score
表:
SELECT student.*,student_score.* FROM student CROSS JOIN student_score;
这两种方式的输出一样。
student_id | name | student_id | subject | score
------------+------+------------+---------+-------
1 | Tim | 1 | English | 90
1 | Tim | 1 | Math | 80
1 | Tim | 2 | English | 85
1 | Tim | 5 | English | 92
2 | Jim | 1 | English | 90
2 | Jim | 1 | Math | 80
2 | Jim | 2 | English | 85
2 | Jim | 5 | English | 92
3 | Lucy | 1 | English | 90
3 | Lucy | 1 | Math | 80
3 | Lucy | 2 | English | 85
3 | Lucy | 5 | English | 92
(12 rows)
内连接
内连接基于连接条件组合两个表中的行。内连接相当于加了过滤条件的交叉连接。
内连接将第一个表的每一行与第二个表的每一行进行比较,如果满足给定的连接条件,则将两个表的行组合在一起作为结果集中的一行。
以下 SQL 语句将 student
表和 student_score
表内连接,以查找有效的学生成绩信息:
SELECT student.*,student_score.* FROM student INNER JOIN student_score ON student.student_id = student_score.student_id;
等于:
SELECT student.*,student_score.* FROM student, student_score WHERE student.student_id = student_score.student_id;
student_id | name | student_id | subject | score
------------+------+------------+---------+-------
1 | Tim | 1 | English | 90
1 | Tim | 1 | Math | 80
2 | Jim | 2 | English | 85
(3 rows)
注意输出结果中,student
表中 student_id
为 3
的行和 student_score
表中 student_id
为 5
的行没有出现在输出结果中,这是因为他们没有满足连接条件:student.student_id = student_score.student_id
。
自然连接
自然连接同样是基于条件的连接,它是一种特殊的内连接。两个表做自然连接时,两个表中所有同名的列都将做等值比较。这些连接条件都是隐式创建的。
以下 SQL 语句对 student
表和 student_score
做自然连接,等效于上面的内连接语句:
SELECT * FROM student NATURAL JOIN student_score;
student_id | name | subject | score
------------+------+---------+-------
1 | Tim | English | 90
1 | Tim | Math | 80
2 | Jim | English | 85
(3 rows)
注意,自然连接不需要使用 ON
创建连接条件,它的连接条件是隐式创建的。 自然连接的结果集中,两个表中同名的列只出现一次。
左连接
左连接是左外连接的简称,左连接需要连接条件。
两个表左连接时,第一个表称为左表,第二表称为右表。例如 A LEFT JOIN B
,A
是左表,B
是右表。
左连接以左表的数据行为基础,根据连接条件匹配右表的每一行,如果匹配成功则将左表和右表的行组合成新的数据行返回;如果匹配不成功则将左表的行和 NULL 值组合成新的数据行返回。
以下 SQL 语句将 student
表和 student_score
表左连接:
SELECT student.*,student_score.* FROM student LEFT JOIN student_score ON student.student_id = student_score.student_id;
student_id | name | student_id | subject | score
------------+------+------------+---------+--------
1 | Tim | 1 | English | 90
1 | Tim | 1 | Math | 80
2 | Jim | 2 | English | 85
3 | Lucy | <null> | <null> | <null>
(4 rows)
注意:
结果集中包含了
student
表的所有记录行。student_score
表中不包含student_id = 3
的记录行,因此结果集中最后一行中来自student_score
的列的内容为NULL
。student_score
表存在多个student_id
为1
的行,因此结果集中也产生了多个来自student
表对应的行。
由于两个表都使用相同的字段进行等值比较,因此您可以使用 USING
以下查询中所示的子句:
SELECT student.*,student_score.* FROM student LEFT JOIN student_score USING(student_id);
右连接
右连接是右外连接的简称,右连接需要连接条件。
右连接与左连接处理逻辑相反,右连接以右表的数据行为基础,根据条件匹配左表中的数据。如果匹配不到左表中的数据,则左表中的列为 NULL
值。
以下 SQL 语句将 student
表和 student_score
表右连接:
SELECT student.*,student_score.* FROM student RIGHT JOIN student_score ON student.student_id = student_score.student_id;
student_id | name | student_id | subject | score
------------+--------+------------+---------+-------
1 | Tim | 1 | English | 90
1 | Tim | 1 | Math | 80
2 | Jim | 2 | English | 85
<null> | <null> | 5 | English | 92
从结果集可以看出,由于左表中不存在到与右表 student_id = 5
匹配的记录,因此最后一行左表的列的值为 NULL
。
右连接其实是左右表交换位置的左连接,即 A RIGHT JOIN B
就是 B LEFT JOIN A
,因此右连接很少使用。
上面例子中的右连接可以转换为下面的左连接:
SELECT student.*,student_score.* FROM student_score LEFT JOIN student ON student.student_id = student_score.student_id;
全连接
全连接是全外连接的简称,它是左连接和右连接的并集。全连接需要连接条件。
以下 SQL 语句将 student
表和 student_score
表全连接:
SELECT student.*,student_score.*FROM student FULL JOIN student_score ON student.student_id = student_score.student_id;
student_id | name | student_id | subject | score
------------+--------+------------+---------+--------
1 | Tim | 1 | English | 90
1 | Tim | 1 | Math | 80
2 | Jim | 2 | English | 85
<null> | <null> | 5 | English | 92
3 | Lucy | <null> | <null> | <null>
(5 rows)
由于两个表都使用相同的字段进行等值比较,因此您可以使用 USING
以下查询中所示的子句:
SELECT student.*,student_score.* FROM student FULL JOIN student_score USING(student_id);
全连接是左连接和右连接的并集。 上面的全连接可以使用 LEFT JOIN
, RIGHT JOIN
, 和 UNION
改写:
SELECT student.*,student_score.* FROM student LEFT JOIN student_score USING(student_id)
UNION
SELECT student.*,student_score.* FROM student RIGHT JOIN student_score USING(student_id);
本章节介绍了unvdb中的连接语句,包括交叉连接、内连接、左连接、右连接、全连接、自然连接
子查询用法与实例
子查询是嵌套是在另一个查询中的查询,也被称为内部查询。
通常情况下,我们会使用子查询构建更复杂的 SQL 语句。比如,您可以将子查询使用在 EXISTS
, IN
, ANY
, ALL
等表达式中,也可以将子查询的结果直接和值比较。
EXISTS 运算符中使用子查询
unvdb EXISTS
运算符需要一个子查询作为操作数,以检查此子查询是否返回行。
比如,要从 Sakila 数据库中的 language
表查找那些在 film
表中用到的语言,您可以使用如下语句:
SELECT * FROM language WHERE EXISTS( SELECT * FROM film WHERE film.language_id = language.language_id);
language_id | name | last_update
-------------+----------------------+---------------------
1 | English | 2006-02-15 05:02:19
在上面的语句中,下面的查询就是一个子查询:
SELECT * FROM film WHERE film.language_id = language.language_id
IN 运算符中使用子查询
IN
运算符也需要一个集合作为操作数,因此您也可以使用 子查询 作为 IN
运算符右侧的操作数。
比如,您可以使用如下带有 IN
运算符和子查询的语句达到上面的目的:
SELECT * FROM language WHERE language_id in(SELECT DISTINCT language_id FROM film);
language_id | name | last_update
-------------+----------------------+---------------------
1 | English | 2006-02-15 05:02:19
(1 row)
这里,下面的语句就是一个子查询,以查询出所有的影片用到的 language_id
:
SELECT DISTINCT language_id FROM film;
将子查询直接和值进行比较
比如,要计算租金高于平均租金的影片的数量,您可以使用一下语句实现:
SELECT count(*) FROM film WHERE rental_rate > (SELECT avg(rental_rate) FROM film);
count
-------
659
这里,我们使用了下面的子查询计算影片的平均租金:
SELECT avg(rental_rate) FROM film;
然后,将租金 rental_rate
和上面子查询的结果进行比较,从而得到租金比平均租金大的影片的数量。
子查询可以帮助您构建更复杂的 SQL 语句。
通用表表达式与递归查询
通用表表达式,简称为 CTE,它提供了一种语句级别的临时表的功能,以帮助你构建复杂但是清晰的 SQL 语句。
CTE 语法
unvdb 通用表表达式使用 WITH
关键字定义,下面是unvdb CTE 的语法:
WITH [RECURSIVE] cte_name [(cte_column_list)] AS (cte_definition) primary_statement;
这里:
通用表表达式以
WITH
关键字开始。RECURSIVE
关键字表示此通用表表达式是可递归查询的。它是可选的。cte_name
是通用表表达式是的名称,相当于临时表的表名。cte_column_list
是通用表表达式的列名的列表,多个列名使用逗号分隔。 它是可选的。cte_definition
是通用表表达式的辅助语句,它可以是SELECT
,INSERT
,UPDATE
, 或者DELETE
语句。primary_statement
是主要语句,它会用到上面WITH
中定义的通用表表达式。 它可以是SELECT
,INSERT
,UPDATE
, 或者DELETE
。
通用表表达式基本实例
假设,您想要确定每个影片的租金是否比它坐在的评级的平均租金高。 您可以使用如下带有 CTE 的语句:
WITH file_rating_avg AS (SELECT rating,avg(rental_rate) avg_rental_rate FROM film GROUP BY rating) SELECT f.film_id,f.title,f.rental_rate,a.avg_rental_rate,f.rental_rate > avg_rental_rate "Greater?" FROM film f,file_rating_avg a WHERE f.rating = a.rating LIMIT 10;
film_id | title | rental_rate | avg_rental_rate | Greater?
---------+------------------+-------------+--------------------+----------
1 | ACADEMY DINOSAUR | 0.99 | 3.0518556701030928 | f
2 | ACE GOLDFINGER | 4.99 | 2.8888764044943820 | t
3 | ADAPTATION HOLES | 2.99 | 2.9709523809523810 | t
4 | AFFAIR PREJUDICE | 2.99 | 2.8888764044943820 | t
5 | AFRICAN EGG | 2.99 | 2.8888764044943820 | t
6 | AGENT TRUMAN | 2.99 | 3.0518556701030928 | f
7 | AIRPLANE SIERRA | 4.99 | 3.0348430493273543 | t
8 | AIRPORT POLLOCK | 4.99 | 2.9387179487179487 | t
9 | ALABAMA DEVIL | 2.99 | 3.0348430493273543 | f
10 | ALADDIN CALENDAR | 4.99 | 2.9709523809523810 | t
(10 rows)
首先,我们使用如下语句定义了一个通用表表达式,名称为 file_rating_avg
:
WITH file_rating_avg AS ( SELECT rating, avg(rental_rate) avg_rental_rate FROM film GROUP BY rating)
然后,我们在后面的主语句中使用了 file_rating_avg
作为一个临时表。
SELECT
f.film_id,
f.title,
f.rental_rate,
a.avg_rental_rate,
f.rental_rate > avg_rental_rate "Greater?"
FROM
film f,
file_rating_avg a
WHERE f.rating = a.rating
LIMIT 10;
通用表表达式递归查询
unvdb 通用表表达式支持递归查询,这很适合应用在一些存储树形数据的方案中,比如产品的分类、系统的导航菜单等。
接下来,我们设计一个用于保存产品分类的表,然后使用 unvdb 通用表表达式获取一个指定分类以及该分类下的所有的下级分类。
我们要在 testdb
数据库中演示这个示例。请先使用下面的语句创建数据库testdb
:
CREATE DATABASE testdb;
选择 testdb
数据库为当前数据库:
\c testdb;
使用下面语句创建产品分类表 category
:
DROP TABLE IF EXISTS category;
CREATE TABLE category (id SERIAL PRIMARY KEY,name VARCHAR NOT NULL,parent_id INT,CONSTRAINT fk_category FOREIGN KEY(parent_id) REFERENCES category(id));
我们创建了一个 category
表,它有 id
, name
, parent_id
三个列,并且 parent_id
列存储上级分类的 id
。 注意,这里我们使用了 外键约束。
使用下面的语句插入一些包含了产品分类信息的行:
INSERT INTO category (id, name, parent_id)
VALUES
(1, 'ROOT', NULL),
(2, 'Baby', 1),
(3, 'Home And Kitchen', 1),
(4, 'Baby Care', 2),
(5, 'Feeding', 2),
(6, 'Gifts', 2),
(7, 'Safety', 2),
(8, 'Bedding', 3),
(9, 'Bath', 3),
(10, 'Furniture', 3),
(11, 'Grooming', 4),
(12, 'Hair Care', 4),
(13, 'Baby Foods', 5),
(14, 'Food Mills', 5),
(15, 'Solid Feeding', 5),
(16, 'Bed Pillows', 8),
(17, 'Bed Skirts', 8);
假设,你想要查询 id
为 2 的分类以及它的所有的下级分类,请使用下面的语句:
WITH RECURSIVE cte_categories AS (
SELECT
id,
name,
parent_id
FROM category
WHERE id = 2
UNION
SELECT
c.id,
c.name,
c.parent_id
FROM category c, cte_categories cs
WHERE cs.id = c.parent_id
)
SELECT *
FROM cte_categories;
id | name | parent_id
----+---------------+-----------
2 | Baby | 1
4 | Baby Care | 2
5 | Feeding | 2
6 | Gifts | 2
7 | Safety | 2
11 | Grooming | 4
12 | Hair Care | 4
13 | Baby Foods | 5
14 | Food Mills | 5
15 | Solid Feeding | 5
(10 rows)
我们通过 WITH RECURSIVE
定义了一个 CTE cte_categories
, 然后,我们在主语句中从 cte_categories
查找数据。
通过使用通用表表达式,您可以简化你的复杂的 SQL 语句,还可以使用递归查询。
UNION 用法与实例
UNION
是一个集合运算符,它返回两个集合的并集,它用于合并两个结果集。
其他的集合操作运算符还有: EXCEP
和 INTERSECT
UNION 语法
要合并两个结果集,请按照以下语法使用使用 UNION
运算符:
SELECT_statement_1
UNION [ALL]
SELECT_statement_2
[ORDER BY ...];
或者您可以同时合并多个结果集,如下:
SELECT_statement_1
UNION [ALL]
SELECT_statement_2
UNION [ALL]
SELECT_statement_3
UNION [ALL]
...
[ORDER BY ...];
这里:
SELECT_statement_N
都是个独立SELECT
语句。参与
UNION
运算的所有的结果集应该具有相同的列,并且列的数据类型和顺序应该相同。UNION
与UNION ALL
具有不同的逻辑:UNION
合并两个结果集,并删除重复的行。UNION ALL
合并两个结果,保留所有的行。
ORDER BY
子句用于对最终结果进行排序,它是可选的
UNION 实例
在本示例中,我们将使用 generate_series()
函数用来生成结果集。
我们需要用到两个结果集,先看第一个结果集:
SELECT generate_series(1, 5);
generate_series
-----------------
1
2
3
4
5
再看一下第二个结果集:
SELECT generate_series(3, 6);
generate_series
-----------------
3
4
5
6
现在,我们对两个结果集进行 UNION
运算:
SELECT generate_series(1, 5)
UNION
SELECT generate_series(3, 6) ORDER BY generate_series;
generate_series
-----------------
1
2
3
4
5
6
(6 rows)
从结果看出,UNION
运算符合并了第一个结果集和第二个结果集后删除了其中的重复的行,并返回了合并后的所有行。
如果你不想要删除重复的行,请使用 UNION ALL
运算符,如下:
SELECT generate_series(1, 5)
UNION ALL
SELECT generate_series(3, 6) ORDER BY generate_series;
generate_series
-----------------
1
2
3
3
4
4
5
5
6
(9 rows)
UNION
和 UNION ALL
用来合并两个或者结果集合并为一个结果集。不同的地方在于, UNION
会删除其中的重复的行。
INTERSECT 用法与实例
INTERSECT
是一个集合运算符,它返回两个集合的交集。也就是说,它返回那些同时位于两个结果集中的行。
其他的集合操作运算符还有: UNION
和 EXCEPT
。
INTERSECT 语法
要计算两个结果集的交集,请按照以下语法使用使用 INTERSECT
运算符:
SELECT_statement_1
INTERSECT
SELECT_statement_2
[ORDER BY ...];
或者您可以同时计算多个结果集的交集,如下:
SELECT_statement_1
INTERSECT
SELECT_statement_2
INTERSECT
SELECT_statement_3
INTERSECT
...
[ORDER BY ...];
SELECT_statement_N
都是个独立SELECT
语句。参与
INTERSECT
运算的所有的结果集应该具有相同的列,并且列的数据类型和顺序应该相同。ORDER BY
子句用于对最终结果进行排序,它是可选的。
INTERSECT 实例
在本示例中,我们将使用 generate_series()
函数用来生成结果集。
我们需要用到两个结果集,先看第一个结果集:
SELECT generate_series(1, 5);
generate_series
-----------------
1
2
3
4
5
再看一下第二个结果集:
SELECT generate_series(3, 6);
generate_series
-----------------
3
4
5
6
现在,我们对两个结果集进行 INTERSECT
运算:
SELECT generate_series(1, 5)
INTERSECT
SELECT generate_series(3, 6) ORDER BY generate_series;
generate_series
-----------------
3
4
5
(3 rows)
从结果看出,INTERSECT
运算符返回了第一个集合和第二个集合的共有的行。
INTERSECT
用来计算两个结果集的交集。
EXCEPT 用法与实例
EXCEPT
是一个集合运算符,它用于从一个集合中减去出现在另外一个集合中的行。
其他的集合操作运算符还有: UNION
和 INTERSECT
。
EXCEPT 语法
要从一个结果集中减去另一个结果集,请按照以下语法使用 EXCEPT
运算符:
SELECT_statement_1
EXCEPT
SELECT_statement_2
[ORDER BY ...];
或者您可以减去多个结果集,如下:
SELECT_statement_1
EXCEPT
SELECT_statement_2
EXCEPT
SELECT_statement_3
EXCEPT
...
[ORDER BY ...];
这里:
SELECT_statement_N
都是个独立SELECT
语句。参与
EXCEPT
运算的所有的结果集应该具有相同的列,并且列的数据类型和顺序应该相同。ORDER BY
子句用于对最终结果进行排序,它是可选的。
比如,下面的语句:
SELECT generate_series(1, 5)
EXCEPT
SELECT generate_series(3, 6);
generate_series
-----------------
1
2
这里, generate_series()
函数用来生成结果集。
先看一下第一个结果集:
SELECT generate_series(1, 5);
generate_series
-----------------
1
2
3
4
5
再看一下第二个结果集:
SELECT generate_series(3, 6);
generate_series
-----------------
3
4
5
6
EXCEPT
运算符从第一个结果集中的所有的行中删除同时位于第二个结果集中的行,并返回了第一个结果集中剩下的行。
EXCEPT 实例
要从影片表 film
中获取评级为 G
的影片,请使用下面的语句:
SELECT film_id, title, rating, length, rental_rate FROM film WHERE rating = 'G';
film_id | title | rating | length | rental_rate
---------+---------------------------+--------+--------+-------------
2 | ACE GOLDFINGER | G | 48 | 4.99
4 | AFFAIR PREJUDICE | G | 117 | 2.99
5 | AFRICAN EGG | G | 130 | 2.99
11 | ALAMO VIDEOTAPE | G | 126 | 0.99
22 | AMISTAD MIDSUMMER | G | 85 | 2.99
25 | ANGELS LIFE | G | 74 | 2.99
26 | ANNIE IDENTITY | G | 86 | 0.99
39 | ARMAGEDDON LOST | G | 99 | 0.99
43 | ATLANTIS CAUSE | G | 170 | 2.99
...
996 | YOUNG LANGUAGE | G | 183 | 0.99
(178 rows)
如果您想要从影片表 film
中获取评级为 G
的影片,但是不包括那些影片长度大于 55 分钟的影片,请使用如下带有 EXCEPT
运算符的语句:
SELECT film_id, title, rating, length, rental_rate FROM film WHERE rating = 'G'
EXCEPT
SELECT film_id, title, rating, length, rental_rate FROM film WHERE length > 55;
film_id | title | rating | length | rental_rate
---------+---------------------+--------+--------+-------------
292 | EXCITEMENT EVE | G | 51 | 0.99
2 | ACE GOLDFINGER | G | 48 | 4.99
247 | DOWNHILL ENOUGH | G | 47 | 0.99
430 | HOOK CHARIOTS | G | 49 | 0.99
542 | LUST LOCK | G | 52 | 2.99
497 | KILL BROTHERHOOD | G | 54 | 0.99
402 | HARPER DYING | G | 52 | 0.99
575 | MIDSUMMER GROUNDHOG | G | 48 | 4.99
237 | DIVORCE SHINING | G | 47 | 2.99
83 | BLUES INSTINCT | G | 50 | 2.99
862 | SUMMER SCARFACE | G | 53 | 0.99
697 | PRIMARY GLASS | G | 53 | 0.99
794 | SIDE ARK | G | 52 | 0.99
(13 rows)
如果您想要从影片表 film
中获取评级为 G
的影片,但是不包括那些影片长度大于 55 分钟的影片,还不包括那些租金大于 2.99 美元的影片,请使用如下带有 EXCEPT
运算符的语句:
SELECT film_id, title, rating, length, rental_rate FROM film WHERE rating = 'G'
EXCEPT
SELECT film_id, title, rating, length, rental_rate FROM film WHERE length > 55
EXCEPT
SELECT film_id, title, rating, length, rental_rate FROM film WHERE rental_rate >= 2.99;
film_id | title | rating | length | rental_rate
---------+------------------+--------+--------+-------------
292 | EXCITEMENT EVE | G | 51 | 0.99
247 | DOWNHILL ENOUGH | G | 47 | 0.99
430 | HOOK CHARIOTS | G | 49 | 0.99
497 | KILL BROTHERHOOD | G | 54 | 0.99
402 | HARPER DYING | G | 52 | 0.99
862 | SUMMER SCARFACE | G | 53 | 0.99
697 | PRIMARY GLASS | G | 53 | 0.99
794 | SIDE ARK | G | 52 | 0.99
(8 rows)
请注意,我们在语句的末尾放置了ORDER BY子句以按标题对电影进行排序。
INSERT 用法与实例
INSERT
语句用于向数据表中插入一个或多个新行。
INSERT
语法
要使用 unvdb INSERT
向表中插入新行,请使用如下的语法:
INSERT INTO table_name(column1, column2, …) VALUES (value11, value12, …) [, (value21, value22, …), ...] [ON CONFLICT conflict_target conflict_action] [RETURNING expr];
INSERT INTO
和VALUES
是关键字table_name
是要插入数据行的表名。(column1, column2, …)
是列列表,其中是通过逗号分隔的各个列。(value11, value12, …)
是值列表,其中是通过逗号分隔的各个列的值。值列表中的值于列列表中的列一一对应。要一次插入多个数据行,请使用多个使用逗号分隔的值列表。
ON CONFLICT
用来在 unvdb 中实现 upsert 操作。RETURNING
子句是可选的。它用于返回插入的行的信息。expr
可以是列名,或表达式等。
RETURNING 子句
INSERT
语句有一个可选 RETURNING
子句,用于返回插入行的信息。如果具有 RETURNING
子句时,INSERT
语句按照 RETURNING
子句返回,否则它返回成功插入的行数。
RETURNING
子句可采用以下几种形式:
返回指定的列,请使用列表。多个列使用逗号分隔。
RETURNING column1 RETURNING column1, column2
您还可以使用
AS
对列名指定别名:RETURNING column1 AS column1_new_1 RETURNING column1 AS column1_new_1, column2 AS column1_new_2
返回新行的所有的列,请使用星号 (
*
)。RETURNING *
返回一个表达式计算的值
RETURNING expr
不带 RETURNING
子句的返回值
没有指定 RETURNING
子句的 INSERT
语句的返回值具有一下形式:
INSERT oid count
这里:
oid
是一个对象标识符。unvdb 在内部将oid
用作其系统表的主键。通常,INSERT
语句返回oid
值为 0。count
是INSERT
语句成功插入的行数。
INSERT 语句示例
我们要在 testdb
数据库中演示下面的示例。请先使用下面的语句创建 testdb
数据库:
CREATE DATABASE testdb;
选择 testdb
数据库为当前数据库:
\c testdb;
为了演示,我们需要创建一个新表,命名为 student
:
DROP TABLE IF EXISTS student;
CREATE TABLE student (id SERIAL PRIMARY KEY,name VARCHAR(50) NOT NULL,gender CHAR(1) NOT NULL,birthday DATE,notes VARCHAR(255));
向表中插入单行
以下语句向 student
表中插入一个新行:
INSERT INTO student(name, gender)
VALUES ('Tom', 'M');
语句返回以下输出:
INSERT 0 1
这里, INSERT 0 1
中的 1
表示已经成功插入一行。
让我们使用 以下 SELECT
语句显示 student
表的内容:
SELECT * FROM student;
id | name | gender | birthday | notes
----+------+--------+----------+-------
1 | Tom | M | |
我们看到:
id
列的值为 1。这是因为id
列为SERIAL
列, unvdb 会自动生成一个序列值。birthday
和notes
列的值为NULL
。因为他们没有NOT NULL
约束,unvdb 使用NULL
插入到这些列中。
向表中插入单行并指定返回值
以下语句向 student
表中插入一个新行并返回插入的行:
INSERT INTO student(name, gender) VALUES ('Lucy', 'F') RETURNING *;
语句返回以下输出:
id | name | gender | birthday | notes
----+------+--------+----------+-------
2 | Lucy | F | |
这里, 由于 INSERT
语句带有 RETURNING *
子句,因此语句返回了插入的新行中的所有列。如果我们只想返回其中的一列或者几列,请在 RETURNING
子句指定具体的列。如下:
INSERT INTO student(name, gender) VALUES ('Jack', 'M') RETURNING id AS "Student ID", name, gender;
语句返回以下输出:
Student ID | name | gender
------------+------+--------
3 | Jack | M
这里,我们在 RETURNING
子句指定了 id
, name
和 gender
三列,并且为 id
指定了别名 Student ID
。
向表中插入多行
我们可以使用一个 INSERT
语句插入多行,如下:
INSERT INTO student(name, gender) VALUES ('Jim', 'M'), ('Kobe', 'M'), ('Linda', 'F') RETURNING *;
语句返回以下输出:
id | name | gender | birthday | notes
----+-------+--------+----------+-------
4 | Jim | M | |
5 | Kobe | M | |
6 | Linda | F | |
这里,我们使用一个 INSERT
语句向 student
表中插入了 3 行。
INSERT – 插入日期值
要将日期值插入具有 DATE
类型的列中,请使用格式为 'YYYY-MM-DD'
的日期字符串。
要向 student
表中插入一行带有生日的数据,请使用下面的语句:
INSERT INTO student (name, gender, birthday) VALUES('Alice', 'F', '2023-07-25') RETURNING *;
输出:
id | name | gender | birthday | notes
----+-------+--------+------------+-------
7 | Alice | F | 2023-07-25 |
INSERT
语句用来向一个表中插入一个或者多个新行。如果带有 RETURNING
子句, INSERT
语句将返回插入的新行的信息,否则它将返回成功插入的行数。
NSERT ON CONFLICT 语句用法与实例
INSERT ON CONFLICT
语句允许您在插入数据时处理一些数据冲突的情况,如果不存在冲突,则正常插入,如果存在冲突,可以更新已有的行。也就是说 INSERT ON CONFLICT
语句实现了 upsert 功能。
INSERT ON CONFLICT
语法
要在 unvdb 实现 upsert 功能,请按照如下语法使用 INSERT ON CONFLICT
语句
INSERT INTO table_name(column_list) VALUES(value_list) ON CONFLICT conflict_target conflict_action [RETURNING {* | column_names}];;
相比较于 INSERT
语句来说,INSERT ON CONFLICT
只是多了 ON CONFLICT
子句。
conflict_target
是存在冲突的对象,它可以是以下之一:一个列名。该列必须是主键或者唯一索引。
ON CONSTRAINT constraint_name
, 并且constraint_name
必须是唯一约束的名称。WHERE
子句。
conflict_action
是存在冲突时要采取的动作,它可以是以下之一:DO NOTHING
: 如果存在冲突,不采取任何动作。DO UPDATE
: 如果存在冲突,使用DO UPDATE SET column_1 = value_1, .. WHERE condition
更新表中的字段。
INSERT ON CONFLICT
实例
我们要在 testdb
数据库中演示下面的示例。请先使用下面的语句创建 testdb
数据库:
CREATE DATABASE testdb;
选择 testdb
数据库为当前数据库:
\c testdb;
为了演示,我们需要创建一个新表,命名为 users
:
CREATE TABLE users (ID SERIAL PRIMARY KEY,NICKNAME VARCHAR(50) NOT NULL,LOGIN_NAME VARCHAR(50) UNIQUE,NOTES VARCHAR(255));
这里,我们创建了一个 users
表,它有 id
, nickname
, login_name
, 和 notes
四列, 其中 login_name
是一个唯一索引列。
让我们再使用 INSERT
语句插入一些行到 users
表:
INSERT INTO users (nickname, login_name, notes) VALUES ('Tim', 'tim', 'This is Tim'),('Tom', 'tom', 'This is Tom');
让我们再插入一个新行,其中带有和已有行重复的 login_name
:
INSERT INTO users (nickname, login_name, notes) VALUES ('Tim2', 'tim', 'This is Tim2');
ERROR: duplicate key value violates unique constraint "users_login_name_key"
DETAIL: Key (login_name)=(tim) already exists.
让我们使用 INSERT ON CONFLICT
语句重试一次,以便在存在重复 login_name
时采取一些动作。我们可以采取两种动作:
使用
DO NOTHING
表示不做任何事情:INSERT INTO users (nickname, login_name, notes) VALUES ('Tim2', 'tim', 'This is Tim2') ON CONFLICT (login_name) DO NOTHING;
INSERT 0 0
这里,我们使用
DO NOTHING
选项。然后,unvdb没有返回错误。使用
DO UPDATE
更新其他的字段:INSERT INTO users (nickname, login_name, notes) VALUES ('Tim2', 'tim', 'This is Tim2') ON CONFLICT (login_name) DO UPDATE SET nickname = 'Tim2', notes = 'This is Tim2' RETURNING *;
id | nickname | login_name | notes ----+----------+------------+-------------- 1 | Tim2 | tim | This is Tim2 (1 row)
在
DO UPDATE
子句中,您还可以使用EXCLUDED
对象引用引发冲突的数据,上面的语句可以使用EXCLUDED
修改为如下语句:INSERT INTO users (nickname, login_name, notes) VALUES ('Tim2', 'tim', 'This is Tim2') ON CONFLICT (login_name) DO UPDATE SET nickname = EXCLUDED.nickname,notes = EXCLUDED.notes RETURNING *;
在冲突对象中,除了字段名称,您还可以使用约束名称。上面的语句可以使用约束名称
users_login_name_key
代替列名login_name
:INSERT INTO users (nickname, login_name, notes) VALUES ('Tim3', 'tim', 'This is Tim3') ON CONFLICT ON CONSTRAINT users_login_name_key DO UPDATE SET nickname = EXCLUDED.nickname,notes = EXCLUDED.notes RETURNING *;
id | nickname | login_name | notes ----+----------+------------+-------------- 1 | Tim3 | tim | This is Tim3 (1 row)
UPDATE 用法与实例
UPDATE
语句允许你更新表中的已有的数据行。
UPDATE 语法
UPDATE
语句可以更新表中的一行或者多行数据,可以更新表中的一个或者多个列的值。 以下是 UPDATE
语句的基本语法:
UPDATE [IGNORE] table_name SET column_name1 = value1,column_name2 = value2,... [WHERE clause] [RETURNING expr];
用法说明:
UPDATE
关键字后指定要更新数据的表名。使用
SET
子句设置列的新值。多个列使用逗号分隔。列的值可以是普通的字面值,也可以是表达式运算,还可以是子查询。使用
WHERE
子句指定要更新的行的条件。只有符合WHERE
条件的行才会被更新。WHERE
子句是可选的。如果不指定WHERE
子句,则更新表中的所有行。RETURNING
子句是可选的。它用于返回更新的行的信息。expr
可以是列名或者表达式,多个列或者表达式请使用逗号分隔。 您还可以使用*
表示表中的所有的列。如果不指定
RETURNING
子句,UPDATE
语句将返回更新的行数。
UPDATE
语句中的 WHERE
子句非常重要。除非您特意,否则不要省略 WHERE
子句。
UPDATE 实例
下面我们将通过几个实例来展示 UPDATE
的具体用法。
在以下实例中,customer
表中存储了客户的信息。
使用 UPDATE 修改单列值
在这个例子中,我们将把 customer_id
等于 1
的客户的电子邮件修改为 NEW.MARY.SMITH@sakilacustomer.org
。
使用以下
SELECT
语句查看更新前的数据。
SELECT first_name, last_name, email FROM customer WHERE customer_id = 1;
first_name | last_name | email
------------+-----------+-------------------------------
MARY | SMITH | MARY.SMITH@sakilacustomer.org
使用以下
UPDATE
语句更新email
列的值。
UPDATE customer SET email = 'NEW.MARY.SMITH@sakilacustomer.org' WHERE customer_id = 1;
UPDATE 1
在此 UPDATE
语句中:
通过
WHERE
子句指定更新的条件为customer_id = 1
。通过
SET
子句将email
列的值设置为新电子邮件。
UPDATE
返回了 UPDATE 1
,它表示更新了一行。
或者,您可以使用以下
RETURNING
子句直接查看更新后的数据:
UPDATE customer SET email = 'NEW.MARY.SMITH@sakilacustomer.org' WHERE customer_id = 1 RETURNING first_name, last_name, email;
first_name | last_name | email
------------+-----------+-----------------------------------
MARY | SMITH | NEW.MARY.SMITH@sakilacustomer.org
使用 UPDATE 修改多列值
在这个例子中,我们将同时更新 customer_id
等于 1
的客户的 first_name
, last_name
, email
列。
UPDATE customer SET first_name = 'Tim',last_name = 'Duncan',email = 'Tim.Duncan@sakilacustomer.org' WHERE customer_id = 1 RETURNING first_name, last_name, email;
first_name | last_name | email
------------+-----------+-------------------------------
Tim | Duncan | Tim.Duncan@sakilacustomer.org
使用表达式更新
使用 UPDATE
更新时,列的值可以设置为表达式的运算结果,比如函数或其他的运算。
下面的 UPDATE
更新所有客户的电子邮件的域名部分:
UPDATE customer SET email = REPLACE(email, 'sakilacustomer.org', 'sjkjc.com') RETURNING first_name, last_name, email;
first_name | last_name | email
-------------+--------------+---------------------------------
PATRICIA | JOHNSON | PATRICIA.JOHNSON@sjkjc.com
LINDA | WILLIAMS | LINDA.WILLIAMS@sjkjc.com
BARBARA | JONES | BARBARA.JONES@sjkjc.com
ELIZABETH | BROWN | ELIZABETH.BROWN@sjkjc.com
JENNIFER | DAVIS | JENNIFER.DAVIS@sjkjc.com
MARIA | MILLER | MARIA.MILLER@sjkjc.com
SUSAN | WILSON | SUSAN.WILSON@sjkjc.com
MARGARET | MOORE | MARGARET.MOORE@sjkjc.com
DOROTHY | TAYLOR | DOROTHY.TAYLOR@sjkjc.com
LISA | ANDERSON | LISA.ANDERSON@sjkjc.com
...
(599 rows)
注意,本例中没有使用 WHERE
子句,所以表中所有的数据都进行了更新。
使用子查询更新
下面实例展示了如何为没有绑定商店的客户绑定一个随机商店。
UPDATE customer SET store_id = (SELECT store_id FROM store ORDER BY random() LIMIT 1) WHERE store_id IS NULL;
在本例中,我们通过以下 SELECT
语句返回一个随机的商店 id:
SELECT store_id FROM store ORDER BY random() LIMIT 1
在 SET
子句中,将 store_id
的值设置为上面的子查询的结果。
UPDATE
语句用来更新一个表中的一个或者多个已有的行。如果带有 RETURNING
子句, UPDATE
语句将返回更新后的行,否则它将返回被更新的行数。
UPDATE … FROM 用法与实例
有时候,你需要根据另一个表中的数据更新一个表中的数据。比如:根据产品销量明细表更新销量统计表。
在 unvdb,您可以使用子查询做到一点,也可以更加方便地使用 UPDATE...FROM
语句完成它。
UPDATE…FROM 语法
要使用 UPDATE...FROM
语句根据另一个表中的数据更新一个表中的数据,请遵循以下语法:
UPDATE [IGNORE] table_name SET column_name1 = value1,column_name2 = value2,... FROM another_table[, ...] WHERE clause [RETURNING expr];
和普通的
UPDATE
语句相比,此语句多了FROM
子句,并且WHERE
子句是必须的。您需要在
WHERE
子句中指定两个表连接的条件。
比如下面的语句:
UPDATE a
SET a.c1 = b.c2
FROM b
WHERE a.b_id = b.id;
对于 a
表的每一行,该 UPDATE
语句都检查 b
表的每一行。如果值 a
表的 b_id
列的值等于在 b
表的 id
列,该 UPDATE
语句将更新 b
的表 c2
列的值到 a
表的 c1
列。
UPDATE…FROM 实例
这里的实例用到了以下表:
city
表中存储了城市的信息。country
表中存储了国家的信息。
假设现在有一个需求,需要更新 city
表中的城市名称,在其后添加 @
和国家名,请运行以下语句:
UPDATE city_copy a SET city = city || '@' || b.country FROM country b WHERE b.country_id = 1 or b.country_id = 2 RETURNING city_id, city;
city_id | city
---------+--------------------------------------------------
1 | A Corua (La Corua)@Spain
2 | Abha@Saudi Arabia
3 | Abu Dhabi@United Arab Emirates
4 | Acua@Mexico
5 | Adana@Turkey
6 | Addis Abeba@Ethiopia
7 | Aden@Yemen
8 | Adoni@India
9 | Ahmadnagar@India
10 | Akishima@Japan
...
(600 rows)
这里,我们在 UPDATE
语句中使用 FROM country b
子句从 country
表中获取满足条件 a.country_id = b.country_id
的行,并将该行中的 country
列的值应用到表达式 city || '@' || b.country
中。
您也可以使用子查询实现上面的需求,如下:
UPDATE city_copy a SET city = ( SELECT a.city || '@' || b.country FROM country b WHERE a.country_id = b.country_id) RETURNING city_id, city;
DELETE 语句用法与实例
DELETE
语句用于从表中删除符合指定条件的行或者所有行。
DELETE 语法
要从 unvdb 数据库中的表中删除行,请按照如下的语法形式使用 DELETE
语句。
DELETE FROM table_name [where_clause] [RETURNING expr];
在
DELETE FROM
关键字后的table_name
是要从中删除数据的表的名称。where_clause
是WHERE
子句。 您可以在其中使用条件来指定要删除表中的哪些行。WHERE
子句是可选的。如果不指定WHERE
子句,表中的所有行将被删除。RETURNING
子句是可选的。它用于返回删除的行的信息。expr
可以是列名或者表达式,多个列或者表达式请使用逗号分隔。 您还可以使用*
表示表中的所有的列。如果不指定
RETURNING
子句,DELETE
语句将返回删除的行数。
如果您要更加高效率的清空一个表,请优先使用 TRUNCATE TABLE
语句。
DELETE 语句示例
为了防止数据丢失,我们将创建一个表 film_copy
,它是 film
表的拷贝。
CREATE TABLE film_copy AS SELECT * FROM film;
film_copy
和 film
有相同的列和行。
使用 unvdb DELETE 从表中删除满足条件的行
要从 film_copy
表中删除 film_id
为 1 的 1 个影片,请使用如下语句:
DELETE FROM film_copy
WHERE film_id = 1;
DELETE 1
该语句返回 1 表示已经删除了 1 行。
要从 film_copy
表中删除 film_id
为 2, 3 或者 4 的 3 个影片,请使用如下语句:
DELETE FROM film_copy
WHERE film_id in (2, 3, 4);
DELETE 3
该语句返回 3 表示已经删除了 3 行。
使用 unvdb DELETE 删除并返回删除的行
要从 film_copy
表中删除 film_id
为 10 或者 11 的 2 个影片,并返回删除的影片的标题,请使用如下语句:
DELETE FROM film_copy
WHERE film_id in (10, 11)
RETURNING film_id, title;
film_id | title
---------+------------------
10 | ALADDIN CALENDAR
11 | ALAMO VIDEOTAPE
(2 rows)
这里, DELETE
删除了 2 行,并返回了删除的 2 行的中的部分列。
使用 unvdb DELETE 从表中删除所有行
要从 film_copy
表中删除所有的影片, 请使用如下语句:
DELETE FROM film_copy;
DELETE
语句用来从一个表中删除一个或多个行。 如果不带有 WHERE
子句, 则删除所有的行。 如果带有 RETURNING
子句, DELETE
语句将返回删除的行的信息,否则它返回删除的行数。
TRUNCATE TABLE 清空表
当我们需要清空一个表中的所有行时,除了使用 DELETE * FROM table
还可以使用 TRUNCATE TABLE
语句。
TRUNCATE
语句和不带任何条件的 DELETE
语句的效果相同。但是 TRUNCATE
速度更快,因为它不扫描表。并且,TRUNCATE
会立即回收磁盘空间。 TRUNCATE
在清空大表时很有用。
TRUNCATE
语法
要清空一个或者多个表中的所有的行,请按照以下语法使用使用 TRUNCATE
语句:
TRUNCATE [TABLE] [ONLY] table_name [ * ] [, ... ] [RESTART IDENTITY | CONTINUE IDENTITY] [ CASCADE | RESTRICT ]
TABLE
关键字是可选的。ONLY
关键字是可选的。如果在表名前指定了ONLY
,则只清空此表,不包含它的子表。否则,此表和它的子表都将被清空。table_name
是要清空的表的名字。 表名后的*
明确的指示子表也将被清空。您可以在一个
TRUNCATE
语句中清空多个表。 多个表名使用逗号分隔。RESTART IDENTITY
选项指示自动重置表中的列拥有的序列。CONTINUE IDENTITY
选项指示不改变表中的序列的值,它是默认的。CASCADE
选项指示同时清空那些通过外键引用table_name
的表。RESTRICT
选项指示如果有外键引用要清空的表,则拒绝操作。
TRUNCATE
实例
我们通过以下实例比较 TRUNCATE
和 DELETE
在清空一个表的效率。
首先,我们使用下面的语句创建一个 test_truncate
表用于测试:
CREATE TABLE test_truncate (v INTEGER);
然后,使用下面的 sql 语句插入 1000 万条数据:
INSERT INTO test_truncate (v) SELECT generate_series(1, 10000000) v;
为了观察查询语句消耗的时间,请先使用下面的命令打开计时:
\timing
通过以下 DELETE
语句删除表中的所有行:
DELETE FROM test_truncate;
DELETE 10000000
Time: 6566.458 ms (00:06.566)
上面是 DELETE
语句的返回结果。它显示了从表中删除 10000000 行,总共耗费 6566.458 毫秒。
再使用上面的 INSERT
语句重新插入 1000 万行后,通过以下 TRUNCATE TABLE
语句清空表:
TRUNCATE TABLE test_truncate;
TRUNCATE TABLE
Time: 31.785 ms
上面是 TRUNCATE TABLE
语句的返回结果。它显示了清空此表共耗费 31.785 毫秒。所以 TRUNCATE
比 DELETE
快的多。
CASE 条件表达式
CASE
表达式是一个条件表达式,它与其他编程语言中的 if-else 语句作用相同。
您可以在 SELECT
语句以及 WHERE
, GROUP BY
, 和 HAVING
子句中使用 CASE
表达式。
CASE
语法
这里是 unvdb CASE
表达式的语法:
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE value3
END
condition1
,condition2
是一些布尔表达式,返回值为true
或false
。这个表达式可以理解为:如果
condition1
为真返回result1
,如果condition2
为真返回result2
,否则返回result3
。您可以指定一个或者多个
WHEN ... THEN
判断。ELSE
部分是可以省略的,您只能指定一个ELSE
语句。所有的判断从上向下依次判断,直到遇到条件为真,并返回此条件对应的值。如果没有条件为真,则返回
ELSE
指定的值。如果没有指定ELSE
,则返回NULL
。
如果所有的条件都是针对同一个字段或者表达式进行等值判断,则可以将 CASE
表达式简写为如下形式:
CASE expr
WHEN value1 THEN result1
WHEN value2 THEN result2
ELSE result3
END
这个表达式的含义是:如果 expr
等于 value1
,则返回 result1
,如果 expr
等于 value2
,则返回 result2
,否则返回 result3
。
CASE
实例
简单示例
先看一个简单的示例。
比如,您想要获取当前日期是星期几的名称
SELECT
current_date "Today",
CASE
WHEN extract(DOW FROM current_date) = 1 THEN 'Monday'
WHEN extract(DOW FROM current_date) = 2 THEN 'Tuesday'
WHEN extract(DOW FROM current_date) = 3 THEN 'Wednesday'
WHEN extract(DOW FROM current_date) = 4 THEN 'Thursday'
WHEN extract(DOW FROM current_date) = 5 THEN 'Friday'
WHEN extract(DOW FROM current_date) = 6 THEN 'Saturday'
ELSE 'Sunday'
END "Day of Week";
Today | Day of Week
------------+-------------
2023-07-25 | Tuesday
这里,我们使用 current_date
获得当前的日期,并使用 extract()
函数获取当前日期的工作日编号。
由于上面的 CASE
表达式中的条件都是等值判断,因此我们可以将 CASE
表达式简化为:
SELECT
current_date "Today",
CASE extract(DOW FROM current_date)
WHEN 1 THEN 'Monday'
WHEN 2 THEN 'Tuesday'
WHEN 3 THEN 'Wednesday'
WHEN 4 THEN 'Thursday'
WHEN 5 THEN 'Friday'
WHEN 6 THEN 'Saturday'
ELSE 'Sunday'
END "Day of Week";
Today | Day of Week
------------+-------------
2023-07-25 | Tuesday
使用 CASE
表达式实现自定义排序
有时候单纯的按照字段的值排序并不能满足要求,我们需要按照自定义的顺序的排序。比如,我们需要按照电影分级 'G', 'PG', 'PG-13', 'R', 'NC-17'
的顺序对影片进行排序。
对于这样的需求,它可以理解为按照列表中元素的索引位置进行排序。我们使用 CASE
子句函数实现它。
假设您要根据影片的分级按照的 'G', 'PG', 'PG-13', 'R', 'NC-17'
顺序对影片进行排序。 下面使用 CASE
表达式实现自定义排序:
SELECT
film_id, title, rating
FROM
film
ORDER BY CASE rating
WHEN 'G' THEN 1
WHEN 'PG' THEN 2
WHEN 'PG-13' THEN 3
WHEN 'R' THEN 4
WHEN 'NC-17' THEN 5
END;
357 | GILBERT PELICAN | G
597 | MOONWALKER FOOL | G
354 | GHOST GROUNDHOG | G
...
595 | MOON BUNCH | PG
6 | AGENT TRUMAN | PG
600 | MOTIONS DETAILS | PG
...
9 | ALABAMA DEVIL | PG-13
657 | PARADISE SABRINA | PG-13
956 | WANDA CHAMBER | PG-13
...
749 | RULES HUMAN | R
8 | AIRPORT POLLOCK | R
17 | ALONE TRIP | R
...
520 | LICENSE WEEKEND | NC-17
517 | LESSON CLEOPATRA | NC-17
114 | CAMELOT VACATION | NC-17
...
(1000 rows)
在这个例子中,我们使用 CASE
子句将电影的分级转换为一个数字。然后使用 ORDER BY
按照这个数字进行排序。