PostgreSQl 語言篇

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?我的表呢?表在哪裏?

5b03ddc14c301

不得不說相比於之前的版本,很想罵人。甚至於在很長一段時間內,面對pgAdmin 4筆者都是一臉懵逼的狀態。好在天無絕人之路,小編幸得高人指點,終於找到它把數據庫表藏到了模式>>public>>下面。

這裏寫圖片描述

高人名叫殤莫憶(一聽名字就知道是高人),住在https://blog.csdn.net/qq_28289405


向表中插入一行數據

  1. 對號入座。

    類似於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座標。

  2. 韓信點兵。

    第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);

    推薦使用這種方式。

  3. 一氣呵成。

    想象一下如果你有一萬條數據要錄入,要是靠手一條一條命令敲進去。。。喂,120嗎。。。

    5b03eca7b9d34

    所以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表達式之間用ANDORNOT連接。

例如,查詢”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字句自不必多說。其中w1w2都是weather表,類似於C語言中聲明變量的做法。也就是說查詢的數據來自同一張表——weather表。

w1w2就是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一樣,就不一一演示了。大家可以自己試試,練習一下。

現在問題又來了。如果我想輸出最低溫最大的城市的名字,該如何是好呢?

5b051d1279281

這個時候是比較麻煩的,需要使用嵌套查詢。如下:

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)呢?這樣是萬萬不可滴,切記切記!

5b05226641797

原因是:聚合函數的輸入並不是表中的所有記錄,而是由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 BYWHERE是相似的。只不過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字句的執行順序。

它們的執行順序依次是:

  1. WHERE字句
  2. GROUP字句
  3. 聚合函數
  4. 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)

這一集就這麼短!

5b05327134a22


刪除

使用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;

這樣的命令將偷偷刪除表中的所有記錄,不會有任何提示。所以如果不是腦子抽風儘量碰它。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章