Chapter 1. SQL 語法

Table of Contents
1.1. 詞法結構
1.1.1. 標識符和關鍵字
1.1.2. 常數
1.1.3. 運算子
1.1.4. 特殊字元
1.1.5. 註釋
1.1.6. 詞法優先級
1.2. 值表達式
1.2.1. 欄位參照
1.2.2. 位置參數
1.2.3. 運算子調用
1.2.4. 函數調用
1.2.5. 統計表達式
1.2.6. 類型轉換
1.2.7. 標量幾查詢
1.2.8. 表達式計算

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

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

1.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 才完整.每條命令的準確語法規則都在 PostgreSQL 7.3 參考手冊裡描寫.

1.1.1. 標識符和關鍵字

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

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

PostgreSQL 系統使用不超過 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" 與上面三個以及它們之間都是不同的. [1]

1.1.2. 常數

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

1.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 遵循標準。)

1.1.2.2. 位串常數

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

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

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

1.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 (歷史原因)風格
     

1.1.2.4. 其它類型的常數

任意類似的常數可以用下清單示法中的 任何一種來輸入:

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

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

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

typename ( 'string' )

不過並非所有類型名可以這樣使用﹔參閱 Section 1.2.6 獲取細節.

::CAST(),和 函數調用語法也可以用於宣告任意表達式的運行時類型轉換, 如 Section 1.2.6 中討論的那樣. 但是 type 'string' 的形式只能用於宣告一個純文字常數的類型. type 'string' 的另外一個限制是它不能用於陣列類型﹔要用 :: 或者 CAST() 宣告一個陣列常數的類型.

1.1.2.5. 陣列常數

陣列常數的通用格式如下:

'{ val1 delim val2 delim ... }'

這裡 delim 是該類型的分隔符字元, 和在它的 pg_type 記錄裡記錄的一樣. (對於所有內建類型,它是逗號字元 ",".) 每個 val 要麼是該陣列元素類型的常數, 要麼是一個幾陣列.下面是一個陣列常數的範例

'{{1,2,3},{4,5,6},{7,8,9}}'

這個常數是兩維,3 乘 3陣列,組成三個整數的幾陣列.

獨立的陣列元素可以放在雙引號(" ) 中間以避免 因空白帶來的歧義. 如果沒有引號,那麼陣列值分析器就會忽略開頭的空白.

(陣列常數實際上只是我們前面討論的通用類型常數的一種特例. 該常數開始是當做字串對待的然後傳遞給陣列輸入轉換過程. 可能需要明確地類型宣告.)

1.1.3. 運算子

一個運算子是最多 NAMEDATALEN-1 (預設 63 個字元)個下列字元的序列:

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

不過,對運算子名字有幾個限制:

  • $ (美元)不能是單字元運算子,但它可以是一個多字元運算子 名字的一部分.

  • --/* 不能出現在運算子 名字中的任何地方,因為它們會被當做註釋開始對待.

  • 多字元運算子不能以 +- 結束, 除非其名字至少還包含下列運算子之一:

    ~ ! @ # % ^ & | ` ? $

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

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

1.1.4. 特殊字元

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

  • 美元符號($)後面跟著數位用於在一個函數體中 表示參數的位置.在其他環境裡美元符號可能是一個運算子名字的一部分.

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

  • 方括弧([])用於選取陣列元素. 參閱 Section 5.12 獲取更多資訊.

  • 逗號(,在一些語法構造裡用於分隔 一個清單的元素.

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

  • 冒號 (:)用於從陣列中選取 "片段".(參閱 Section 5.12.)在一些 SQL 方言裡(比如內嵌 SQL ), 冒號用於前綴變數名.

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

  • 句點 (.用在浮點數常數裡, 並用於分隔樣式,表和欄位名字.

1.1.5. 註釋

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

-- 這是標準的 SQL92 註釋

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

/* 多行註釋
 * 可以巢狀︰/* 巢狀的塊註釋 */
 */

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

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

1.1.6. 詞法優先級

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

SELECT 5 ! - 6;

will be parsed as

SELECT 5 ! (- 6);

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

SELECT (5 !) - 6;

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

Table 1-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 1-1 表裡面為 "任何其它"運算子顯示的預設優先級。不管什麼特定的運算子出現在 OPERATOR()裡,都是這樣。

Notes

[1]

PostgreSQL 裡對未加引號的名幾總是 轉換成小寫, 這和 SQL 是不兼容的,SQL 裡要求未引起來的名字總是轉成大寫. 因此 foo 等於 "FOO". 如果你想寫可移植的程式,那麼我們建議你要麼就總是引起某個 名字,要麼就堅決不引.