PostgreSQL簡介(一)—— Getting Started
1、What is PostgreSQL?
PostgreSQL是一個開源的物件關係型資料庫,開發於加州伯克利電腦科學實驗室。支援大部分標誌SQL語法,並且提供很多現代功能:
- 複雜查詢
- 外來鍵
- 觸發器
- 檢視更新
- 事務完整性
- 多版本併發控制
此外,PostgreSQL還提供很多方式讓使用者自定義擴充套件,例如增加如下物件:
- 資料型別(data types)
- 函式(functions)
- 操作(operators)
- 統計函式(aggregate functions)
- 索引方法(index methods)
- 過程語言(procedural languages)
And because of the liberal license, PostgreSQL can be used, modified, and distributed by anyone free of charge for any purpose, be it private, commercial, or academic.
2、Installation
--- 安裝yum.repo ---
[[email protected]-db ~]# yum install https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-7-x86_64/pgdg-centos10-10-2.noarch.rpm
--- 安裝pg server ---
[[email protected]-db ~]# yum install postgresql10-server
安裝完之後,會自動建立一個作業系統使用者:postgres
--- 初始化資料庫(Optionally) ---
[[email protected]-db ~]# /usr/pgsql-10/bin/postgresql-10-setup initdb
--- 啟動資料庫(Optionally) ---
[[email protected]-db ~]# systemctl start postgresql-10
--- 設定開機自啟動(Optionally) ---
[[email protected]-db ~]# systemctl enable postgresql-10
3、Getting Started
3.1、建立資料庫
[root@node-db ~]# createdb mydb
createdb: could not connect to database template1: FATAL: role "root" does not exist
原因:當前作業系統使用者(root)並非pg使用者
--- 切換使用者 ---
[[email protected] ~]# su - postgres
Last login: Thu Jul 19 10:25:07 CST 2018 on pts/0
--- 建立資料庫 ---
-bash-4.2$ createdb mydb
--- 刪除資料庫 ---
-bash-4.2$ dropdb mydb
3.2、訪問資料庫
訪問資料庫可以使用互動式命令列工具psql,或者使用圖形介面工具pgAdmin,在或者是自己開發一個自定義的管理應用。
--- 使用psql連線資料庫 ---
-bash-4.2$ psql mydb
psql (10.4)
Type "help" for help.
mydb=#
--- 查詢資料庫版本 ---
mydb=# select version();
version
---------------------------------------------------------------------------------------------------------
PostgreSQL 10.4 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28), 64-bit
(1 row)
PostgreSQL有一些內在的命令,這些命令並非SQL命令,它們通常以”\”開頭,例如:
--- 列印幫助資訊 ---
mydb=# \h
--- 退出psql ---
mydb=# \q
3.3、SQL語法
PostgreSQL是物件關係型資料庫管理系統,本質上還是關係型資料庫,依然使用table來儲存資料,資料在table中以row的形式存在,row有一組column組成,column的順序是固定的,查詢時,SQL不保證row的順序;
- 建表
postgres=# CREATE TABLE weather (
postgres(# city varchar(80),
postgres(# temp_lo int, -- low temperature
postgres(# temp_hi int, -- high temperature
postgres(# prcp real, -- precipitation
postgres(# date date
postgres(# );
CREATE TABLE
postgres=#
postgres=# CREATE TABLE cities (
postgres(# name varchar(80),
postgres(# location point
postgres(# );
CREATE TABLE
PostgreSQL支援標準的SQL型別:
int
,smallint
,real
,double precision
,char(*N*)
,varchar(*N*)
,date
,time
,timestamp
, 和interval
,此外,使用者還可以建立自定義型別。
- 刪除表
postgres=# drop table weather;
PostgreSQL以分號結尾,且在SQL中除雙引號外不區分大小寫,回車並不代表輸入結束。
- 插入資料
postgres=# INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27');
INSERT 0 1
postgres=# INSERT INTO cities VALUES ('San Francisco', '(-194.0, 53.0)');
INSERT 0 1
postgres=# INSERT INTO weather (city, temp_lo, temp_hi, prcp, date)
postgres-# VALUES ('San Francisco', 43, 57, 0.0, '1994-11-29');
INSERT 0 1
postgres=# INSERT INTO weather (date, city, temp_hi, temp_lo)
postgres-# VALUES ('1994-11-29', 'Hayward', 54, 37);
INSERT 0 1
- 將資料寫入檔案
--- 將資料寫入檔案 ---
postgres=# copy weather to '/tmp/weather.txt';
COPY 3
postgres-# \q
-bash-4.2$ cat /tmp/weather.txt
San Francisco 46 50 0.25 1994-11-27
San Francisco 43 57 0 1994-11-29
Hayward 37 54 \N 1994-11-29
--- 將資料列印在控制檯 ---
postgres=# copy weather to stdout;
San Francisco 46 50 0.25 1994-11-27
San Francisco 43 57 0 1994-11-29
Hayward 37 54 \N 1994-11-29
copy寫入檔案時,必須指定絕對路徑
- 從檔案中載入資料
postgres=# copy weather from '/tmp/weather.txt';
COPY 3
- 簡單查詢
--- 使用*查詢所有列 ---
postgres=# select * from weather;
city | temp_lo | temp_hi | prcp | date
---------------+---------+---------+------+------------
San Francisco | 46 | 50 | 0.25 | 1994-11-27
San Francisco | 43 | 57 | 0 | 1994-11-29
Hayward | 37 | 54 | | 1994-11-29
San Francisco | 46 | 50 | 0.25 | 1994-11-27
San Francisco | 43 | 57 | 0 | 1994-11-29
Hayward | 37 | 54 | | 1994-11-29
(6 rows)
--- 指定查詢的列 ---
postgres=# SELECT city, temp_lo, temp_hi, prcp, date FROM weather;
--- 四則運算 ---
postgres=# SELECT city, (temp_hi+temp_lo)/2 AS temp_avg, date FROM weather;
--- 條件過濾 ---
postgres=# SELECT * FROM weather
postgres-# WHERE city = 'San Francisco' AND prcp > 0.0;
--- 排序 ---
postgres=# SELECT * FROM weather
postgres-# ORDER BY city;
--- 多欄位排序 ---
postgres=# SELECT * FROM weather
postgres-# ORDER BY city, temp_lo;
--- 去掉重複資料 ---
postgres=# SELECT DISTINCT city
postgres-# FROM weather;
--- 去掉重複資料並排序 ---
postgres=# SELECT DISTINCT city
postgres-# FROM weather
postgres-# ORDER BY city;
用法與普通SQL大體一致
tips:在psql中使用tab可以快速補全!
- 聯合查詢
postgres=# SELECT *
postgres-# FROM weather, cities
postgres-# WHERE city = name;
city | temp_lo | temp_hi | prcp | date | name | location
---------------+---------+---------+------+------------+---------------+-----------
San Francisco | 46 | 50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
San Francisco | 43 | 57 | 0 | 1994-11-29 | San Francisco | (-194,53)
(2 rows)
--- 指定查詢列 ---
postgres=# SELECT city, temp_lo, temp_hi, prcp, date, location
postgres-# FROM weather, cities
postgres-# WHERE city = name;
--- 明確列所屬表 ---
postgres=# SELECT weather.city, weather.temp_lo, weather.temp_hi,
postgres-# weather.prcp, weather.date, cities.location
postgres-# FROM weather, cities
postgres-# WHERE cities.name = weather.city;
--- 內連線 ---
postgres=# SELECT *
postgres-# FROM weather INNER JOIN cities ON (weather.city = cities.name);
--- 左外連線 ---
postgres=# SELECT *
postgres-# FROM weather LEFT OUTER JOIN cities ON (weather.city = cities.name);
--- 使用別名 ---
postgres=# SELECT W1.city, W1.temp_lo AS low, W1.temp_hi AS high,
postgres-# W2.city, W2.temp_lo AS low, W2.temp_hi AS high
postgres-# FROM weather W1, weather W2
postgres-# WHERE W1.temp_lo < W2.temp_lo
postgres-# AND W1.temp_hi > W2.temp_hi;
- 統計函式
postgres=# SELECT max(temp_lo) FROM weather;
max
-----
46
(1 row)
和標準SQL一樣,統計函式無法在where條件中使用
--- 分組函式 ---
postgres=# SELECT city, max(temp_lo)
postgres-# FROM weather
postgres-# GROUP BY city;
city | max
---------------+-----
Hayward | 37
San Francisco | 46
(2 rows)
--- 分組過濾 ---
postgres=# SELECT city, max(temp_lo)
postgres-# FROM weather
postgres-# GROUP BY city
postgres-# HAVING max(temp_lo) < 40;
- 更新資料
postgres=# UPDATE weather
postgres-# SET temp_hi = temp_hi - 2, temp_lo = temp_lo - 2
postgres-# WHERE date > '1994-11-28';
UPDATE 2
- 刪除資料
postgres=# DELETE FROM weather WHERE city = 'Hayward';
DELETE 1
--- 刪除指定表中所有資料 ---
postgres=# delete from weather ;
DELETE 2
3.4、高階功能
- 檢視
postgres=# CREATE VIEW myview AS
postgres-# SELECT city, temp_lo, temp_hi, prcp, date, location
postgres-# FROM weather, cities
postgres-# WHERE city = name;
CREATE VIEW
postgres=# SELECT * FROM myview;
city | temp_lo | temp_hi | prcp | date | location
---------------+---------+---------+------+------------+-----------
San Francisco | 46 | 50 | 0.25 | 1994-11-27 | (-194,53)
San Francisco | 43 | 57 | 0 | 1994-11-29 | (-194,53)
(2 rows)
- 外來鍵
postgres=# drop view myview ;
DROP VIEW
postgres=# drop table weather ;
DROP TABLE
postgres=# drop table cities ;
DROP TABLE
postgres=# CREATE TABLE cities (
postgres(# city varchar(80) primary key,
postgres(# location point
postgres(# );
CREATE TABLE
postgres=#
postgres=# CREATE TABLE weather (
postgres(# city varchar(80) references cities(city),
postgres(# temp_lo int,
postgres(# temp_hi int,
postgres(# prcp real,
postgres(# date date
postgres(# );
CREATE TABLE
postgres=# INSERT INTO weather VALUES ('Berkeley', 45, 53, 0.0, '1994-11-28');
ERROR: insert or update on table "weather" violates foreign key constraint "weather_city_fkey"
DETAIL: Key (city)=(Berkeley) is not present in table "cities".
- 事務
postgres=# select * from weather ;
city | temp_lo | temp_hi | prcp | date
---------------+---------+---------+------+------------
San Francisco | 46 | 50 | 0.25 | 1994-11-27
San Francisco | 43 | 57 | 0 | 1994-11-29
(2 rows)
--- 回滾 ---
postgres=# begin;
BEGIN
postgres=# update weather set prcp = prcp + 1;
UPDATE 2
postgres=# rollback;
ROLLBACK
postgres=# select * from weather ;
city | temp_lo | temp_hi | prcp | date
---------------+---------+---------+------+------------
San Francisco | 46 | 50 | 0.25 | 1994-11-27
San Francisco | 43 | 57 | 0 | 1994-11-29
(2 rows)
--- 提交 ---
postgres=# begin;
BEGIN
postgres=# update weather set prcp = prcp + 1;
UPDATE 2
postgres=# commit;
COMMIT
postgres=# select * from weather;
city | temp_lo | temp_hi | prcp | date
---------------+---------+---------+------+------------
San Francisco | 46 | 50 | 1.25 | 1994-11-27
San Francisco | 43 | 57 | 1 | 1994-11-29
(2 rows)
--- savepoint ---
postgres=# begin;
BEGIN
postgres=# update weather set prcp = prcp - 1;
UPDATE 2
postgres=# savepoint s1;
SAVEPOINT
postgres=# update weather set prcp = prcp + 100;
UPDATE 2
postgres=# rollback to s1;
ROLLBACK
postgres=# update weather set prcp = prcp + 2;
UPDATE 2
postgres=# commit;
COMMIT
postgres=# select * from weather;
city | temp_lo | temp_hi | prcp | date
---------------+---------+---------+------+------------
San Francisco | 46 | 50 | 2.25 | 1994-11-27
San Francisco | 43 | 57 | 2 | 1994-11-29
(2 rows)
- 分析函式(Window Functions)
--- 準備資料 ---
postgres=# CREATE TEMPORARY TABLE empsalary (
postgres(# depname varchar,
postgres(# empno bigint,
postgres(# salary int,
postgres(# enroll_date date
postgres(# );
CREATE TABLE
postgres=# INSERT INTO empsalary VALUES
postgres-# ('develop', 10, 5200, '2007-08-01'),
postgres-# ('sales', 1, 5000, '2006-10-01'),
postgres-# ('personnel', 5, 3500, '2007-12-10'),
postgres-# ('sales', 4, 4800, '2007-08-08'),
postgres-# ('personnel', 2, 3900, '2006-12-23'),
postgres-# ('develop', 7, 4200, '2008-01-01'),
postgres-# ('develop', 9, 4500, '2008-01-01'),
postgres-# ('sales', 3, 4800, '2007-08-01'),
postgres-# ('develop', 8, 6000, '2006-10-01'),
postgres-# ('develop', 11, 5200, '2007-08-15');
INSERT 0 10
--- 計算部門平均值,不分組 ---
postgres=# 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)
--- 組內排序 ---
postgres=# SELECT depname, empno, salary,
postgres-# rank() OVER (PARTITION BY depname ORDER BY salary DESC)
postgres-# FROM empsalary;
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)
--- 求和、不分組 ---
postgres=# SELECT salary, sum(salary) OVER () FROM empsalary;
--- 組內求和 ---
postgres=# SELECT salary, sum(salary) OVER (ORDER BY salary) FROM empsalary;
--- 組內top2 ---
postgres=# SELECT depname, empno, salary, enroll_date
postgres-# FROM
postgres-# (SELECT depname, empno, salary, enroll_date,
postgres(# rank() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos
postgres(# FROM empsalary
postgres(# ) AS ss
postgres-# WHERE pos < 3;
--- 提取並複用公共部分 ---
postgres=# SELECT sum(salary) OVER w, avg(salary) OVER w
postgres-# FROM empsalary
postgres-# WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);
WINDOW … AS … 這種用法有點意識,之前沒用過~
- 繼承(Inheritance)
繼承的概念來自面向物件的資料庫,這一概念開啟了一種有趣的資料庫設計的可能性。
--- 建立表 ---
postgres=# CREATE TABLE cities (
postgres(# name text,
postgres(# population real,
postgres(# altitude int -- (in ft)
postgres(# );
CREATE TABLE
--- 建立表,繼承自另一張表 ---
postgres=# CREATE TABLE capitals (
postgres(# state char(2)
postgres(# ) INHERITS (cities);
CREATE TABLE
--- 插入資料 ---
postgres=# insert into cities values ('San Francisco', 7.24E+5, 63);
INSERT 0 1
postgres=# insert into cities values ('Las Vegas', 2.583E+5, 2174);
INSERT 0 1
postgres=# insert into cities values ('Mariposa', 1200, 1953);
INSERT 0 1
postgres=# insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA');
INSERT 0 1
postgres=# insert into capitals values ('Madison', 1.913E+5, 845, 'WI');
INSERT 0 1
--- 查詢 ---
postgres=# SELECT name, altitude FROM cities WHERE altitude > 500;
name | altitude
-----------+----------
Las Vegas | 2174
Mariposa | 1953
Madison | 845
(3 rows)
postgres=# SELECT name, altitude FROM ONLY cities WHERE altitude > 500;
name | altitude
-----------+----------
Las Vegas | 2174
Mariposa | 1953
(2 rows)
postgres=# select * from capitals;
name | population | altitude | state
------------+------------+----------+-------
Sacramento | 369400 | 30 | CA
Madison | 191300 | 845 | WI
(2 rows)
子表繼承了父表的欄位,父表儲存子表的資料,Interesting~