2010-03-30 3 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 #으로) 간단한 테스트를 만든

테스트 '스크립트 -insert을'을 - 총 : 00 : 00 : 00.0292841; 통화 당 : 2ms

Test 'Script -Update'- 총계 : 00 : 00 : 00.0056296; 통화 당 : 0ms

Test 'Script -Select'- total : 00 : 00 : 00.0051738; 통화 당 : 0ms

Test 'Script -Delete'- total : 00 : 00 : 00.0059258; 통화 당 : 0ms

테스트 '절차 삽입'- 합계 : 00 : 00 : 01.2567146; 통화 당 : 125ms

테스트 '절차 - 업데이트'- 합계 : 00 : 00 : 00.7442440; 통화 당 : 74ms

테스트 '절차 선택'- 합계 : 00 : 00 : 00.5120446; 통화 당 : 51ms

테스트 '절차 - 삭제'- 합계 : 00 : 00 : 01.0619165; 총 -

테스트 '스크립트 -insert': 00:00 106ms

원격 서버와의 상황이 훨씬 더 나은,하지만 여전히 plaqin 쿼리 및 저장 프로 시저 사이에 큰 차이입니다 : 호출 당 : 00.0709299; 통화 당 : 7ms

Test 'Script -Update'- 총계 : 00 : 00 : 00.0161777; 전화 당 : 1ms

Test 'Script -Select'- total : 00 : 00 : 00.0258113; 통화 당 : 2ms

Test 'Script -Delete'- total : 00 : 00 : 00.0166242; 통화 당 : 1ms

테스트 '절차 삽입'- 합계 : 00 : 00 : 00.5116138; 통화 당 : 51ms

테스트 '절차 - 업데이트'- 합계 : 00 : 00 : 00.3802251; 통화 당 : 38ms

테스트 '절차 선택'- 총계 : 00 : 00 : 00.1241245; 통화 당 : 12ms

테스트 '절차 - 삭제'- 합계 : 00 : 00 : 00.4336334; 통화 당 : 43ms

SP 성능을 향상시킬 수 있습니까? 제발 조언.

ADO.NET 드라이버 버전 - 9.10.2.9

Server 버전 - 9.10.0.9 (ANSI - GERMAN, OEM - GERMAN)

고마워요!

답변

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이기 때문에 SELECT가 __output 테이블을 채울 정적 커서 (서버가 작성하고 채우는 내부 임시 파일을 의미 함)가 될 것이지만 이제는 또 다른 __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과 새 베타 버전으로 실행했습니다. 나는 리모트 서버에 대해 1000 회의 반복을 실행했다. 내가 위에서 설명한 select max(id) 변화 버전을 실행

v9.1:  101 seconds 
v10 beta: 2.2 seconds 

참고 : 속도 차이가 유의 하였다. 이 테스트는 상당히 오래된 개발 PC에서 수행되었습니다.

+0

좋아요! v10이 v9보다 성능이 훨씬 좋아진 것 같습니다.1로 제레미 약속했다. 불행히도, 현재이 시점에서 v9.1로 살 필요가 있으므로 현재로서는 SP가 없습니다. –