PostgreSQL WITH 子句

在 PostgreSQL 中,WITH 子句提供了一种编写辅助语句的方法,以便在更大的查询中使用。
WITH 子句有助于将复杂的大型查询分解为更简单的表单,便于阅读。
这些语句通常称为通用表表达式,也可当做一个为查询而存在的临时表。
通用表表达式:Common Table Express, CTE
WITH 子句是在多次执行子查询时特别有用,允许我们在查询中通过它的名称(可能是多次)引用它。
WITH 子句在使用前必须先定义。

//WITH 查询的基础语法如下:
WITH
   name_for_summary_data AS (
      SELECT Statement)
   SELECT columns
   FROM name_for_summary_data
   WHERE conditions <=> (
      SELECT column
      FROM name_for_summary_data)
   [ORDER BY columns]
   
//name_for_summary_data 是 WITH 子句的名称,
//name_for_summary_data 可以与现有的表名相同,并且具有优先级。
//可以在 WITH 中使用数据 INSERT, UPDATE 或 DELETE 语句,允许您在同一个查询中执行多个不同的操作。

WITH 递归

在 WITH 子句中 可以使用自身输出的数据。
通用表表达式 (CTE) 具有一个重要的优点,那就是能够引用其自身,从而创建递归 CTE。递归 CTE 是一个重复执行初始 CTE 以返回数据子集直到获取完整结果集的公用表表达式。
实例:

mydb=# select * from company;
 id | name  | age |                      address                       | salary | join_date
----+-------+-----+----------------------------------------------------+--------+------------
  4 | Mark  |  25 | Rich-Mond                                          |  65000 | 2007-12-13
  5 | David |  27 | Texas                                              |  85000 | 2007-12-13
  3 | Teddy |  23 | Norway                                             |  20000 |
  2 | Allen |  25 | Texas                                              |        | 2007-12-13
  1 | Paul  |  32 | California                                         |  20000 | 2001-07-13
  8 | Paul  |  24 | Houston                                            |  20000 |
  6 | pasl  |  24 | Hou                                                |  20000 |
  7 | lili  |  23 | hy                                                 |  10000 |
  9 | James |  44 | Norway                                             |   5000 |
 10 | James |  45 | Texas                                              |   5000 |
(10 行记录)

//将使用 WITH 子句在上表中查询数据:
mydb=# With CTE AS
mydb-# (Select
mydb(#  ID
mydb(# , NAME
mydb(# , AGE
mydb(# , ADDRESS
mydb(# , SALARY
mydb(# FROM COMPANY )
mydb-# Select * From CTE;
 id | name  | age |                      address                       | salary
----+-------+-----+----------------------------------------------------+--------
  4 | Mark  |  25 | Rich-Mond                                          |  65000
  5 | David |  27 | Texas                                              |  85000
  3 | Teddy |  23 | Norway                                             |  20000
  2 | Allen |  25 | Texas                                              |
  1 | Paul  |  32 | California                                         |  20000
  8 | Paul  |  24 | Houston                                            |  20000
  6 | pasl  |  24 | Hou                                                |  20000
  7 | lili  |  23 | hy                                                 |  10000
  9 | James |  44 | Norway                                             |   5000
 10 | James |  45 | Texas                                              |   5000
(10 行记录)

//接下来让我们使用 RECURSIVE 关键字和 WITH 子句编写一个查询,查找 SALARY(工资) 字段小于 20000 的数据并计算它们的和:
mydb=# WITH RECURSIVE t(n) AS (
mydb(#    VALUES (0)
mydb(#    UNION ALL
mydb(#    SELECT SALARY FROM COMPANY WHERE SALARY < 20000
mydb(# )
mydb-# SELECT sum(n) FROM t;
  sum
-------
 20000
(1 行记录)

//下面我们建立一张和 COMPANY 表相似的 COMPANY1 表,
//注意:两个表的必须有相同的列,且列字段与类型一致
mydb=# CREATE TABLE COMPANY1(
mydb(#    ID INT PRIMARY KEY     NOT NULL,
mydb(#    NAME           TEXT    NOT NULL,
mydb(#    AGE            INT     NOT NULL,
mydb(#    ADDRESS        CHAR(50),
mydb(#    SALARY         REAL,
mydb(#    JOIN_DATE      DATE
mydb(# );
CREATE TABLE
mydb=# select * from company1;
 id | name | age | address | salary | join_date
----+------+-----+---------+--------+-----------
(0 行记录)
//使用 DELETE 语句和 WITH 子句删除 COMPANY 表中 SALARY(工资) 字段小于10000 的数据,
//并将删除的数据插入 COMPANY1 表,实现将 COMPANY 表数据转移到 COMPANY1 表中:
mydb=# WITH moved_rows AS (
mydb(#    DELETE FROM COMPANY
mydb(#    WHERE
mydb(#       SALARY < 10000
mydb(#    RETURNING *
mydb(# )
mydb-# INSERT INTO COMPANY1 (SELECT * FROM moved_rows);
INSERT 0 2

mydb=# select * from company1;
 id | name  | age |                      address                       | salary | join_date
----+-------+-----+----------------------------------------------------+--------+-----------
  9 | James |  44 | Norway                                             |   5000 |
 10 | James |  45 | Texas                                              |   5000 |
(2 行记录)

mydb=# select * from company;
 id | name  | age |                      address                       | salary | join_date
----+-------+-----+----------------------------------------------------+--------+------------
  4 | Mark  |  25 | Rich-Mond                                          |  65000 | 2007-12-13
  5 | David |  27 | Texas                                              |  85000 | 2007-12-13
  3 | Teddy |  23 | Norway                                             |  20000 |
  2 | Allen |  25 | Texas                                              |        | 2007-12-13
  1 | Paul  |  32 | California                                         |  20000 | 2001-07-13
  8 | Paul  |  24 | Houston                                            |  20000 |
  6 | pasl  |  24 | Hou                                                |  20000 |
  7 | lili  |  23 | hy                                                 |  10000 |
(6 行记录)

PostgreSQL HAVING 子句:筛选分组后的各组数据

HAVING 子句可以让我们筛选分组后的各组数据。

WHERE 子句与 HAVING 子句的区别:

  • WHERE 子句:在所选列上设置条件,
  • HAVING 子句:在由 GROUP BY 子句创建的分组上设置条件。
//下面是 HAVING 子句在 SELECT 查询中的位置:
SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY

//HAVING 子句必须放置于 GROUP BY 子句后面,ORDER BY 子句前面
//下面是 HAVING 子句在 SELECT 语句中基础语法:
SELECT column1, column2
FROM table1, table2
WHERE [ conditions ]
GROUP BY column1, column2
HAVING [ conditions ]
ORDER BY column1, column2

实例

//删除company表中数据
mydb=# delete from company;
DELETE 6
mydb=# select *from company
mydb-# ;
 id | name | age | address | salary | join_date
----+------+-----+---------+--------+-----------
(0 行记录)

//向company表中添加数据
mydb=# INSERT INTO COMPANY VALUES (1,'Paul',32,'California',20000),(2,'Allen',25,'Texas',15000),
(3,'Teddy',23,'Norway',20000),(4,'Mark',25,'Rich-Mond',65000),(5,'David',27,'South-Hall',85000),
(6,'Kim',22,'South-Hall',45000),(7,'James',24,'Houston',1000);
INSERT 0 7
mydb=# select *from company;
 id | name  | age |                      address                       | salary | join_date
----+-------+-----+----------------------------------------------------+--------+-----------
  1 | Paul  |  32 | California                                         |  20000 |
  2 | Allen |  25 | Texas                                              |  15000 |
  3 | Teddy |  23 | Norway                                             |  20000 |
  4 | Mark  |  25 | Rich-Mond                                          |  65000 |
  5 | David |  27 | South-Hall                                         |  85000 |
  6 | Kim   |  22 | South-Hall                                         |  45000 |
  7 | James |  24 | Houston                                            |   1000 |
(7 行记录)

//将找出根据 NAME 字段值进行分组,并且 name(名称) 字段的计数少于 2 数据:
mydb=# SELECT NAME FROM COMPANY GROUP BY name HAVING count(name) < 2;
 name
-------
 James
 Teddy
 Kim
 Mark
 Allen
 David
 Paul
(7 行记录)

//往company表里添加几条数据:
mydb=# INSERT INTO COMPANY VALUES (8, 'Paul', 24, 'Houston', 20000.00),(9, 'James', 44, 'Norway', 5000.00),(10, 'James', 45, 'Texas', 5000.00);
INSERT 0 3
mydb=# select * from company;
 id | name  | age |                      address                       | salary | join_date
----+-------+-----+----------------------------------------------------+--------+-----------
  1 | Paul  |  32 | California                                         |  20000 |
  2 | Allen |  25 | Texas                                              |  15000 |
  3 | Teddy |  23 | Norway                                             |  20000 |
  4 | Mark  |  25 | Rich-Mond                                          |  65000 |
  5 | David |  27 | South-Hall                                         |  85000 |
  6 | Kim   |  22 | South-Hall                                         |  45000 |
  7 | James |  24 | Houston                                            |   1000 |
  8 | Paul  |  24 | Houston                                            |  20000 |
  9 | James |  44 | Norway                                             |   5000 |
 10 | James |  45 | Texas                                              |   5000 |
(10 行记录)

//将找出根据 NAME 字段值进行分组,并且 name(名称) 字段的计数少于 2 数据:
mydb=# SELECT NAME FROM COMPANY GROUP BY name HAVING count(name) < 2;
 name
-------
 Mark
 Allen
 David
 Teddy
 Kim
(5 行记录)

//将找出根据 name 字段值进行分组,并且名称的计数大于 1 数据:
mydb=# SELECT NAME FROM COMPANY GROUP BY name HAVING count(name) > 1;
 name
-------
 Paul
 James
(2 行记录)

PostgreSQL DISTINCT 关键字:去除重复记录,只获取唯一的记录

在 PostgreSQL 中,DISTINCT 关键字与 SELECT 语句一起使用,用于去除重复记录,只获取唯一的记录。

我们平时在操作数据时,有可能出现一种情况,在一个表中有多个重复的记录,当提取这样的记录时,DISTINCT 关键字就显得特别有意义,它只获取唯一一次记录,而不是获取重复记录。

//用于去除重复记录的 DISTINCT 关键字的基本语法如下:
SELECT DISTINCT column1, column2,.....columnN
FROM table_name
WHERE [condition]

实例

mydb=# SELECT name FROM COMPANY;
 name
-------
 Paul
 Allen
 Teddy
 Mark
 David
 Kim
 James
 Paul
 James
 James
(10 行记录)

//在 SELECT 语句中使用 DISTINCT 子句:
mydb=# SELECT DISTINCT name FROM COMPANY;
 name
-------
 Allen
 David
 James
 Kim
 Mark
 Paul
 Teddy
(7 行记录)

———