PostgreSQl
SQL語言
概念
pgsql是關係型數據庫管理系統。關係本質上是表的數學表達。
表是行的集合。同一個表中每行都有相同的列。每個列都是一個特定的數據類型。每列都有固定的順序,但是無序的。
表被分組存放到數據庫,由單個pgsql服務器實例管理的數據庫集合構成一個數據庫集羣。
創建表
先來看一個例子:
CREATE TABLE weather (
city varchar(80),
temp_lo int, -- low temperature
temp_hi int, -- high temperature
prcp real, -- precipitation
date date
);
操作如下圖:
劃掉的部分不是怕泄密,而是那些地方不重要。
啓動pgsql後輸入上述命令,就可以創建一張表。psql能夠識別分號纔是命令的結束,當輸入爲結束之前,命令提示符也是不一樣的。
SQL命令中可以自由使用空格、縮進和換行,上面的命令既可以在一行輸入,也可以在多行輸入。正如你所見,換行以後並沒有結束命令的輸入,遇到分號;
命令才結束。
--
表示單行註釋。
SQL不區分大小寫,但是雙引號內用以保存的字符串是區分大小寫的。
varchar(80)
表示長度爲80個字符的可以存儲任意字符的字符串。int
表示整數類型。real
表示單精度浮點數。date
表示日期。正如你所見,變量名和類型都叫date
,這是允許的,因爲類型並不是SQL關鍵字。
SQL數據類型
int
smallint
real
double precision
char(N)
varchar(N)
date
time
timestamp
interval
幾何類型
自定義類型
就像前面說的,類型名稱不是SQL關鍵字,除了SQL標準中的特殊類型。比如下面的point
類型:
CREATE TABLE cities (
name varchar(80),
location point
);
最後,要刪除一張表可以使用如下命令:
DROP TABLE tablename;
現在我們爲mydb數據庫創建了兩張表,那麼去哪裏能看到這兩張表呢?
還記得pgsql默認安裝的pgAdmin嗎?它是圖形化應用程序,從這裏可以清楚的看到。如果你安裝的也是最新的pgAdmin 4的話,那麼一定會想哭的,WTF?我的表呢?表在哪裏?
不得不說相比於之前的版本,很想罵人。甚至於在很長一段時間內,面對pgAdmin 4筆者都是一臉懵逼的狀態。好在天無絕人之路,小編幸得高人指點,終於找到它把數據庫表藏到了模式
>>public
>>表
下面。
高人名叫殤莫憶(一聽名字就知道是高人),住在https://blog.csdn.net/qq_28289405 。
向表中插入一行數據
對號入座。
類似於C語言對結構體的初始化。我們說過,列在行中的順序是固定的,我們可以按照列的順序將所有值依次列出來插入一行。如下:
INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27');
INSERT
是插入命名,INTO
後面是數據庫名字,VALUES
後面是要插入的數據。所偶遇數據用括號括起來,數據之間用逗號隔開,非數值的數據全部要用單引號引起來。即使是
point
這樣的SQL標準類型,也要用寫在引號中。如下:INSERT INTO cities VALUES ('San Francisco', '(-194.0, 53.0)');
point
類型需要座標對作爲輸入,即x
座標和y
座標。韓信點兵。
第1中方法中,要求數據的順序和列的順序不差分毫。這裏的方式也可以按列的名字插入數據,類似於golang中按名稱初始化結構體。
INSERT INTO weather (city, temp_lo, temp_hi, prcp, date) VALUES ('San Francisco', 43, 57, 0.0, '1994-11-29');
如你所見,現在數據庫名字後面跟了一個括號,裏面列出了需要插入數據的列的名字,
VALUES
後面是與前邊對應的數據值。這樣你不必記得列在行中的順序,只需要知道你需要往哪些列中插入數據即可,現在數據的順序完全有weather
後面的字段順序決定。這也意味着你可以省略某些列的初始化,例如不初始化降雨量(prec):
INSERT INTO weather (date, city, temp_hi, temp_lo) VALUES ('1994-11-29', 'Hayward', 54, 37);
推薦使用這種方式。
一氣呵成。
想象一下如果你有一萬條數據要錄入,要是靠手一條一條命令敲進去。。。喂,120嗎。。。
所以SQL提供了
COPY
命令從文件中加載大量數據。因爲對COPY
命令進行了優化,所以它要比INSERT
更快。然而陰陽相生,在更快的同時也失去了部分的靈活性。COPY weather FROM '/home/user/weather.txt';
而且,文件名必須在運行後端進程的機器上可用,而不是客戶端,因爲後端進程直接讀取文件。
查詢
使用SELECT
語句。SELECT
包含以下幾個部分:
查詢列表。緊跟在
SELECT
後面,用逗號隔開的列名,想查詢哪些列的數據就把列的名字寫在後面,*
表示所有列。表。跟在
FROM
後面,想要查詢的表的名字,可以有多個表。查詢條件(可選)。查詢的約束條件。
普通查詢
讓我們來看第一個例子,從weather表中查詢所有數據。
SELECT * FROM weather;
SELECT city, temp_lo, temp_hi, prcp, date 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
(3 rows)
查詢表達式
查詢列表中不僅可以寫想要查詢的列的名字。還可以是表達式。如下:
SELECT city, (temp_hi+temp_lo)/2 AS temp_avg, date FROM weather;
輸入上面的命令,你將得到如下輸出:
city | temp_avg | date
---------------+----------+------------
San Francisco | 48 | 1994-11-27
San Francisco | 50 | 1994-11-29
Hayward | 45 | 1994-11-29
(3 rows)
AS
字句的作用是給字段重命名,它不是必須的。如果沒有AS
字句,對應字段會用默認命名?column?
,結果如下:
city | ?column? | date
---------------+----------+------------
San Francisco | 48 | 1994-11-27
San Francisco | 50 | 1994-11-29
Hayward | 45 | 1994-11-29
(3 rows)
WHERE
條件查詢
WHERE
後面必須跟bool表達式。查詢只返回表達式爲true
的數據。多個bool表達式之間用AND
、OR
和NOT
連接。
例如,查詢”San Francisco”降雨量大於0的數據:
SELECT * FROM weather WHERE city = 'San Francisco' AND prcp > 0.0;
ORDER BY
排序查詢
ORDER BY
後跟一個或多個列名稱,可以使查詢結果按指定的列排序。例如:
按城市名排序:
SELECT * FROM weather ORDER BY city;
city | temp_lo | temp_hi | prcp | date
---------------+---------+---------+------+------------
Hayward | 37 | 54 | | 1994-11-29
San Francisco | 43 | 57 | 0 | 1994-11-29
San Francisco | 46 | 50 | 0.25 | 1994-11-27
按城市名和最低溫排序:
SELECT * FROM weather ORDER BY city, temp_lo;
自所以需要按多個列來排序,因爲如果只按一個列來排序時,難保該列中會有同樣的值,那麼他們之間的順序依然是任意的。
DISTINCT
去除重複的數據
在查詢列表中的列名字前加上DISTINCT
可以查詢該列的數據並去掉重複的數據。
SELECT DISTINCT city FROM weather;
city
---------------
Hayward
San Francisco
(2 rows)
當然,在去重的同時,也可以進行排序。去重排序兩不誤。
SELECT DISTINCT city FROM weather ORDER BY city;
爲什麼會有去重的同時排序這種操作呢?
原來在早期的pgsql中,DISTINCT
命令會自動對結果進行排序,而不需要手動ORDER BY
。但這並不是SQL的標準,於是後來pgsql就去掉了DISTINCT
命令的排序功能。所以現在纔有了去重並排序這樣的操作。
連表查詢
同時訪問一個或多個表的多個行。
例如,在weather和cities表中查詢字段city和字段name相等的數據:
SELECT * FROM weather, cities 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)
問題1:city字段和name字段內容相同,重複輸出。
可以指定查詢列表,選擇輸出哪些列來去掉重複。
SELECT city, temp_lo, temp_hi, prcp, date, location
FROM weather, cities
WHERE city = name;
思考:上面的查詢語句去掉WHERE
字句後,結果是什麼?(排列組合)
問題2:語法分析器能夠自動識別查詢列表中的列屬於哪個表,但如果兩個表中存在同名字段,需要加上表名顯示指定該字段屬於哪個表。
SELECT weather.city, weather.temp_lo, weather.temp_hi,
weather.prcp, weather.date, cities.location
FROM weather, cities
WHERE cities.name = weather.city;
也許你正奇怪,爲什麼連表查詢沒有看到JOIN
呢?其實到目前爲止,上面講的查詢都是在做INNER JOIN
查詢。本節的第一條查詢命令等同於下面的查詢語句:
SELECT * FROM weather INNER JOIN cities ON (weather.city = cities.name);
ON
字句表示的是查詢條件。
五種JOIN
1. JOIN
SELECT * FROM weather JOIN cities ON (weather.city = cities.name);
單獨使用JOIN
等同於INNER JOIN
。
2. INNER JOIN
SELECT * FROM weather INNER JOIN cities ON (weather.city = cities.name);
只輸出符合查詢條件的行。
3. LEFT OUTER JOIN
SELECT * FROM weather LEFT OUTER JOIN cities ON (weather.city = cities.name);
列出LEFT OUTER JOIN
關鍵字左邊的表中的所有行,以及關鍵字右邊表中符合查詢條件的行。如果沒有匹配的行則輸出空。
city | temp_lo | temp_hi | prcp | date | name | location
---------------+---------+---------+------+------------+---------------+-----------
Hayward | 37 | 54 | | 1994-11-29 | |
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)
(3 rows)
4. RIGHT OUTER JOIN
SELECT * FROM weather RIGHT OUTER JOIN cities ON (weather.city = cities.name);
與LEFT OUTER JOIN
相反,RIGHT OUTER JOIN
會列出關鍵字右邊表的所有行,以及左邊表中符合查詢條件的行。沒有匹配的行輸出空。
爲了更清晰的分析命令的含義,我們先向cities表中再插入一條數據:
INSERT INTO cities VALUES ('London', '(205, 88)');
然後再執行上面的查詢語句。
city | temp_lo | temp_hi | prcp | date | name | location
---------------+---------+---------+------+------------+---------------+-----------
San Francisco | 10 | 20 | 3 | 2000-03-01 | San Francisco | (-194,53)
San Francisco | 46 | 50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
| | | | | London | (205,88)
因爲cities表中只有一條記錄,而且匹配成功,所以結果看起來跟INNER JOIN
的結果一樣。
5. FULL OUTER JOIN
SELECT * FROM weather FULL OUTER JOIN cities ON (weather.city = cities.name);
兩個表中的所有記錄都會列出,相匹配的記錄輸出到同一行,沒有匹配的那邊輸出空。
city | temp_lo | temp_hi | prcp | date | name | location
---------------+---------+---------+------+------------+---------------+-----------
San Francisco | 46 | 50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
Hayward | 37 | 54 | | 1994-11-30 | |
San Francisco | 10 | 20 | 3 | 2000-03-01 | San Francisco | (-194,53)
| | | | | London | (205,88)
(4 行記錄)
你可以清楚的看到查詢結果中哪些地方是空着的。
關於JOIN
就只有這麼5種組合方式,其他的組合在語法上就是錯誤的。切記!
一個表除了可以和其他表JOIN
,還可以自己跟自己玩,稱爲self join。
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, weather W2
WHERE W1.temp_lo < W2.temp_lo AND W1.temp_hi > W2.temp_hi;
先看結果再分析。
city | low | high | city | low | high
---------------+-----+------+---------------+-----+------
San Francisco | 43 | 57 | San Francisco | 46 | 50
Hayward | 37 | 54 | San Francisco | 46 | 50
(2 rows)
這裏用到了很多重命名技術。查詢列表中的AS
字句自不必多說。其中w1
和w2
都是weather表,類似於C語言中聲明變量的做法。也就是說查詢的數據來自同一張表——weather表。
w1
和w2
就是weather表中的兩條記錄,不要把它們看做是表,只是它們來自同一張表而已。滿足w1
的氣溫範圍包含w2
的氣溫範圍時,將這兩條記錄輸出。
這種重命名技術也可以用在其他查詢語句中,例如:
SELECT * FROM weather w, cities c WHERE w.city = c.name;
這相當於是給表取了一個別名。
聚合(統計,拿不準哪個詞合適)函數
count
sum
avg
max
min
以上這些函數和C語言中的函數的用方法別無二致。都是函數名後跟括號,括號中是參數,這裏參數是列名稱。例如,查詢weather表中最低溫最大的數據:
SELECT max(temp_lo) FROM weather;
max
-----
46
(1 row)
其他函數的使用同max
一樣,就不一一演示了。大家可以自己試試,練習一下。
現在問題又來了。如果我想輸出最低溫最大的城市的名字,該如何是好呢?
這個時候是比較麻煩的,需要使用嵌套查詢。如下:
SELECT city FROM weather WHERE temp_lo = (SELECT max(temp_lo) FROM weather);
city
---------------
San Francisco
(1 row)
每個SELECT
是一個獨立的計算單元,它們之間互不影響。所以我們可以將一個SELECT
的輸出作爲另一個SELECT
的輸入。
也許你覺得這樣的嵌套太麻煩了。那麼可不可以在WHERE
字句直接寫WHERE temp_lo = max(temp_lo)
呢?這樣是萬萬不可滴,切記切記!
原因是:聚合函數的輸入並不是表中的所有記錄,而是由WHERE
字句選出來的那些記錄。WHERE
都還沒執行,max
又拿什麼去計算呢?所以聚合函數不能用在WHERE
字句中!
聚合函數與GROUP BY
讓咱們先來個例子開開胃。
SELECT city, max(temp_lo) FROM weather GROUP BY city;
city | max
---------------+-----
Hayward | 37
San Francisco | 46
(2 rows)
GROUP BY
的意思就是分組。其後跟着的列名,所有該列的值相同的行都會被分爲一組。所有查詢操作包括聚合函數都會在每個組上執行,最後返回查詢結果 。
GROUP BY
和WHERE
是相似的。只不過WHERE
是將所有滿足bool表達式的行作爲一個組返回,然後在這個組上執行其他查詢操作。而GROUP BY
將表中的所有行按某列分成若干組,然後其他查詢操作在每個組上都會執行一遍。
聚合函數與HAVING
通過在HAVING
字句中使用聚合函數,可以進一步限制查詢出來的結果。例如在上面的查詢結果中選出最低溫的最大值小於40的記錄。
SELECT city, max(temp_lo)
FROM weather
GROUP BY city
HAVING max(temp_lo) < 40;
city | max
---------+-----
Hayward | 37
(1 row)
總結
最後WHERE
字句還支持LIKE
模式匹配:
SELECT city, max(temp_lo)
FROM weather
WHERE city LIKE 'S%'
GROUP BY city
HAVING max(temp_lo) < 40;
那麼問題是爲什麼將模式匹配放在WHERE
子中,而不是放到HAVING
字句中?
要回答這個問題首先要弄清楚聚合函數、WHERE
字句、GROUP
字句以及HAVING
字句的執行順序。
它們的執行順序依次是:
WHERE
字句GROUP
字句- 聚合函數
HAVING
字句
WHERE
字句最先執行,選出符合條件的記錄。其次是GROUP
字句,這也意味着GROUP
的分組對象可以是SELECT
選出來的部分記錄。
聚合函數在GROUP BY
函數之後執行,這也是爲什麼在WHERE
字句和GROUP BY
字句中不能使用聚合函數的原因。
HAVING
字句最後執行,所以其中可以用聚合函數進一步過濾查詢結果。HAVING
基本就是和聚合函數一起使用,鮮有HAVING
字句中不帶聚合函數的。
最後我們來回答上面的問題。將模式匹配放在WHERE
子中,而不是放到HAVING
字句中顯然更高效。因爲這樣只需要對WHERE
選出來的記錄執行GROUP
操作和HAVING
過濾,而不是對所有的記錄執行。
更新
更新數據用UPDATE
命令。更新只能修改已有的記錄。
UPDATE weather
SET temp_hi = temp_hi - 2, temp_lo = temp_lo - 2
WHERE date > '1994-11-28';
UPDATE
後跟要修改的表名。然後是SET
以及後面的表達式,表達式之間用逗號分隔,負責修改數據。最後是可選的查詢條件,意味着可以限定修改的範圍,也可以全部修改。
不信你看↓↓
SELECT * FROM weather;
city | temp_lo | temp_hi | prcp | date
---------------+---------+---------+------+------------
San Francisco | 46 | 50 | 0.25 | 1994-11-27
San Francisco | 41 | 55 | 0 | 1994-11-29
Hayward | 35 | 52 | | 1994-11-29
(3 rows)
這一集就這麼短!
刪除
使用DELETE
命令刪除一行,也就整條記錄。
DELETE FROM weather WHERE city = 'Hayward';
city = 'Hayward'
記錄已被刪除,不信隨便查。
SELECT * FROM weather;
city | temp_lo | temp_hi | prcp | date
---------------+---------+---------+------+------------
San Francisco | 46 | 50 | 0.25 | 1994-11-27
San Francisco | 41 | 55 | 0 | 1994-11-29
(2 rows)
最後,刪除命令也可以不帶查詢條件。
DELETE FROM tablename;
這樣的命令將偷偷刪除表中的所有記錄,不會有任何提示。所以如果不是腦子抽風儘量碰它。