Skip to content

Commit 097a303

Browse files
committed
add: pg post - sql fundmentals
1 parent bc69570 commit 097a303

File tree

2 files changed

+729
-1
lines changed

2 files changed

+729
-1
lines changed

content/posts/2025-11-07_postgresql-02-sql.md

Lines changed: 275 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ SQL (Structured Query Language) 是关系数据库最重要的操作语言,且
1313

1414
SQL 命令一般分为 DQL、DML、DDL 三类:
1515

16-
- DQL: 数据查询语句,基本就是 SELECT 查询命令,用于数据查询。
16+
- DQL: Data Query Language 数据查询语句,基本就是 SELECT 查询命令,用于数据查询。
1717

1818
- DML: Data Manipulation Language 数据操纵语言,主要用于插入、更新、删除数据,即 INSERT、UPDATE、DELETE 三类。
1919

@@ -216,3 +216,277 @@ ORDER BY 需要在 WHERE 语句之后,若顺序不对会报错。
216216
```SQL
217217
SELECT * FROM student WHERE age >= 15 ORDER BY age;
218218
```
219+
220+
还可以对多个查询结果排序
221+
222+
```SQL
223+
SELECT * FROM student ORDER BY age, student_name;
224+
```
225+
226+
后面加上 DESC 开逆序
227+
228+
```SQL
229+
SELECT * FROM student ORDER BY age DESC;
230+
SELECT * FROM student ORDER BY age DESC, student_name;
231+
```
232+
233+
### 分组查询
234+
235+
例如要统计不同年龄段的学生人数,可以使用分组查询
236+
237+
```SQL
238+
# SELECT age, count(*) FROM student GROUP BY age;
239+
240+
age | count
241+
-----+-------
242+
15 | 2
243+
14 | 1
244+
```
245+
246+
注意,使用 GROUP BY 语句时需要使用聚合函数,常见的有 count、sum 等
247+
248+
### 多表关联查询
249+
250+
多表关联查询也称表 join,例如有一张 class 班级表,建表语句如下
251+
252+
```SQL
253+
CREATE TABLE class(no int primary key, class_name varchar(40));
254+
```
255+
256+
插入一些测试数据
257+
258+
```SQL
259+
# INSERT INTO class VALUES(1, '初二(1)班');
260+
# INSERT INTO class VALUES(2, '初二(2)班');
261+
# INSERT INTO class VALUES(3, '初二(3)班');
262+
# INSERT INTO class VALUES(4, '初二(4)班');
263+
264+
# SELECT * FROM class;
265+
no | class_name
266+
----+-------------
267+
1 | 初二(1)班
268+
2 | 初二(2)班
269+
3 | 初二(3)班
270+
4 | 初二(4)班
271+
(4 rows)
272+
```
273+
274+
还有一张学生表 student,建表语句如下
275+
276+
```SQL
277+
CREATE TABLE student(no int primary key, student_name varchar(40), age int, class_no int);
278+
```
279+
280+
同样插入一些数据
281+
282+
```
283+
# SELECT * FROM student;
284+
no | student_name | age | class_no
285+
----+--------------+-----+----------
286+
1 | 张三 | 14 | 1
287+
2 | 吴二 | 15 | 1
288+
3 | 李四 | 13 | 2
289+
4 | 吴三 | 15 | 2
290+
5 | 王二 | 15 | 3
291+
6 | 李三 | 14 | 3
292+
7 | 吴二 | 14 | 3
293+
8 | 张四 | 14 | 4
294+
```
295+
296+
假如想要查询每个学生名字与班级名称之间的关系,就需要关联查询两张表
297+
298+
```SQL
299+
# SELECT student_name, class_name FROM student, class
300+
WHERE student.class_no = class.no;
301+
302+
student_name | class_name
303+
--------------+-------------
304+
张三 | 初二(1)班
305+
吴二 | 初二(1)班
306+
李四 | 初二(2)班
307+
吴三 | 初二(2)班
308+
王二 | 初二(3)班
309+
李三 | 初二(3)班
310+
吴二 | 初二(3)班
311+
张四 | 初二(4)班
312+
(8 rows)
313+
```
314+
315+
关联查询就是在 WHERE 子句中加上需要关联的条件 `WHERE student.class_no = class.no;`
316+
317+
由于两张表中有些列的名称相同,例如 student 中的 no 是学生编号,class 中的 no 是班级编号,所以关键条件中要明确使用 `表名.列名` 来明确唯一定位某一列
318+
319+
```SQL
320+
SELECT student_name, class_name FROM student a, class b
321+
WHERE a.class_no = b.no;
322+
```
323+
324+
还可以在关联查询的 WHERE 子句中加上其他过滤条件
325+
326+
```SQL
327+
SELECT student_name, class_name FROM student a, class b
328+
WHERE a.class_no = b.no
329+
AND age > 14;
330+
```
331+
332+
### 子查询
333+
334+
当一个查询是另一个查询的条件时,称为子查询。
335+
336+
主要有 4 种语法的子查询:
337+
338+
- 带有谓词 IN 的子查询: `expression [NOT] in (sqlstatement)`
339+
- 带有谓词 EXISTS 的子查询: `[NOT] EXISTS (sqlstatement)`
340+
- 带有比较运算符的子查询: `comparsion(>,<,=,!=) (sqlstatement)`
341+
- 带有 ANY (SOME) 或 ALL 谓词的子查询: `comparsion [ANY|ALL|SOME] (sqlstatement)`
342+
343+
下面仍然使用 class 和 student 两个表来做示例
344+
345+
- 使用带有谓词 IN 的子查询来查询 “初二(1)班” 的学生记录
346+
347+
```SQL
348+
SELECT * FROM student
349+
WHRER class_no IN (
350+
SELECT no FROM class
351+
WHERE class_name='初二(1)班'
352+
);
353+
```
354+
355+
查询结果如下
356+
357+
```SQL
358+
no | student_name | age | class_no
359+
----+--------------+-----+----------
360+
1 | 张三 | 14 | 1
361+
2 | 吴二 | 15 | 1
362+
(2 rows)
363+
```
364+
365+
- 同样可以使用带 EXISTS 谓词的子查询实现
366+
367+
```SQL
368+
SELECT * FROM student s
369+
WHERE EXISTS (
370+
SELECT 1 FROM class c
371+
WHERE s.class_no=c.no
372+
AND c.class_name = '初二(1)班'
373+
);
374+
```
375+
376+
EXISTS 是一个存在性测试,它检查子查询是否返回至少一行数据,执行逻辑如下:
377+
378+
1. 对于每个学生记录,数据库会执行一次子查询
379+
2. 子查询检查:class 表中查找匹配 WHERE 规则的记录
380+
3. 返回判断:如果查询找到匹配,EXISTS 为 True,则结果包含该学生,否则为 False 不包含
381+
382+
上面查询使用 SELECT 1 是因为只关系是否有返回值,而不关系返回值的类型
383+
384+
- 还可以使用带有比较符的子查询实现
385+
386+
```SQL
387+
SELECT * FROM student
388+
WHERE class_no = (
389+
SELECT no FROM calss c
390+
WHERE class_name = '初二(1)班'
391+
);
392+
```
393+
394+
- 还可以使用带 ANY (SOME) 或 ANY 谓词的子查询来实现
395+
396+
```SQL
397+
SELECT * FROM student
398+
WHERE class_no = ANY(
399+
SELECT no FROM class c
400+
WHERE class_name = '初二(1)班'
401+
);
402+
```
403+
404+
- 如果要查询*两个*班级的学生记录,则不能使用带有等于 “=” 比较符的子查询
405+
406+
```SQL
407+
SELECT * FROM student
408+
WHERE no = (
409+
SELECT no FROM class c
410+
WHERE class_name in ('初二(1)班', '初二(2)班')
411+
);
412+
413+
ERROR: more than one row returned by a subquery used as an expression
414+
```
415+
416+
上面报错说子查询不能返回多行,这种操作就是在说“找出学号等于(初二1班或初二2班的班级号)的学生“,但是等于号不能等于两个东西,只能等于一个东西,逻辑就错误了。
417+
418+
这种不能返回多行的子查询也叫*标量子查询*,标量子查询不仅能嵌套在 WHERE 子句中,也可以嵌套在 SELECT 的列表中。
419+
420+
如果要查询每个班级学生的最大年龄,可以使用下面 SQL 语句:
421+
422+
```SQL
423+
SELECT no, class_name, (
424+
SELECT max(age) AS max_age
425+
FROM student s WHERE s.no = c.no
426+
) AS max_age FROM class c;
427+
```
428+
429+
查询两个班级学生信息的时候,用带有 ANY (SOME) 谓词的子查询就没问题了,AYN 就是说只要等于里面任何*一个*就行。
430+
431+
```SQL
432+
SELECT * FROM student
433+
WHERE class_no = ANY(
434+
SELECT no FROM class c
435+
WHERE calss_name in ('初一(1)班', '初二(2)班')
436+
);
437+
```
438+
439+
## 其他 SQL 语句
440+
441+
### INSERT ... SELECT 语句
442+
443+
使用 INSERT ... SELECT 语句可以将一张表中的数据插入另一张表中,属于 DML 语句。
444+
445+
假设创建一张学生表的备用表 student_bak,建表语句如下:
446+
447+
```SQL
448+
CREATE TABLE student_bak(
449+
no int primary key,
450+
student_name varchar(40),
451+
age int,
452+
class_no int
453+
);
454+
```
455+
456+
可以使用下面语句将 student 备份到备份表中
457+
458+
```SQl
459+
INSERT INTO student_bak SELECT * FROM student;
460+
```
461+
462+
### UNION 语句
463+
464+
使用 UNION 语句可以把从两张表中查询出来的数据合在一个结果集下
465+
466+
```SQL
467+
SELECT * FROM student WHERE no = 1
468+
UNION
469+
SELECT * FROM student_bak WHERE no = 2;
470+
```
471+
472+
要注意的是,UNION 语句会将结果相同的两条记录合并成一条。
473+
如果不想合并相同记录,可以使用 UNION ALL 语句。
474+
475+
```SQl
476+
SELECT * FROM student WHERE no = 1
477+
UNION ALL
478+
SELECT * FROM student_bak WHERE no = 1;
479+
```
480+
481+
### TRUNCATE TABLE 语句
482+
483+
TRUNCATE TABLE 语句作用是清空表内容,不含 WHERE 条件的 DELETE 语句(`DELETE FROM table_name`)也可以情况表内容,但两者实现原理不同。
484+
485+
- TRUNCATE TABLE 是 DDL 数据定义语句,相当于重新定义一个新的表的方法,把原来表内容直接丢弃了,执行速度快
486+
- DELETE FROM 是 DML 数据操作语句,可以认为是一行一行地删除,执行速度较慢
487+
488+
## Summary 总结
489+
490+
从上面内容可以看出来,SQL是一种声明式编程语言,与命令式编程语言有较大的差异。
491+
声明式编程语言主要是描述用户需要做什么,需要得到什么结果,不像命令式编辑语言需要描述怎么做,过程是什么。
492+
SQL语句能智能地实现用户的需要,而不需要用户关心具体的运行过程。

0 commit comments

Comments
 (0)