在Postgres里用Sequence[譯]


原文鏈接:http://www.neilconway.org/docs/sequences/
如果不習(xí)慣簡(jiǎn)書(shū)的格式隔嫡,可以點(diǎn)擊下面鏈接查看:筆記版本

在twitter(也可能是其他地方)上很多人在問(wèn)關(guān)于如何在PostgreSql中使用sequence。為了避免重復(fù)回答這個(gè)問(wèn)題,我覺(jué)得在這里總結(jié)一下在Postgresql中使用sequence的基本步驟還是很有意義的募谎。

什么是Sequence?

Sequence是數(shù)據(jù)庫(kù)中一類特殊的對(duì)象士修,其用于生成唯一數(shù)字標(biāo)識(shí)符智什。一個(gè)典型的應(yīng)用場(chǎng)景就是手動(dòng)生成一系列主鍵持灰。Sequence和Mysql中的AUTO_INCREMENT的概念很像,但又不完全相同怎燥。

在表格中怎么使用Sequence?

Sequence最常見(jiàn)的場(chǎng)景就是生成Serial這個(gè)偽類型棠涮,Serial類型主要有下面幾個(gè)特征:

  • Serial的值就是Sequence生成的。
  • 每次調(diào)用Sequence會(huì)生成一個(gè)新值(Serial類型的)刺覆。
  • 由于Sequence生成的值都是非空的严肪,所以它會(huì)在這一列數(shù)據(jù)上加一個(gè)NOT NULL的標(biāo)志。
  • Sequence是自動(dòng)生成的谦屑,Postgresql假設(shè)Sequence只用于生成Series列(唯一)驳糯,所以如果刪除了這一列,數(shù)據(jù)庫(kù)會(huì)自動(dòng)刪除這個(gè)Sequence氢橙。

例如:下面的命令會(huì)新建一個(gè)表和一個(gè)Sequence對(duì)象酝枢,并且把這個(gè)Sequence對(duì)象關(guān)聯(lián)到這個(gè)表上。

test=# CREATE TABLE users (
test(#     id    SERIAL, -- assign each user a numeric ID
test(#     name  TEXT,
test(#     age   INT4
test(# );
NOTICE:  CREATE TABLE will create implicit sequence
"users_id_seq" for serial column "users.id"
CREATE TABLE

在這個(gè)例子里悍手,自動(dòng)生成的Sequence對(duì)象被自動(dòng)命名為user_id_seq帘睦。如果不想讓數(shù)據(jù)庫(kù)自動(dòng)命名的話袍患,可以調(diào)用下面介紹的pg_get_serial_sequence()這個(gè)函數(shù)。

需要注意的是使用Series列并不會(huì)自動(dòng)創(chuàng)建索引竣付,也不會(huì)把這一列標(biāo)注為主鍵诡延。解決這個(gè)問(wèn)題也很簡(jiǎn)單,只需要手動(dòng)加上PRIMARY KEY這個(gè)標(biāo)示即可古胆。

CREATE TABLE users (
    -- make the "id" column a primary key; this also creates
    -- a UNIQUE constraint and a b+-tree index on the column
    id    SERIAL PRIMARY KEY,
    name  TEXT,
    age   INT4
);

怎么把Sequence的值賦給新插入的數(shù)據(jù)?

如果你用了Serial變量肆良,其默認(rèn)值就是Sequence下一次生成的值。為了讓插入的時(shí)候取到這個(gè)默認(rèn)值逸绎,要么忽略插入對(duì)象中Serial這一列的值惹恃,要么在這個(gè)位置寫(xiě)上DEFAULT這個(gè)關(guān)鍵字。
下面是例子棺牧,二者完全等價(jià):

-- 忽略id
INSERT INTO users (name, age) VALUES ('Mozart', 20);
-- 使用DEFAULT關(guān)鍵字
INSERT INTO users (name, age, id) VALUES ('Mozart', 20, DEFAULT);

怎么獲取Sequence最新生成的數(shù)據(jù)?

你可以使用currval()這個(gè)函數(shù)來(lái)獲取Sequence最新生成的值巫糙,需要注意的是這里獲取的是本次session中的值,這么設(shè)計(jì)是有目的的颊乘。currval()需要一個(gè)參數(shù):Sequence的名字曲秉。可以調(diào)用pg_get_serial_sequence()來(lái)找到和指定列關(guān)聯(lián)的Sequence的名字疲牵。
下面是例子:

SELECT currval(pg_get_serial_sequence('users', 'id'));

需要注意的是,如果該Sequence在本次session中從來(lái)沒(méi)有生成過(guò)新的數(shù)據(jù)榆鼠,則currval()會(huì)報(bào)錯(cuò)纲爸。

會(huì)不會(huì)有競(jìng)爭(zhēng)存在?

假如一個(gè)數(shù)據(jù)庫(kù)客戶端插入了一個(gè)Sequence生成的值,與此同時(shí)另一個(gè)客戶端又插入了一個(gè)值妆够,這樣currval()獲取的值豈不是有可能取到一個(gè)錯(cuò)誤的值识啦?

事實(shí)上并不會(huì)這樣,Sequence通過(guò)優(yōu)雅的設(shè)計(jì)避免了這個(gè)問(wèn)題神妹。currval()這回返回當(dāng)前session的新值颓哮,所以其他用戶的插入并不會(huì)改變currval()返回的值,只有該用戶的插入操作才會(huì)更新該值鸵荠。

插入并獲取插入的數(shù)據(jù)ID豈不是需要兩個(gè)Query語(yǔ)句?

使用上文中所說(shuō)的currval()我們需要執(zhí)行兩次Query語(yǔ)句:一個(gè)用于插入數(shù)據(jù)冕茅,另一個(gè)用于獲取新插入的ID。為了減少和數(shù)據(jù)庫(kù)連接的次數(shù)蛹找,我們可以把插入和獲取ID的兩條指令連起來(lái)一起丟到數(shù)據(jù)庫(kù)執(zhí)行(譯者注:不熟悉PHP姨伤,不過(guò)個(gè)人不推薦這么做,畢竟不是原子操作庸疾,高并發(fā)長(zhǎng)連接的情況下出錯(cuò)的可能性應(yīng)該還是有的)乍楚。下面是例子(PHP,在nodejs和python里可以有類似的寫(xiě)法):

pg_exec("INSERT INTO users (name, age) VALUES ('Bach', 15);
         SELECT currval(pg_get_serial_sequence('users', 'id'));")

這條指令雖有兩條命令届慈,但是只需要和數(shù)據(jù)庫(kù)通信一次即可徒溪,所以基本可以忽略第二條query的額外耗時(shí)忿偷。

另外,對(duì)于Postgresql 8.2以后的版本臊泌,可以直接采用RETURNING語(yǔ)法解決這個(gè)問(wèn)題鲤桥。(推薦這種方式)

INSERT INTO users (name, age) VALUES ('Liszt', 10) RETURNING id;

該語(yǔ)句會(huì)返回新插入的行的ID。

Sequence生成的數(shù)據(jù)的范圍?

Sequence生成的是64位的整形數(shù)據(jù)缺虐,我們?cè)谏厦嬗玫腟erial類型是一個(gè)32位的整形芜壁,如果需要使用64位的Serial則需要使用Serial8類型。

Sequence生成的數(shù)據(jù)會(huì)不連續(xù)嗎?

當(dāng)然會(huì)高氮,Sequence是用于生成唯一的數(shù)據(jù)標(biāo)識(shí)慧妄,并不需要嚴(yán)格連續(xù)。比如:如果兩個(gè)客戶端同時(shí)插入不同的數(shù)據(jù)(會(huì)調(diào)用nextval())的時(shí)候剪芍,每個(gè)客戶端會(huì)得到一個(gè)新的Sequence值塞淹。如果其中一個(gè)在事務(wù)中失敗或者其他原因回滾了,這個(gè)時(shí)候就會(huì)出現(xiàn)Sequence數(shù)據(jù)(即ID)不連續(xù)的現(xiàn)象罪裹,這只是其中一個(gè)例子饱普。
修復(fù)這個(gè)問(wèn)題也很簡(jiǎn)單,具體參考下面的鏈接解決ID不連續(xù)的問(wèn)題

事務(wù)中的Sequence

Sequence操作是基于session的状共,與事務(wù)無(wú)關(guān)套耕。nextval()會(huì)遞增Sequence的值,但是即便是事務(wù)回滾了Sequence也不會(huì)撤銷峡继,而不論是在事務(wù)內(nèi)外冯袍,currval()都會(huì)返回Sequence最新的值。

兩個(gè)表共享一個(gè)Sequence?

實(shí)現(xiàn)這個(gè)功能的最簡(jiǎn)單做法就是手動(dòng)生成一個(gè)Sequence碾牌,然后不要使用Serial類型康愤,而是手動(dòng)把Sequence綁定到對(duì)應(yīng)的列。
下面是具體例子:

CREATE SEQUENCE common_fruit_id_seq;

CREATE TABLE apples (
    id      INT4 DEFAULT nextval('common_fruit_id_seq') NOT NULL,
    price   NUMERIC
);

CREATE TABLE oranges (
    id      INT4 DEFAULT nextval('common_fruit_id_seq') NOT NULL,
    weight  NUMERIC
);

nextval() 會(huì)生成一系列新的值舶吗。需要注意的是這個(gè)手動(dòng)生成的Sequence并不會(huì)隨著表被自動(dòng)刪除征冷,而且你也沒(méi)法調(diào)用 pg_get_serial_sequence()這個(gè)函數(shù)。

更多Sequence的資料

見(jiàn)Postgresql文檔:


說(shuō)明:本文是基于8.2或者更早的版本進(jìn)行講解的誓琼,翻譯本文的時(shí)候Postgresql已經(jīng)更新到了9.6检激,細(xì)節(jié)可能會(huì)有所不同。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末腹侣,一起剝皮案震驚了整個(gè)濱河市呵扛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌筐带,老刑警劉巖今穿,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異伦籍,居然都是意外死亡蓝晒,警方通過(guò)查閱死者的電腦和手機(jī)腮出,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)芝薇,“玉大人胚嘲,你說(shuō)我怎么就攤上這事÷宥” “怎么了馋劈?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)晾嘶。 經(jīng)常有香客問(wèn)我妓雾,道長(zhǎng),這世上最難降的妖魔是什么垒迂? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任械姻,我火速辦了婚禮,結(jié)果婚禮上机断,老公的妹妹穿的比我還像新娘楷拳。我一直安慰自己,他們只是感情好吏奸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布欢揖。 她就那樣靜靜地躺著,像睡著了一般奋蔚。 火紅的嫁衣襯著肌膚如雪她混。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天旺拉,我揣著相機(jī)與錄音,去河邊找鬼棵磷。 笑死蛾狗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的仪媒。 我是一名探鬼主播沉桌,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼算吩!你這毒婦竟也來(lái)了留凭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤偎巢,失蹤者是張志新(化名)和其女友劉穎蔼夜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體压昼,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡求冷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年瘤运,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匠题。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拯坟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出韭山,到底是詐尸還是另有隱情郁季,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布钱磅,位于F島的核電站梦裂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏续搀。R本人自食惡果不足惜塞琼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望禁舷。 院中可真熱鬧彪杉,春花似錦、人聲如沸牵咙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)洁桌。三九已至渴丸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間另凌,已是汗流浹背谱轨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吠谢,地道東北人土童。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像工坊,于是被迫代替她去往敵國(guó)和親献汗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容