資料庫效能的應用程式設計

執行後,請勿變更 SQLStatement 物件的 text 屬性。請讓每個 SQL 陳述式使用一個 SQLStatement 實體,並使用陳述式參數來提供不同的值。

在執行任何 SQL 陳述式之前,執行階段會進行準備 (編譯) 並決定內部執行的步驟,以便實際執行陳述式的作業。當您在先前未曾執行的 SQLStatement 實體上呼叫 SQLStatement.execute() 時,該陳述式會自動進行準備,然後再執行。在後續呼叫 execute() 方法時,只要 SQLStatement.text 屬性尚未變更,該陳述式就會保持已就緒的狀態。因此,執行起來就會較為快速。

為了取得重複使用陳述式的最大效益,如果在陳述式執行之間變更值,請使用陳述式參數來自訂您的陳述式 (陳述式參數是使用 SQLStatement.parameters 關聯陣列屬性所指定)。與變更 SQLStatement 實體的 text 屬性的不同之處在於,如果您變更陳述式參數的值,執行階段不需要再度準備陳述式。

當您重複使用 SQLStatement 實體時,您的應用程式必須在準備好之後,立即儲存該 SQLStatement 實體的參考。若要保留該實體的參考,請將變數宣告為類別範圍的變數,而不是函數範圍的變數。可用來讓 SQLStatement 成為類別範圍變數的其中一個方法,就是建立應用程式結構,讓 SQL 陳述式包覆在單一類別中。一組整合執行的陳述式也可以包覆在單一類別中 (此技巧稱為使用命令設計模式)。若將實體定義為類別的成員變數,只要應用程式中有包裝函式類別的實體,它們就會保留。最簡單的方式,就是在函數之外定義包含 SQLStatement 實體的變數,讓該實體保留在記憶體中。例如,將 SQLStatement 實體宣告為 ActionScript 類別中的成員變數,或宣告為 JavaScript 檔案中的非函數變數。然後,您就可以設定陳述式的參數值,並在要實際執行查詢時呼叫其 execute() 方法。

使用資料庫索引來提升比較和排序資料的執行速度。

當您為某欄建立索引時,資料庫就會儲存該欄資料的副本。該副本會以數字或字母順序來排序。此排序可讓資料庫快速地比對值 (例如使用相等運算子時),然後使用 ORDER BY 子句來排序結果資料。

資料庫索引會持續保持在更新狀態,這將造成針對該表格所執行的資料變更作業 (INSERT 或 UPDATE) 變得有些緩慢。不過,可大幅提升資料擷取速度。由於索引會犧牲效能,因此您不應該在每個資料表的每一欄都建立索引。而是使用策略來定義您的索引。使用下列準則來規劃索引策略:

  • 對聯結資料表、WHERE 子句或 ORDER BY 子句中使用的欄建立索引

  • 如果某些欄經常一起使用,請將這些欄建立為單一索引。

  • 對於內含擷取之文字資料,且以字母順序排序的欄,請指定該索引的 COLLATE NOCASE 定序

考慮在應用程式閒置期間,預先編譯 SQL 陳述式。

因為資料庫引擎需要準備 (編譯) SQL 文字,所以第一次執行 SQL 陳述式時速度較慢。由於準備及執行陳述式可能相當複雜,因此,最好預先載入初始資料,然後在背景中執行其他陳述式:

  1. 請先載入應用程式所需的資料。

  2. 當應用程式的初始啟動作業完成後,或是處於應用程式的其他「閒置」時間時,執行其他陳述式。

例如,假設您的應用程式完全不需存取資料庫,即可顯示其初始螢幕。在此情況下,請等到螢幕顯示出現之後,再開啟資料庫連線。最後,建立 SQLStatement 實體,然後執行任何您可執行的部分。

或者,假設您的應用程式要在啟動後立即顯示一些資料,例如特定查詢的結果。在此情況下,請直接執行 SQLStatement 實體以進行該項查詢。載入並顯示初始資料之後,請為其它資料庫作業建立 SQLStatement 實體,可能的話,請執行較後面階段才需要的其他陳述式。

實際作業時,如果重複使用 SQLStatement 實體,那麼您只需在第一次使用時花費額外的時間來準備陳述式。這對整體效能而言可能不會有太大的影響。

將交易中的多個 SQL 資料變更作業分組。

假設您要執行多個 SQL 陳述式,其中包含新增或變更資料 ( INSERT UPDATE 陳述式)。您可以在明確的交易內執行所有陳述式,讓效能大幅提升。如果未明確地開始進行交易,每個陳述式都會在本身自動建立的交易中執行。每個交易 (每個陳述式) 執行完成之後,執行階段會將結果資料寫入磁碟上的資料庫檔案。

另一方面,請仔細想想,如果明確建立交易,並在交易的內容中執行陳述式,會發生什麼情況?執行階段會在記憶體中進行所有變更,然後在認可交易後,將所有變更一次寫入資料檔中。將資料寫入磁碟中通常是作業中最耗費時間的部分。因此,一次寫入磁碟 (而不是每個 SQL 陳述式寫入一次) 可以大幅提升效能。

使用 SQLStatement 類別的 execute() 方法 (搭配 prefetch 參數) 與 next() 方法,來分批處理大量 SELECT 查詢結果。

假設您要執行的 SQL 陳述式會擷取大型結果集。應用程式必須處理迴圈中的每一列資料。例如,如果要格式化資料或從中建立物件。處理該資料可能會花費很長的時間,進而導致顯示問題,例如出現凍結現象或是螢幕沒有回應。其中一種解決方案是將工作分割成數個區塊,如 非同步作業 中所述。SQL 資料庫 API 可讓您輕鬆分割資料處理作業。

SQLStatement 類別 execute() 方法有一個選用參數 prefetch (第一個參數)。使用該參數提供的值可指定資料庫在執行完成時可傳回的結果列上限:

dbStatement.addEventListener(SQLEvent.RESULT, resultHandler); 
dbStatement.execute(100); // 100 rows maximum returned in the first set

傳回第一組結果資料之後,您可以呼叫 next() 方法,繼續執行陳述式並擷取另一組結果列。和 execute() 方法一樣, next() 方法會接受 prefetch 參數指定的傳回列數上限:

// This method is called when the execute() or next() method completes 
function resultHandler(event:SQLEvent):void 
{ 
    var result:SQLResult = dbStatement.getResult(); 
    if (result != null) 
    { 
        var numRows:int = result.data.length; 
        for (var i:int = 0; i < numRows; i++) 
        { 
            // Process the result data 
        } 
         
        if (!result.complete) 
        { 
            dbStatement.next(100); 
        } 
    } 
}

您可以繼續呼叫 next() 方法,直到載入所有資料。如先前的清單所示,您可以判斷資料於何時全部載入。檢查每次完成 execute() next() 方法時,都會建立的 SQLResult 物件所屬的 complete 屬性。

備註: 使用 prefetch 參數與 next() 方法,會分割結果資料的處理。請勿使用此參數與方法,將查詢結果限制為其結果集的一部分。如果您要在陳述式的結果集中擷取列的子集,請使用 SELECT 陳述式的 LIMIT 子句。如果結果集很大,您仍可以使用 prefetch 參數與 next() 方法來分割結果的處理程序。
考慮透過單一資料庫使用非同步 SQLConnection 物件,以便同時執行多個陳述式。

當 SQLConnection 物件使用 openAsync() 方法連線到資料庫時,它會在背景中執行,而不是在主要的執行階段執行緒中執行。此外,每個 SQLConnection 都會在自己的背景執行緒中執行。透過使用多個 SQLConnection 物件,您就可以有效地同時執行多個 SQL 陳述式。

但此方法也有一些缺點。最重要的缺點是每個額外的 SQLStatement 物件都需要額外的記憶體。此外,同時執行也可能會加重處理器的工作負荷,尤其是對只有一個 CPU 或 CPU 核心的電腦而言。基於以上考量,不建議您將此方法用於行動裝置。

另一個考量是,因為 SQLStatement 物件會連結至單一 SQLConnection 物件,所以可能會失去重複使用 SQLStatement 物件的優勢。因此,如果已使用與 SQLStatement 物件關聯的 SQLConnection 物件,就不能重複使用 SQLStatement 物件。

如果您選擇使用連線到單一資料庫的多個 SQLConnection 物件,請記住,每個物件都會在自己的交易中執行其陳述式。請務必在任何變更資料的程式碼中說明這些獨立的交易,例如,新增、修改或刪除資料。

Paul Robertson 建立的開放原始碼程式庫不但可協助您利用多個 SQLConnection 物件所提供的優點,還可減少會產生的缺點,該程式庫會使用 SQLConnection 物件集區,並管理相關聯的 SQLStatement 物件。如此一來,便能確保重複使用 SQLStatement 物件,您也可以使用多個 SQLConnection 物件來同時執行多個陳述式。如需詳細資訊以及下載該元件庫,請造訪 http://probertson.com/projects/air-sqlite/