Chapter 4. SQL 語法

Table of Contents
4.1. 詞法結構
4.1.1. 標識符和關鍵字
4.1.2. 常量
4.1.3. 操作符
4.1.4. 特殊字符
4.1.5. 注釋
4.1.6. 詞法優先級
4.2. 值表達式
4.2.1. 字段引用
4.2.2. 位置參數
4.2.3. 下標
4.2.4. 字段選擇
4.2.5. 操作符調用
4.2.6. 函數調用
4.2.7. 聚集表達式
4.2.8. 類型轉換
4.2.9. 標量子查詢
4.2.10. 數組構造器
4.2.11. 表達式計算規則

本章描述 SQL 的語法。 這些內容是理解隨後各章的基礎,那些章裡面將詳細介紹 SQL 命令如何用于定義和修改數據。

我們也建議那些已經很熟悉 SQL 的用戶仔細閱讀本章,因為有一些規則和概念在 SQL 數據庫之間實現得並不一致,或者是有些東西是 PostgreSQL 特有的。

4.1. 詞法結構

SQL 輸入由一系列命令組成。 一條命令是由一系列記號構成, 用一個分號(";")結尾。 輸入流的終止也結束一條命令。那些記號是合法的取決于特定命令的語法。

記號可以是一個關鍵字, 一個標識符,一個 引號包圍的標識符, 一個文本(或常量),或者是特殊的字符符號。 記號通常由空白分隔(空格,tab,換行符),但如果不存在混淆的時候也可以不用 (通常只是一個特殊字符與一些其它記號類型相聯的時候)。

另外,在 SQL 輸入裡可以有注釋。 它們不是記號,它們實際上等效于空白。

比如,下列命令是(語法上)合法的 SQL 輸入:

SELECT * FROM MY_TABLE;
UPDATE MY_TABLE SET A = 5;
INSERT INTO MY_TABLE VALUES (3, 'hi there');

這裡是三條命令的序列,每條一行(盡管並不要求這麼做; 多條命令可以在一行裡,並且命令可以合理地分裂成多個行)。

如果從哪些記號標識命令,哪些是操作數或參數的角度考慮, SQL 語法並不是非常一致。通常頭幾個記號是命令名字, 因此上面的例子我們通常可以說是一個"SELECT", 一個"UPDATE",和一個"INSERT"命令。 不過, UPDATE 命令總是要求一個 SET 在某個位置出現,並且這個變體的 INSERT 還要求有一個 VALUES 才完整。每條命令的準確語法規則都在 Part VI 裡描寫。

4.1.1. 標識符和關鍵字

象上面的例子裡的 SELECTUPDATE, 或 VALUES 這樣的記號都是關鍵字的例子, 也就是那些在 SQL 語言裡有固定含義的單詞。 記號 MY_TABLEA標識符的例子。 根據使用它們的命令的不同,它們標識表,字段,或者其它數據庫對象的名字。 因此,有時候只是簡單地叫它們"名字"。 關鍵字和標識符有著同樣的詞法結構,意思是我們在沒有認識這種語言之前是無法區分一個記號是標識符還是名字。 你可以在 Appendix C 裡找到一個關鍵字的完整列表。

SQL 標識符和關鍵字必須以一個字母開頭 (a-z 以及帶可區別標記的字母以及非拉丁字母 )或下劃線開頭 (_)開頭。標識符和關鍵字裡隨後的字符可以是字母,數字(0-9), 或者下劃線,但 SQL 標準不會定義包含數字或者以下劃線開頭或結尾的關鍵字。

系統使用不超過 NAMEDATALEN-1 個字符作為標識符; 你可以在命令中寫更長的名字,但它們會被截斷。缺省時, NAMEDATALEN 是 64,因此標識符最大長度是 63 如果覺得這個限制有問題,那麼你可以在 src/include/postgres_ext.h 裡修改 NAMEDATALEN 來改變它。

標識符和關鍵字名字都是大小寫無關的。因此

UPDATE MY_TABLE SET A = 5;

也可以等效地寫成

uPDaTE my_TabLE SeT a = 5;

一種好習慣是把關鍵字寫成大寫,而名字等用小寫。

UPDATE my_table SET a = 5;

還有第二種標識符:分隔標識符引號包圍的標識符。 它是通過在雙引號(" ) 裡包圍任意字符序列形成的。 分隔標識符總是一個標識符,而不是關鍵字。因此,你可以用 "SELECT" 表示一個字段名字或者名字叫 "SELECT" 的表,而一個沒有引號的 SELECT 將被當做一條命令的一部分,因此如果把它當做一個表的名字或者字段名字用的話就會產生一個分析錯誤。 上面的例子可以用引起的標識符這麼寫:

UPDATE "my_table" SET "a" = 5;

引號包圍的標識符可以包含除引號本身以外的任何其它字符。 要包含一個雙引號,我們可以寫兩個雙引號。 這樣我們就可以構造那些原本是不允許的表或者字段名字, 比如那些包含空白或與號的名字。但長度限制依舊。

把一個標識符引號包圍的起來同時也令它大小寫相關,而沒有引號包圍的來的名字總是轉成小寫。 比如,我們認為標識符 FOOfoo"foo" 是一樣的 PostgreSQL名字, 但 "Foo""FOO" 與上面三個以及它們之間都是不同的。 (PostgreSQL 裡對未加引號的名子總是轉換成小寫, 這和 SQL 是不兼容的,SQL 裡要求未引號包圍的來的名字總是轉成大寫。 因此 foo 等于 "FOO"。 如果你想寫可移植的程序,那麼我們建議你要麼就總是引號包圍的某個名字,要麼就堅決不引。)

4.1.2. 常量

PostgreSQL 裡有三種隱含類型的常量: 字符串,位串,和數值。 常量也可以聲明為明確的類型,這樣就可以使用更準確的表現形式以及可以通過系統更有效地處理。隱含常量在下面描述; 明確常量稍後描述。

4.1.2.1. 字符串常量

SQL 裡的字符串常量是一個由單引號("'")圈定範圍的任意字符的序列,比如, 'This is a string'。 SQL 允許你在字符串裡嵌入單引號,方法是敲入兩個連續的單引號(比如,'Dianne''s horse')。 在 PostgreSQL 裡,單引號還可以用一個反斜扛("\")來逃逸,比如, 'Dianne\'s horse'

還可以使用 C-風格的反斜扛逃逸: \b 是一個退格,\f 是一個進紙,\n 是一個換行符, \r 是一個回車,\t 是一個水平制表符,而\xxx, 這裡 xxx 是一個八進制數,是對應 ASCII 碼的字符。任何其它跟在反斜扛後面的字符都當做文本看待。 因此,要在字符串常量裡包含反斜扛,你可以敲兩個反斜扛。

編碼為零的字符不能出現在字符串常量中。

兩個只是通過至少有一個換行符的空白分隔的字符串常量會被連接在一起,並當做它們是寫成一個常量處理。 比如:

SELECT 'foo'
'bar';

等效于

SELECT 'foobar';

SELECT 'foo'      'bar';

是非法的語法,(這個略微有些怪異的行為是 SQL 聲明的; PostgreSQL 遵循標準。)

4.1.2.2. 位串常量

位串常量看起來很象在開引號前面有一個 B (大寫或小寫)的字符串(它們之間沒有空白), 比如 B'1001'。位串常量裡可以用的字符只有 01

另外,位串常量可以用十六進制表示法聲明,方法是使用前綴的 X (大寫或者小寫),比如,X'1FF'。 這種表示法等效于一個每個十六進制位四個二進制位的位串常量。

兩種形式的位串常量都可以象普通字串常量那樣跨行連續。

4.1.2.3. 數值常量

數值常量接受下列通用的形式:

digits
digits.[digits][e[+-]digits]
[digits].digits[e[+-]digits]
digitse[+-]digits

這裡的 digits 是一個或多個十進制位(0 到 9)。 如果有小數點,那麼至少有一位在小數點前面或後面。如果出現了指數分隔符(e),那麼至少有一個位跟在它後面。 在常量裡不能有空格或者其他字符嵌入在內。 請注意任何前導地正號或者負號實際上都不認為是常量的一部分; 它是施加于常量的一個操作符。

這裡是一些合法的數值常量的例子:

42
3.5
4.
.001
5e2
1.925e-3

如果一個數值常量既不包含小數點,也不包含指數操作符, 那麼如果它的數值可以放在integer類型中(32位), 則認為它是integer類型;如果它的數值可以放在 bigint中(64位),則認為它是 bigint; 否則認為它是 numeric類型。包含小數點和/或指數操作符的常量總是被認為是numeric類型。

給一個數值常量賦予初始數據類型只是類型解析算法的開端。 在大多數情況下該常量會根據環境被自動強制轉換成最合適的類型。 必要時,你可以通過強制類型轉換把一個數值解析成特定的數據類型。 比如,你可以強制要求把一個數值當作類型realfloat4)來看,方法時這麼寫:

REAL '1.23'  -- 字串風格
'1.23'::REAL -- PostgreSQL (歷史原因)風格
     

4.1.2.4. 其它類型的常量

任意類似的常量可以用下列表示法中的任何一種來輸入:

type 'string'
'string'::type
CAST ( 'string' AS type )

在字符串的文本將傳遞給那種叫 type 的類型的輸入轉換過程。結果是這種類型的一個常量。 如果不存在該常量所屬類型的歧義,那麼明確的類型映射可以省略(比如,當你把它當做一個沒有重載的函數的參數傳遞時), 這種情況下它會自動轉換。

我們還可以用函數樣的語法來聲明類型轉換:

typename ( 'string' )

不過並非所有類型名可以這樣使用;參閱 Section 4.2.8 獲取細節。

::CAST(),和函數調用語法也可以用于聲明任意表達式的運行時類型轉換, 如 Section 4.2.8 中討論的那樣。 但是 type 'string' 的形式只能用于聲明一個文本常量的類型。 type 'string' 的另外一個限制是它不能用于數組類型;要用 :: 或者 CAST() 聲明一個數組常量的類型。

4.1.3. 操作符

一個操作符是最多 NAMEDATALEN-1 (缺省 63 個字符)個下列字符的序列:

+ - * / < > = ~ ! @ # % ^ & | ` ?

不過,對操作符名字有幾個限制:

  • --/* 不能出現在操作符名字中的任何地方,因為它們會被當做注釋開始對待。

  • 多字符操作符不能以 +- 結束, 除非其名字至少還包含下列操作符之一:

    ~ ! @ # % ^ & | ` ?

    比如,@- 是允許的操作符名字, 但 *- 不是。這個限制允許 PostgreSQL 在不要求記號之間有空白的情況下分析 SQL 兼容的查詢。

當你使用非 SQL 標準的操作符名字的時候,你通常需要用空白分隔相鄰的操作符以避免歧義。 比如,如果你定義了一個叫 "@" 的左單目操作符,那麼你就不能寫 X*@Y;而是要寫成 X* @Y 以確保 PostgreSQL 把它讀成兩個操作符,而不是一個。

4.1.4. 特殊字符

有些非字母數字字符有一些特殊含義,因此不能用做操作符。 它們的用法的細節可以在相應的描述語法元素的地方找到。 本節只是描述它們的存在和概括一下這些字符的目的。

  • 美元符號($)後面跟著數字用于在一個函數體定義或者準備好的語句中 表示參數的位置。在其他環境裡美元符號可能是一個標識符名字的一部分。

  • 圓括弧(())用于分組和強制優先級的時候含義與平常一樣。 有些場合裡圓括弧是作為一個特定 SQL 命令的固定語法的一部分要求的。

  • 方括弧([])用于選取數組元素。 參閱 Section 8.10 獲取更多信息。

  • 逗號(,在一些語法構造裡用于分隔一個列表的元素。

  • 分號(;)結束一條 SQL 命令。 它不能出現在一條命令裡的任何地方,除非引號包圍的來當做字符串常量或者標識符用。

  • 冒號 (:)用于從數組中選取"片段"。(參閱 Section 8.10。)在一些 SQL 方言裡(比如嵌入 SQL ), 冒號用于前綴變量名。

  • 星號 (* 在和 SELECT 命令或 COUNT 聚集函數一起使用時有特殊含義。

  • 句點 (.用在數字常量裡,並用于分隔模式,表和字段名字。

4.1.5. 注釋

注釋是任意以雙劃線開頭並延伸到行尾的任意字符序列,比如:

-- 這是標準的 SQL92 注釋

另外,還可以使用 C-風格的塊注釋:

/* 多行注釋
 * 可以嵌套︰/* 嵌套的塊注釋 */
 */

這裡注釋以 /* 開頭並擴展到對應的 */。這些塊注釋可以嵌套,就象 SQL99 裡說的那樣, 但和 C 不一樣,因此我們可以注釋掉一大塊已經包含塊注釋的代碼。

注釋在進一步的語法分析之前被從輸入流刪除並有效地用空白代替。

4.1.6. 詞法優先級

Table 4-1 顯示了 PostgreSQL 裡面的操作符的優先級和關聯性。 大多數操作符都有相同的優先級並且都是左關聯的。 這種情況可能會有不那麼直觀的行為;比如,布爾操作符 <> 和布爾操作符 <=>= 之間有著不同的優先級。同樣,當你把雙目和單目操作符組合使用的時候, 有時候也需要加圓括弧。比如

SELECT 5 ! - 6;

會被分析成

SELECT 5 ! (- 6);

因為分析器不知道 ! 定義成了前綴操作符, 而不是中綴操作符。(-- 知道的時候只能是太晚了 --) 要在本例中獲得你需要的特性,你要寫成

SELECT (5 !) - 6;

這是我們為擴展性付出的代價。

Table 4-1. 操作符優先級(遞減)

操作符/元素關聯性描述
.表/字段名分隔符
::PostgreSQL-特有的類型轉換操作符
[ ]數組元素選則
-單目負號
^冪操作
* / %乘,除,模
+ -加,減
IS IS TRUE, IS FALSE, IS UNKNOWN, IS NULL
ISNULL 測試是否為空值
NOTNULL 測試是否為非空值
(任何其它的)所有其它的本地和用戶定義操作符
IN 集合成員
BETWEEN 包含
OVERLAPS 時間間隔重疊
LIKE ILIKE SIMILAR 字符串模式匹配
< > 小于,大于
=等于,賦值
NOT邏輯反
AND邏輯與
OR邏輯或

請注意操作符優先級也適用于和上面提到的同名的內置操作符用戶定義操作符。 比如,如果你為一些客戶數據類型定義一個 "+" 操作符, 那麼它和內置的 "+" 操作符有同樣的優先級,不管你幹了什麼。

如果在 OPERATOR 語法裡使用了模式修飾的操作符名, 比如

SELECT 3 OPERATOR(pg_catalog.+) 4;

那麼 OPERATOR 構造就會有 Table 4-1 表裡面為"任何其它"操作符顯示的缺省優先級。 不管什麼特定的操作符出現在 OPERATOR()裡,都是這樣。