PostgreSQL之查询

表连接

表t1:

 num | name
-----+------
   1 | a
   2 | b
   3 | c

表t2:

 num | value
-----+-------
   1 | xxx
   3 | yyy
   5 | zzz

1.交叉连接

T1 CROSS JOIN T2

  对来自于T1T2的行的每一种可能的组合(即笛卡尔积),连接表将包含这样一行:它由所有T1里面的列后面跟着所有T2里面的列构成。如果两个表分别有 N 和 M 行,连接表将有 N * M 行。

  FROM T1 CROSS JOIN T2等效于FROM T1 INNER JOIN T2 ON TRUE(见下文)。它也等效于FROM T1,T2

=> SELECT * FROM t1 CROSS JOIN t2;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   1 | a    |   3 | yyy
   1 | a    |   5 | zzz
   2 | b    |   1 | xxx
   2 | b    |   3 | yyy
   2 | b    |   5 | zzz
   3 | c    |   1 | xxx
   3 | c    |   3 | yyy
   3 | c    |   5 | zzz
(9 rows)

2.条件连接

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

  INNEROUTER对所有连接形式都是可选的。INNER是缺省;LEFTRIGHTFULL指示一个外连接。连接条件ONUSING子句中指定, 或者用关键字NATURAL隐含地指定。连接条件决定来自两个源表中的哪些行是匹配”的。  

  2.1 INNER JOIN

  对于 T1 的每一行 R1,生成的连接表都有一行对应 T2 中的每一个满足和 R1 的连接条件的行。

=> SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   3 | c    |   3 | yyy

  2.2  LEFT OUTER JOIN

  列用空值补齐。因此,生成的连接表里为来自 T1 的每一行都至少包含一行。首先,执行一次内连接。然后,为 T1 中每一个无法在连接条件上匹配 T2 里任何一行的行返回一个连接行,该连接行中 T2 的列用空值补齐。因此,生成的连接表里为来自 T1 的每一行都至少包含一行。

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   2 | b    |     |
   3 | c    |   3 | yyy

  2.3  RIGHT OUTER JOIN

  首先,执行一次内连接。然后,为 T2 中每一个无法在连接条件上匹配 T1 里任何一行的行返回一个连接行,该连接行中 T1 的列用空值补齐。因此,生成的连接表里为来自 T2 的每一行都至少包含一行。

=> SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   3 | c    |   3 | yyy
     |      |   5 | zzz
(3 rows)

  2.4 FULL OUTER JOIN

  首先,执行一次内连接。然后,为 T1 中每一个无法在连接条件上匹配 T2 里任何一行的行返回一个连接行,该连接行中 T2 的列用空值补齐。同样,为 T2 中每一个无法在连接条件上匹配 T1 里任何一行的行返回一个连接行,该连接行中 T1 的列用空值补齐。

=> SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   2 | b    |     |
   3 | c    |   3 | yyy
     |      |   5 | zzz

 

VALUES列表

  VALUES提供了一种生成常量表”的方法,它可以被使用在一个查询中而不需要实际在磁盘上创建一个表。

VALUES ( expression [, ...] ) [, ...]
=> SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS t (num,letter);
 num | letter
-----+--------
   1 | one
   2 | two
   3 | three
(3 rows)

 

从修改的行中返回数据

  在INSERT中,可用于RETURNING的数据是插入的行。 

CREATE TABLE users (firstname text, lastname text, id serial primary key);

INSERT INTO users (firstname, lastname) VALUES ('Joe', 'Cool') RETURNING id;

  在UPDATE中,可用于RETURNING的数据是被修改行的新内容。

UPDATE products SET price = price * 1.10
  WHERE price <= 99.99
  RETURNING name, price AS new_price;

  在DELETE中,可用于RETURNING的数据是删除行的内容。

DELETE FROM products
  WHERE obsoletion_date = 'today'
  RETURNING *;

 

WITH查询

  WITH提供了一种方式来书写在一个大型查询中使用的辅助语句。这些语句通常被称为公共表表达式或CTE,它们可以被看成是定义只在一个查询中存在的临时表。在WITH子句中的每一个辅助语句可以是一个SELECTINSERTUPDATEDELETE,并且WITH子句本身也可以被附加到一个主语句,主语句也可以是SELECTINSERTUPDATEDELETE

  1. WITH中的SELECT

  WITHSELECT的基本价值是将复杂的查询分解称为简单的部分。

WITH regional_sales AS (
    SELECT region, SUM(amount) AS total_sales
    FROM orders
    GROUP BY region
), top_regions AS (
    SELECT region
    FROM regional_sales
    WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT region,
       product,
       SUM(quantity) AS product_units,
       SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;

  可选的RECURSIVE修饰符将WITH从单纯的句法便利变成了一种在标准SQL中不能完成的特性。通过使用RECURSIVE,一个WITH查询可以引用它自己的输出。

WITH RECURSIVE search_graph(id, link, data, depth) AS (
    SELECT g.id, g.link, g.data, 1
    FROM graph g
  UNION ALL
    SELECT g.id, g.link, g.data, sg.depth + 1
    FROM graph g, search_graph sg
    WHERE g.id = sg.link
)
SELECT * FROM search_graph;

  WITH查询的一个有用的特性是在每一次父查询的执行中它们通常只被计算一次,即使它们被父查询或兄弟WITH查询引用了超过一次。 因此,在多个地方需要的昂贵计算可以被放在一个WITH查询中来避免冗余工作。

  如果 WITH 查询是非递归和边际效应无关的(就是说,它是一个SELECT包含没有可变函数),则它可以合并到父查询中,允许两个查询级别的联合优化。 默认情况下,这发生在如果父查询仅引用 WITH查询一次的时候,而不是它引用WITH查询多于一次时。 

WITH w AS (
    SELECT * FROM big_table
)
SELECT * FROM w WHERE key = 123;

  这个 WITH 查询将被合并,生成相同的执行计划为

SELECT * FROM big_table WHERE key = 123;

  2.WITH中的数据修改

  在WITH中使用数据修改语句(INSERTUPDATEDELETE),这允许你在同一个查询中执行多个而不同操作。

WITH moved_rows AS (
    DELETE FROM products
    WHERE
        "date" >= '2010-10-01' AND
        "date" < '2010-11-01'
    RETURNING *
)
INSERT INTO products_log
SELECT * FROM moved_rows;

  这个查询实际上从products把行移动到products_logWITH中的DELETE删除来自products的指定行,以它的RETURNING子句返回它们的内容,并且接着主查询读该输出并将它插入到products_log

  3.WITH中的MATERIALIZED

WITH w AS (
    SELECT * FROM big_table
)
SELECT * FROM w AS w1 JOIN w AS w2 ON w1.key = w2.ref
WHERE w2.key = 123;

  WITH查询将被物化,生成一个big_table的临时拷贝,然后与其自身 — 联合,这样将不能从索引上获得任何好处。 如果写成下面的形式,这个查询将被执行得更有效率。

WITH w AS NOT MATERIALIZED (
    SELECT * FROM big_table
)
SELECT * FROM w AS w1 JOIN w AS w2 ON w1.key = w2.ref
WHERE w2.key = 123;

  所以父查询的限制可以直接应用于big_table的扫描。

  一个NOT MATERIALIZED 可能不理想的例子为:

WITH w AS (
    SELECT key, very_expensive_function(val) as f FROM some_table
)
SELECT * FROM w AS w1 JOIN w AS w2 ON w1.f = w2.f;

  在这里,WITH查询的物化确保very_expensive_function每个表行只计算一次,而不是两次。

 

posted @ 2021-01-24 20:00  鄙人取个名字好难  阅读(248)  评论(0编辑  收藏  举报