数据库
首页 > 数据库> > PostgreSQL-查询

PostgreSQL-查询

作者:互联网

检索过程或从数据库中检索数据的命令称为查询。在 SQL 中,SELECT 命令用于指定查询。SELECT 命令的一般语法是:

[WITH with_queries] SELECT select_list FROM table_expression [sort_specification]

WITH 查询是最后处理的,因为它们是一项高级功能。

表引用可以是表名(可能是模式限定的),也可以是派生表,例如子查询、JOIN 构造或这些的复杂组合。如果 FROM 子句中列出了多个表引用,则表是交叉连接的(即,形成它们行的笛卡尔积;)。FROM 列表的结果是一个中间虚拟表,然后可以通过 WHERE、GROUP BY 和 HAVING 子句进行转换,最终成为整个表表达式的结果。

当表引用命名作为表继承层次结构的父表的表时,表引用不仅会生成该表的行,还会生成其所有后代表的行,除非关键字 ONLY 位于表名之前。但是,该引用仅生成出现在命名表中的列——添加到子表中的任何列都将被忽略。

您可以在表名之后写 * 来明确指定包含后代表,而不是只在表名之前写。没有真正的理由再使用这种语法了,因为搜索后代表现在始终是默认行为。但是,它支持与旧版本的兼容性。

表连接类型

Cross join

T1 CROSS JOIN T2

对于 T1 和 T2 中的每个可能的行组合(即笛卡尔积),连接表将包含一行,该行由 T1 中的所有列和 T2 中的所有列组成。如果表分别有 N 和 M 行,则连接表将有 N * M 行。

FROM T1 CROSS JOIN T2 等价于 FROM T1 INNER JOIN T2 ON TRUE。它也相当于 FROM T1、T2。

T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 ON boolean_expression
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 USING ( join column list )
T1 NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2

单词 INNER 和 OUTER 在所有形式中都是可选的。INNER 是默认值;LEFT、RIGHT 和 FULL 表示外连接。连接条件在 ON 或 USING 子句中指定,或由单词 NATURAL 隐式指定。连接条件确定两个源表中的哪些行被认为是“匹配”的。

INNER JOIN
对于 T1 的每一行 R1,连接表对于 T2 中满足与 R1 的连接条件的每一行都有一行。

LEFT OUTER JOIN
首先,执行内连接。然后,对于 T1 中与 T2 中的任何行不满足连接条件的每一行,在 T2 的列中添加一个带有空值的连接行。因此,连接表对于 T1 中的每一行总是至少有一行。

RIGHT OUTER JOIN
首先,执行内连接。然后,对于 T2 中与 T1 中的任何行不满足连接条件的每一行,在 T1 的列中添加一个带有空值的连接行。这与左连接相反:结果表将始终为 T2 中的每一行保留一行。

FULL OUTER JOIN
首先,执行内连接。然后,对于 T1 中与 T2 中的任何行不满足连接条件的每一行,在 T2 的列中添加一个带有空值的连接行。此外,对于 T2 中与 T1 中的任何行不满足连接条件的每一行,添加一个在 T1 的列中具有空值的连接行。

ON 子句是最通用的连接条件:它采用与 WHERE 子句中使用的相同类型的布尔值表达式。如果 ON 表达式的计算结果为真,则 T1 和 T2 中的一对行匹配。

USING 子句是一种速记,它允许您利用连接双方对连接列使用相同名称的特定情况。它采用逗号分隔的共享列名称列表,并形成一个连接条件,其中包括每个列的相等比较。例如,使用 USING (a, b) 连接 T1 和 T2 会产生连接条件 ON T1.a = T2.a AND T1.b = T2.b。

最后,NATURAL 是 USING 的简写形式:它形成一个 USING 列表,其中包含出现在两个输入表中的所有列名。与 USING 一样,这些列在输出表中只出现一次。如果没有公用列名,则 NATURAL JOIN 的行为类似于 JOIN ... ON TRUE,从而产生叉积连接。

测试:

create table t1(num integer,name varchar(50));
create table t2(num integer,value varchar(50));

insert into t1 values(1,'a'),(2,'b'),(3,'c');
insert into t2 values(1,'xxx'),(3,'yyy'),(5,'zzz');

连接测试:

SELECT * FROM t1 CROSS JOIN t2;

SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num;

SELECT * FROM t1 INNER JOIN t2 USING (num);

SELECT * FROM t1 NATURAL INNER JOIN t2;

SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num;

SELECT * FROM t1 LEFT JOIN t2 USING (num);

SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num;

SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num;

SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num AND t2.value = 'xxx';
SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = 'xxx';

这是因为放置在 ON 子句中的限制是在连接之前处理的,而放置在 WHERE 子句中的限制是在连接之后处理的。这对于内部连接无关紧要,但对于外部连接却很重要。

create table mytable1(a int,b int,name varchar(50));
create table mytable2(a int,b int,value varchar(50));
insert into mytable1 values(1,1,'111'),(2,2,'222'),(3,3,'333');
insert into mytable2 values(1,1,'xxx'),(3,3,'yyy'),(5,5,'zzz');

select * from mytable1 natural inner join mytable2;

update mytable2 set b = b+1 where a=1;

natural连接默认是T1和T2表同名字段作为连接条件。

子查询可以是VALUES列表:

FROM (VALUES ('anne', 'smith'), ('bob', 'jones'), ('joe', 'blow'))
 AS names(first, last)

表函数
表函数是产生一组行的函数,由基本数据类型(标量类型)或复合数据类型(表行)组成。它们在查询的 FROM 子句中用作表、视图或子查询。表函数返回的列可以包含在 SELECT、JOIN 或 WHERE 子句中,其方式与表、视图或子查询的列相同。

表函数也可以使用 ROWS FROM 语法组合,结果在并行列中返回;在这种情况下,结果行数是最大函数结果的行数,较小的结果用空值填充以匹配。

function_call [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]
ROWS FROM( function_call [, ... ] ) [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]

如果指定了 WITH ORDINALITY 子句,则会在函数结果列中添加一个 bigint 类型的附加列。此列对函数结果集的行进行编号,从 1 开始。(这是对 UNNEST ... WITH ORDINALITY 的 SQL 标准语法的概括。)默认情况下,序数列称为 ordinality,但列名不同可以使用 AS 子句分配给它。

可以使用任意数量的数组参数调用特殊表函数 UNNEST,它会返回相应数量的列,就像在每个参数上分别调用 UNNEST并使用 ROWS FROM 构造组合一样。

UNNEST( array_expression [, ... ] ) [WITH ORDINALITY] [[AS] table_alias [(column_alias [, ... ])]]

如果没有指定table_alias,则使用函数名作为表名;在 ROWS FROM() 构造的情况下,使用第一个函数的名称。

如果未提供列别名,则对于返回基本数据类型的函数,列名也与函数名相同。对于返回复合类型的函数,结果列获取该类型的各个属性的名称。

举个栗子:

CREATE TABLE foo (fooid int, foosubid int, fooname text);

CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$
      SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;

SELECT * FROM getfoo(1) AS t1;

SELECT * FROM foo
WHERE foosubid IN (
                    SELECT foosubid
                    FROM getfoo(foo.fooid) z
                    WHERE z.fooid = foo.fooid
                  );

CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);

SELECT * FROM vw_getfoo;

在某些情况下,定义可以根据调用方式返回不同列集的表函数很有用。为了支持这一点,表函数可以声明为返回没有 OUT 参数的伪类型记录。当在查询中使用这样的函数时,必须在查询本身中指定预期的行结构,以便系统知道如何解析和计划查询。此语法如下所示:

function_call [AS] alias (column_definition [, ... ])
function_call AS [alias] (column_definition [, ... ])
ROWS FROM( ... function_call AS (column_definition [, ... ]) [, ... ] )

例子:

SELECT *
FROM ROWS FROM
    (
        json_to_recordset('[{"a":40,"b":"foo"},{"a":"100","b":"bar"}]')
            AS (a INTEGER, b TEXT),
        generate_series(1, 3)
    ) AS x (p, q, s)
ORDER BY p;

LATERAL子查询

出现在 FROM 中的子查询前面可以有关键字 LATERAL。这允许他们引用前面的 FROM 项提供的列。(没有 LATERAL,每个子查询都是独立评估的,因此不能交叉引用任何其他 FROM 项。)

FROM 中出现的表函数也可以在关键字 LATERAL 之前,但对于函数,关键字是可选的;在任何情况下,函数的参数都可以包含对前面 FROM 项提供的列的引用。

LATERAL 项可以出现在 FROM 列表的顶层,或者出现在 JOIN 树中。在后一种情况下,它还可以引用位于右侧的 JOIN 左侧的任何项目。

当 FROM 项目包含 LATERAL 交叉引用时,评估过程如下:对于提供交叉引用列的 FROM 项目的每一行,或提供列的多个 FROM 项目的行集,使用 LATERAL 项目评估该行或行集的列值。结果行像往常一样与计算它们的行连接。对列源表中的每一行或每组行重复此操作。

LATERAL 主要用于计算要连接的行需要交叉引用的列时。一个常见的应用是为一个集合返回函数提供一个参数值。例如,假设 vertices(polygon) 返回多边形的顶点集,我们可以识别存储在表中的多边形的靠近在一起的顶点:

SELECT p1.id, p2.id, v1, v2
FROM polygons p1, polygons p2,
     LATERAL vertices(p1.poly) v1,
     LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

等价于

SELECT p1.id, p2.id, v1, v2
FROM polygons p1 CROSS JOIN LATERAL vertices(p1.poly) v1,
     polygons p2 CROSS JOIN LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

LATERAL子查询的主要应用是获取from后面LATERAL之前的表名的字段。

分组集

使用分组集的概念可以实现比上述更复杂的分组操作。FROM 和 WHERE 子句选择的数据按每个指定的分组集单独分组,为每个组计算聚合,就像简单的 GROUP BY 子句一样,然后返回结果。例如:

 create table items_sold(
  brand varchar(30),
  size char(1),
  sales numeric
  );

  insert into items_sold values('Foo','L',10),('Foo','M',20),('Bar','M',15),('Bar','L',5);

 SELECT brand, size, sum(sales) FROM items_sold GROUP BY GROUPING SETS ((brand), (size), ());

GROUPING SETS 的每个子列表可以指定零个或多个列或表达式,其解释方式与直接在 GROUP BY 子句中的方式相同。一个空的分组集意味着所有的行都被聚合到一个单独的组中(即使没有输入行也会被输出),正如上面对于没有 GROUP BY 子句的聚合函数的情况所描述的那样。GROUPING SETS将后面指定的表达式依次分组。

ROLLUP ( e1, e2, e3, ... )

相当于

GROUPING SETS (
    ( e1, e2, e3, ... ),
    ...
    ( e1, e2 ),
    ( e1 ),
    ( )
)

还有:

CUBE ( e1, e2, ... )

相当于

GROUPING SETS (
      ( a, b, c ),
      ( a, b    ),
      ( a,    c ),
      ( a       ),
      (    b, c ),
      (    b    ),
      (       c ),
      (         )
  )

标签:JOIN,t2,T2,查询,PostgreSQL,T1,连接,SELECT
来源: https://www.cnblogs.com/shigongp/p/16655115.html