目前的作法除了慢之外, 另外還有一個比較嚴重的問題, 那就是當有多人同時想要新增資料時, 很容易便會發生衝突的問題 (標準的 race condition 情境).
比較好的解決方式是把流水號的工作交給資料庫本身的機制處理, 先前已經有人提過 sequence, 它與 SQL Server 的 identity 一樣, 由資料庫本身來確保序號的唯一性, Oracle 官方也建議盡量使用這個方式來產生序號.
但是, 應用程式常常碰到的狀況是, 使用者通常不喜歡直接使用資料庫產生的唯一序號來作為顯示的號碼, 所以應用程式除了必須解決序號的唯一性之外, 大多時候還需要將序號格式化成使用者喜歡的格式. 如果格式很簡單, 應用程式將 sequence/identity 的結果稍作加工, 就可以解決格式的問題. 但是, 如果碰上序號格式需要按照年、年月或年月日重新計算序號的話, 就需要比較複雜的處理邏輯.
另外一個常見的方式是使用程式 (通常是資料庫端的 procedure 或 function) 的方式來產生序號, 這種做法需要用到 select for update 的語法來鎖住序號控制紀錄, 等序號產生且將序號控制紀錄遞增之後, 才把序號控制紀錄的鎖定解除, 在序號控制紀錄未解除鎖定的期間, 任何其他人想要取序號, 都必須等待, 這個過程 Oracle 的文件稱為 serialization. (跟把物件 persistance 的那個 serialization 字相同但是概念不同). 如前面所說的, Oracle 基本上建議不要使用這種方式產生序號, 因為 serialization 的過程會產生鎖定, 且會影響到應用程式的 concurrency 程度.
我們的 team 裡面若是需要使用程式來產生序號, 通常是使用一個作用為序號池的 table 來管理所有的序號紀錄 (id, serialno 欄位之外, 再加上其他的格式欄位, 如:序號的長度, 格式, 前置碼, 後置碼, 日期碼, 是否依照年月日重新計算等等), 透過統一的介面 (procedure/function) 來取號, 如此可將序號的產生方式抽象化, 讓前端程式不需要個別處理序號的問題. 雖然與 Oracle 的建議相違背, 但是為了滿足使用者的需求, 有時候這也是不得不為的一種做法.
若是你的問題可以透過設計變更的方式來處理, 建議你綜合考量 sequency 與 serialization 的優劣, 採取一種方式來處理.
若是無法變更目前設計, 則建議將最後一次的結果找個地方存起來, 下次要取號就直接從儲存的地方取出來遞增就好, 使用未受保護的暫存資訊與你目前count整個 table 的方式基本上是等價的.
好問題, 對於這種 count 我也有一樣的問題, 我用土法煉鋼的方法切 parition 與使用 Oracle text 但都達不到我需要的效能標準. 最後我放棄了, 直接手動建 sequence. (或是 procedure 讓自動建 sequence)
如果能 alter column, 或許加 batch number (如 101010) 能有不錯的效果.
你可以增加一個欄位作auto increase
之後,就不用每次要新加一筆資料就重新算一次!
只要新增之後,取這個欄位值當流水序號再回填就好了
這樣超快的吧
dondonyen提到:
若找的到就由最大+1去編流水號
請教各位大大,這段語法在xxx這個table資料量若約3 百萬筆的資料,用這種方式取流水號..
技術顧問為何有工作!!
就是
有人喜歡用 : 年月日+序號 = 主鍵
oracle count(*) from xxx where key_id like '101010-%'
我們都是建議使用:已用文件編號檔
主鍵編號::TableName + 已用編號 (不分文件類別 + 不分年月日 連續跳號)
文件編號::TableName + 文件類別 + 年月日 + 已用編號
用 function 呼叫時 立刻回寫
這個文件編號不可當主鍵
但是可以當唯一鍵以確保資料庫部會重複
我們是: Skype: ADempiere/Compiere 技術轉移顧問
Oracle 資料庫 PL/SQL 技術顧問