PgSQL

基本使用

version()

SELECT version();

current_date

SELECT CURRENT_DATE;

CRUD

SELECT 2 + 2;

DROP TABLE IF EXISTS weather;

DROP TABLE IF EXISTS cities;

-- internal commands, starts with '\'
-- create table
-- smallint, real, double precision, char(N), varchar(N), date, time, timestamp, and interval, as well as other types of general utility and a rich set of geometric types.
CREATE TABLE weather (
  city varchar(80),
  temp_lo int, -- low temperature
  temp_hi int, -- high temperature
  prcp real, -- precipitation
  date date
);

CREATE TABLE cities (
  name varchar(80),
  location point
);

INSERT INTO cities VALUES ('San Francisco', '(-194.0, 53.0)');

INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27');

INSERT INTO weather (city, temp_lo, temp_hi, prcp, date) VALUES ('San Francisco', 43, 57, 0.0, '1994-11-29');

INSERT INTO weather (date, city, temp_hi, temp_lo) VALUES ('1994-11-29', 'Hayward', 54, 37);

SELECT * FROM weather;

SELECT
  city,
  temp_lo,
  temp_hi,
  prcp,
  date
FROM
  weather;

/

SELECT
  city,
  (temp_hi + temp_lo) / 2 AS temp_avg,
  date
FROM
  weather;

>

SELECT
  *
FROM
  weather
WHERE
  city = 'San Francisco'
  AND prcp > 0.0;

ORDER BY

SELECT
  *
FROM
  weather
ORDER BY
  city;

SELECT
  *
FROM
  weather
ORDER BY
  city,
  temp_lo;

DISTINCT

SELECT DISTINCT
  city
FROM
  weather;

SELECT DISTINCT
  city
FROM
  weather
ORDER BY
  city;

WHERE

SELECT
  *
FROM
  weather,
  cities
WHERE
  city = name;

JOIN

SELECT
  *
FROM
  weather
  JOIN cities ON city = name;
SELECT
  weather.city,
  weather.temp_lo,
  weather.temp_hi,
  weather.prcp,
  weather.date,
  cities.location
FROM
  weather
  JOIN cities ON weather.city = cities.name;

JOIN ON

SELECT
  *
FROM
  weather
  LEFT OUTER JOIN cities ON weather.city = cities.name;
SELECT
  w1.city,
  w1.temp_lo AS low,
  w1.temp_hi AS high,
  w2.city,
  w2.temp_lo AS low,
  w2.temp_hi AS high
FROM
  weather w1
  JOIN weather w2 ON w1.temp_lo < w2.temp_lo
    AND w1.temp_hi > w2.temp_hi;
SELECT
  *
FROM
  weather w
  JOIN cities c ON w.city = c.name;

MAX

ERROR:

SELECT city FROM weather WHERE temp_lo = max(temp_lo);     WRONG
SELECT
  max(temp_lo)
FROM
  weather;
SELECT
  city
FROM
  weather
WHERE
  temp_lo = (
    SELECT
      max(temp_lo)
    FROM
      weather);
SELECT
  city,
  max(temp_lo),
  count(*) FILTER (WHERE temp_lo < 30)
FROM
  weather
WHERE
  city LIKE 'S%' -- (1)
GROUP BY
  city
HAVING
  max(temp_lo) < 40;

UPDATE

-- update
UPDATE
  weather
SET
  temp_hi = temp_hi - 2,
  temp_lo = temp_lo - 2
WHERE
  date > '1994-11-28';

DELETE

DELETE FROM weather
WHERE city = 'Hayward';

高级功能

VIEW

大量使用视图是良好的 SQL 数据库设计的一个关键方面。视图允许将表的结构细节封装在一致的接口后面,这些细节可能会随着应用程序的发展而改变。

视图几乎可以用在任何可以使用真实表的地方。在其他视图上建立视图的情况并不少见。

CREATE VIEW myview AS
SELECT
  name,
  temp_lo,
  temp_hi,
  prcp,
  date,
  location
FROM
  weather,
  cities
WHERE
  city = name;
SELECT
  *
FROM
  myview;

COPY

COPY weather
FROM
  '/home/user/weather.txt';

FOREIGN KEY

外键的行为可以根据你的应用进行细微的调整。

CREATE TABLE cities (
        name     varchar(80) primary key,
        location point
);

CREATE TABLE weather (
        city      varchar(80) references cities(name),
        temp_lo   int,
        temp_hi   int,
        prcp      real,
        date      date
);

running

INSERT INTO weather VALUES ('Berkeley', 45, 53, 0.0, '1994-11-28');

should be

ERROR:  insert or update on table "weather" violates foreign key constraint "weather_city_fkey"
DETAIL:  Key (city)=(Berkeley) is not present in table "cities".

Transactions

BEGIN;
UPDATE accounts SET balance = balance - 100.00 WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00 WHERE name = 'Bob';
-- oops ... forget that and use Wally's account
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00 WHERE name = 'Wally';
COMMIT;

Window Functions

快速开始

求所在部门的平均工资

SELECT
  depname, empno, salary, avg(salary)
OVER (PARTITION BY depname)
FROM empsalary;
  depname  | empno | salary |          avg
-----------+-------+--------+-----------------------
 develop   |    11 |   5200 | 5020.0000000000000000
 develop   |     7 |   4200 | 5020.0000000000000000
 develop   |     9 |   4500 | 5020.0000000000000000
 develop   |     8 |   6000 | 5020.0000000000000000
 develop   |    10 |   5200 | 5020.0000000000000000
 personnel |     5 |   3500 | 3700.0000000000000000
 personnel |     2 |   3900 | 3700.0000000000000000
 sales     |     3 |   4800 | 4866.6666666666666667
 sales     |     1 |   5000 | 4866.6666666666666667
 sales     |     4 |   4800 | 4866.6666666666666667
(10 rows)

每个部门的工资排名:

SELECT depname, empno, salary,
       rank() OVER (PARTITION BY depname ORDER BY salary DESC)
FROM empsalary;

rank 函数为当前行的分区中每个不同的 ORDER BY 值产生一个数字等级,使用 ORDER BY 子句定义的顺序。rank 不需要明确的参数,因为其行为完全由 OVER 子句决定。

  depname  | empno | salary | rank
-----------+-------+--------+------
 develop   |     8 |   6000 |    1
 develop   |    10 |   5200 |    2
 develop   |    11 |   5200 |    2
 develop   |     9 |   4500 |    4
 develop   |     7 |   4200 |    5
 personnel |     2 |   3900 |    1
 personnel |     5 |   3500 |    2
 sales     |     1 |   5000 |    1
 sales     |     4 |   4800 |    2
 sales     |     3 |   4800 |    2
(10 rows)

ORDER BY

ORDER BY

SELECT salary, sum(salary) OVER () FROM empsalary;
 salary |  sum
--------+-------
   5200 | 47100
   5000 | 47100
   3500 | 47100
   4800 | 47100
   3900 | 47100
   4200 | 47100
   4500 | 47100
   4800 | 47100
   6000 | 47100
   5200 | 47100
(10 rows)
SELECT salary, sum(salary) OVER (ORDER BY salary) FROM empsalary;
 salary |  sum
--------+-------
   3500 |  3500
   3900 |  7400
   4200 | 11600
   4500 | 16100
   4800 | 25700
   4800 | 25700
   5000 | 30700
   5200 | 41100
   5200 | 41100
   6000 | 47100
(10 rows)

不能使用的情况

窗口函数只允许在查询的 SELECT 和 ORDER BY 子句中使用。其他地方例如 GROUP BY、HAVING 和 WHERE 子句中是禁止的。

这是因为它们在逻辑上是在这些子句的处理之后执行的。

另外,窗口函数在非窗口聚合函数之后执行。这意味着在一个窗口函数的参数中包含一个聚合函数的调用是有效的,但反之则无效。

如果需要在窗口计算执行后对行进行过滤或分组,你可以使用一个子选择。例如。

SELECT depname, empno, salary, enroll_date
FROM
  (SELECT depname, empno, salary, enroll_date,
          rank() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos
     FROM empsalary
  ) AS ss
WHERE pos < 3;

多个窗口函数

上述查询只显示内部查询中排名小于 3 的记录。

当一个查询涉及到多个窗口函数时,可以用一个单独的 OVER 子句来写出每一个窗口函数。

但是如果几个函数需要相同的窗口行为,这样做是重复和容易出错的。

相反,可以在 WINDOW 子句中命名每个窗口行为,然后在 OVER 中引用。比如说。

SELECT sum(salary) OVER w, avg(salary) OVER w
  FROM empsalary
  WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);

Inheritance 继承

创建两个表。一张城市表和一张首都表。当然,首都也是城市,

我们希望在列出所有城市时,能以隐含的方式显示首都。下面是一些备选方案。

使用视图实现:

CREATE TABLE capitals (
  name       text,
  population real,
  elevation  int,    -- (in ft)
  state      char(2)
);

CREATE TABLE non_capitals (
  name       text,
  population real,
  elevation  int     -- (in ft)
);

CREATE VIEW cities AS
  SELECT name, population, elevation FROM capitals
    UNION
  SELECT name, population, elevation FROM non_capitals;

使用继承实现:

在 PostgreSQL 中,一个表可以从零个或多个其他表继承。

CREATE TABLE cities (
  name       text,
  population real,
  elevation  int     -- (in ft)
);

CREATE TABLE capitals (
  state      char(2) UNIQUE NOT NULL
) INHERITS (cities);

capitals 继承了城市的所有列(名称、人口和海拔)。capitals 表有一个额外的列,state。

name 列的类型是 text,这是 PostgreSQL 的原生类型,用于可变长度的字符串。

继承会自动从子表中查询

SELECT name, elevation
  FROM cities
  WHERE elevation > 500;

你可以使用 ONLY 避免自动查询

SELECT name, elevation
    FROM ONLY cities
    WHERE elevation > 500;

SQL 语言

常量

  • 字符串,用单引号

两个仅由至少一个换行的空白处隔开的字符串常量被连接起来,并有效地处理,就像字符串被写成一个常量一样。比如说

SELECT 'foo'
'bar';

SELECT 'foobar';

相等

但是

SELECT 'foo'      'bar';

是不合法的

g