2010-03-30 64 views
6

我對ADS中存儲過程的性能有疑問。我創建了一個簡單的數據庫結構如下:Advantage數據庫服務器:存儲過程性能緩慢

CREATE TABLE MainTable 
(
    Id INTEGER PRIMARY KEY, 
    Name VARCHAR(50), 
    Value INTEGER 
); 

CREATE UNIQUE INDEX MainTableName_UIX ON MainTable (Name); 

CREATE TABLE SubTable 
(
    Id  INTEGER PRIMARY KEY, 
    MainId INTEGER, 
    Name VARCHAR(50), 
    Value INTEGER 
); 

CREATE INDEX SubTableMainId_UIX ON SubTable (MainId); 
CREATE UNIQUE INDEX SubTableName_UIX ON SubTable (Name); 

CREATE PROCEDURE CreateItems 
( 
    MainName VARCHAR (20), 
    SubName VARCHAR (20), 
    MainValue INTEGER, 
    SubValue INTEGER, 
    MainId INTEGER OUTPUT, 
    SubId  INTEGER OUTPUT 
) 
BEGIN 
    DECLARE @MainName VARCHAR (20); 
    DECLARE @SubName VARCHAR (20); 
    DECLARE @MainValue INTEGER; 
    DECLARE @SubValue INTEGER; 

    DECLARE @MainId INTEGER; 
    DECLARE @SubId  INTEGER; 

    @MainName = (SELECT MainName FROM __input); 
    @SubName = (SELECT SubName FROM __input); 
    @MainValue = (SELECT MainValue FROM __input); 
    @SubValue = (SELECT SubValue FROM __input); 

    @MainId = (SELECT MAX(Id)+1 FROM MainTable); 
    @SubId = (SELECT MAX(Id)+1 FROM SubTable); 

    INSERT INTO MainTable (Id, Name, Value) VALUES (@MainId, @MainName, @MainValue); 
    INSERT INTO SubTable (Id, Name, MainId, Value) VALUES (@SubId, @SubName, @MainId, @SubValue); 

    INSERT INTO __output SELECT @MainId, @SubId FROM system.iota; 
END; 

CREATE PROCEDURE UpdateItems 
( 
    MainName VARCHAR (20), 
    MainValue INTEGER, 
    SubValue INTEGER 
) 
BEGIN 
    DECLARE @MainName VARCHAR (20); 
    DECLARE @MainValue INTEGER; 
    DECLARE @SubValue INTEGER; 

    DECLARE @MainId INTEGER; 

    @MainName = (SELECT MainName FROM __input); 
    @MainValue = (SELECT MainValue FROM __input); 
    @SubValue = (SELECT SubValue FROM __input); 

    @MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = @MainName); 

    UPDATE MainTable SET Value = @MainValue WHERE Id  = @MainId; 
    UPDATE SubTable SET Value = @SubValue WHERE MainId = @MainId; 
END; 

CREATE PROCEDURE SelectItems 
( 
    MainName  VARCHAR (20), 
    CalculatedValue INTEGER OUTPUT 
) 
BEGIN 
    DECLARE @MainName VARCHAR (20); 

    @MainName = (SELECT MainName FROM __input); 

    INSERT INTO __output SELECT m.Value * s.Value FROM MainTable m INNER JOIN SubTable s ON m.Id = s.MainId WHERE m.Name = @MainName; 
END; 

CREATE PROCEDURE DeleteItems 
( 
    MainName VARCHAR (20) 
) 
BEGIN 
    DECLARE @MainName VARCHAR (20); 
    DECLARE @MainId INTEGER; 

    @MainName = (SELECT MainName FROM __input); 
    @MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = @MainName); 

    DELETE FROM SubTable WHERE MainId = @MainId; 
    DELETE FROM MainTable WHERE Id  = @MainId; 
END; 

其實,我有這個問題 - 即使這樣光存儲過程的工作非常,非常緩慢(約50-150毫秒)相對平淡查詢(0-5ms) 。

open System; 
open System.Data; 
open System.Diagnostics; 
open Advantage.Data.Provider; 


let mainName = "main name #"; 
let subName = "sub name #"; 

// INSERT 
let cmdTextScriptInsert = " 
    DECLARE @MainId INTEGER; 
    DECLARE @SubId INTEGER; 

    @MainId = (SELECT MAX(Id)+1 FROM MainTable); 
    @SubId = (SELECT MAX(Id)+1 FROM SubTable); 

    INSERT INTO MainTable (Id, Name, Value) VALUES (@MainId, :MainName, :MainValue); 
    INSERT INTO SubTable (Id, Name, MainId, Value) VALUES (@SubId, :SubName, @MainId, :SubValue); 

    SELECT @MainId, @SubId FROM system.iota;"; 
let cmdTextProcedureInsert = "CreateItems"; 

// UPDATE 
let cmdTextScriptUpdate = " 
    DECLARE @MainId INTEGER; 

    @MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = :MainName); 

    UPDATE MainTable SET Value = :MainValue WHERE Id  = @MainId; 
    UPDATE SubTable SET Value = :SubValue WHERE MainId = @MainId;"; 
let cmdTextProcedureUpdate = "UpdateItems"; 

// SELECT 
let cmdTextScriptSelect = " 
    SELECT m.Value * s.Value FROM MainTable m INNER JOIN SubTable s ON m.Id = s.MainId WHERE m.Name = :MainName;"; 
let cmdTextProcedureSelect = "SelectItems"; 

// DELETE 
let cmdTextScriptDelete = " 
    DECLARE @MainId INTEGER; 

    @MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = :MainName); 

    DELETE FROM SubTable WHERE MainId = @MainId; 
    DELETE FROM MainTable WHERE Id  = @MainId;"; 
let cmdTextProcedureDelete = "DeleteItems"; 




let cnnStr = @"data source=D:\DB\test.add; ServerType=local; user id=adssys; password=***;"; 
let cnn = new AdsConnection(cnnStr); 

try 
    cnn.Open(); 

    let cmd = cnn.CreateCommand(); 

    let parametrize ix prms = 
     cmd.Parameters.Clear(); 

     let addParam = function 
      | "MainName" -> cmd.Parameters.Add(":MainName" , mainName + ix.ToString()) |> ignore; 
      | "SubName" -> cmd.Parameters.Add(":SubName" , subName + ix.ToString()) |> ignore; 
      | "MainValue" -> cmd.Parameters.Add(":MainValue", ix * 3     ) |> ignore; 
      | "SubValue" -> cmd.Parameters.Add(":SubValue" , ix * 7     ) |> ignore; 
      | _ ->() 

     prms |> List.iter addParam; 


    let runTest testData = 

     let (cmdType, cmdName, cmdText, cmdParams) = testData; 

     let toPrefix cmdType cmdName = 
      let prefix = match cmdType with 
       | CommandType.StoredProcedure -> "Procedure-" 
       | CommandType.Text   -> "Script -" 
       | _       -> "Unknown -" 
      in prefix + cmdName; 

     let stopWatch = new Stopwatch(); 

     let runStep ix prms = 
      parametrize ix prms; 
      stopWatch.Start(); 
      cmd.ExecuteNonQuery() |> ignore; 
      stopWatch.Stop(); 

     cmd.CommandText <- cmdText; 
     cmd.CommandType <- cmdType; 

     let startId = 1500; 
     let count = 10; 

     for id in startId .. startId+count do 
      runStep id cmdParams; 

     let elapsed = stopWatch.Elapsed; 
     Console.WriteLine("Test '{0}' - total: {1}; per call: {2}ms", toPrefix cmdType cmdName, elapsed, Convert.ToInt32(elapsed.TotalMilliseconds)/count); 


    let lst = [ 
     (CommandType.Text,   "Insert", cmdTextScriptInsert, ["MainName"; "SubName"; "MainValue"; "SubValue"]); 
     (CommandType.Text,   "Update", cmdTextScriptUpdate, ["MainName"; "MainValue"; "SubValue"]); 
     (CommandType.Text,   "Select", cmdTextScriptSelect, ["MainName"]); 
     (CommandType.Text,   "Delete", cmdTextScriptDelete, ["MainName"]) 
     (CommandType.StoredProcedure, "Insert", cmdTextProcedureInsert, ["MainName"; "SubName"; "MainValue"; "SubValue"]); 
     (CommandType.StoredProcedure, "Update", cmdTextProcedureUpdate, ["MainName"; "MainValue"; "SubValue"]); 
     (CommandType.StoredProcedure, "Select", cmdTextProcedureSelect, ["MainName"]); 
     (CommandType.StoredProcedure, "Delete", cmdTextProcedureDelete, ["MainName"])]; 

    lst |> List.iter runTest; 

finally 
    cnn.Close(); 

而且我得到以下結果:爲了測試性能,我(ADS使用ADO.NET提供的F#)創建了一個簡單的測試

測試 '腳本 - 插入' - 總計:00:00:00.0292841;每次調用:2ms

測試「腳本更新」 - 總計:00:00:00.0056296;每次調用:0ms

Test'Script -Select' - total:00:00:00.0051738;每次通話:0ms

Test'Script-Delete' - total:00:00:00.0059258;每次調用:0ms

Test'Procedure-Insert' - total:00:00:01.2567146;每次通話:125ms

測試'程序更新' - 總計:00:00:00.7442440;每次通話:74ms

測試'程序 - 選擇' - 總計:00:00:00.5120446;每次通話:51ms

測試'程序 - 刪除' - 總計:00:00:01.0619165;每次通話:106ms

與遠程服務器的情況就好多了,但還是plaqin查詢和存儲過程的差距很大:

測試「腳本 - 插入」 - 總:00:00 :00.0709299;每個電話:7ms

測試'腳本 - 更新' - 總計:00:00:00.0161777;每次通話:1ms

測試「腳本 - 選擇」 - 總計:00:00:00.0258113;每個電話:2ms

Test'Script -Delete' - total:00:00:00.0166242;每次調用:1ms

測試'程序 - 插入' - 總計:00:00:00.5116138;每次通話:51ms

測試'Procedure-Update' - 總計:00:00:00.3802251;每次通話:38ms

測試'程序 - 選擇' - 總計:00:00:00.1241245;每次調用:12ms

測試'程序 - 刪除' - 總計:00:00:00.4336334;每通電話:43ms

難道有機會提高SP的性能嗎?請指教。

ADO.NET驅動程序版本 - 9.10.2.9

服務器版本 - 9.10.0。9(ANSI - 德國,OEM - 德國)

謝謝!

回答

6

Advantage v10 beta包括直接針對存儲過程性能的各種性能改進。這裏有一些事情要考慮與目前的出貨版本,但是:

在你CreateItems程序它可能會更有效率與使用單光標來代替

@MainName = (SELECT MainName FROM __input); 
@SubName = (SELECT SubName FROM __input); 
@MainValue = (SELECT MainValue FROM __input); 
@SubValue = (SELECT SubValue FROM __input); 

檢索所有參數:

DECLARE input CURSOR; 
OPEN input as SELECT * from __input; 
FETCH input; 
@MainName = input.MainName; 
@SubName = input.SubName; 
@MainValue = input.MainValue; 
@SubValue = input.SubValue; 
CLOSE input; 

這將避免3語句解析/語義/優化/執行操作只是爲了檢索輸入參數(我知道,我們真的需要完全消除__input表)。

SelectItems過程很少會像從客戶端選擇一樣快,特別是在這種情況下,除了抽象參數值(可以很容易地在客戶端上完成)之外,它沒有做任何事情。請記住,因爲它是JOIN,所以填充__output表的SELECT將成爲一個靜態遊標(意味着服務器要創建和填充的內部臨時文件),但是現在還有另一個__output表臨時文件的服務器,再加上你有額外的開銷來填充這個__output表中已經放置在靜態遊標臨時表中的數據,只是爲了複製它(服務器可以更好地檢測這個並替換__output與現有的靜態遊標引用,但它目前不)。

我會嘗試花一些時間在版本10上嘗試您的過程。如果您有測試表在測試中使用,請隨意將它們壓縮併發送到[email protected]並將attn:JD在主題。

5

有一個變化可以幫助CreateItems程序。更改以下兩個語句:

@MainId = (SELECT MAX(Id)+1 FROM MainTable); 
@SubId = (SELECT MAX(Id)+1 FROM SubTable); 

要這樣:

@MainId = (SELECT MAX(Id) FROM MainTable); 
@MainId = @MainId + 1; 
@SubId = (SELECT MAX(Id) FROM SubTable); 
@SubId = @SubId + 1; 

我看着查詢計劃信息(數據優勢建築師)該語句的第一個版本。看起來好像優化器不會將MAX(id)+1分解成組件。語句select max(id) from maintable可以使用ID字段上的索引進行優化。看起來max(id)+1未優化。因此,隨着表格的增長,做出這種改變將會非常重要。

另一件可能有用的事情是在每個腳本的頂部添加一個CACHE PREPARE ON;語句。這可以在多次運行時幫助執行某些過程。

編輯Advantage v10 beta今天發佈。所以我跑了你的CreateItems程序與v9.1和新的beta版本。我對遠程服務器運行了1000次迭代。速度差異顯著:

v9.1:  101 seconds 
v10 beta: 2.2 seconds 

注意,我跑了一個版本,我上述的select max(id)變化。這個測試是在我相當老的開發PC上。

+0

太棒了!似乎v10的性能比v9好得多。1正如傑里米承諾的那樣。 不幸的是,在這一刻我必須與v9.1一起生活,所以目前沒有SP。 – 2010-04-03 15:59:56