2013-08-20 41 views
3

我有兩個包pkg_companypkg_employee。 pkg_company包含(記錄)類型定義typ_company。 pkg_employee包含一個類型定義typ_employee。Oracle Pl/SQL:如何實現指向內存中記錄類型的指針

我有一個公司類型的實例和1000個typ_employee實例。在員工類型中,我想要一個指向公司實例的指針。

declare 
    c pkg_company.typ_company; 
    e pkg_employee.typ_employee; 
begin 
    c := pkg_company.get(12345); 
    for e in (select employeeid from employee where companyid=12345) 
    loop 
    e := pkg_employee.get(12345, c); -- c passed by reference in out nocopy 
    pkg_employee.process(e); -- I would like to access company info inside here as e.c.companyname 
    end loop; 
end; 

我該如何在c裏面存儲指針c?我不想創建c的1000份副本。只需要存儲指針並在需要時訪問該值。

感謝您的幫助!

end;

+0

可能只是添加'c'作爲'pkg_employee.process()'中的另一個'in nocopy'參數? – ThinkJet

+0

這就是我們目前正在做的事情,但是由於有很多函數/過程存在維護開銷,我們可能必須根據邏輯被修改的方式將該參數添加/刪除到函數簽名。如果我們可以在e內部有一個指向c的指針,那將會有很大的幫助。 – nomongo

回答

1

在Oracle SQL中,有可能use REF and DEREF functions將邏輯指針傳遞給存儲在數據庫表中的對象。
但是在PL/SQL中沒有像指針或引用那樣的東西,所以你需要一個解決方法。

以下是PL/SQL中可能的解決方法的一些方法。我很抱歉代碼中的任何錯誤。它的目的是爲了演示比用於生產的方法。

方法1 - 緩存的訪問方法

通用的想法是通過函數訪問實體哪個緩存結果:

create or replace package EntitytAccess as 

    procedure GetCompany1(
    pCompanyId in   number, 
    pCompany out nocopy pkg_company.typ_company 
); 

    function GetCompany2(
    pCompanyId in number 
) return pkg_company.typ_company; 

    procedure ClearCompanyCache; 

end; 

接包體:

create or replace package body EntitytAccess as 

    type typ_company_cache is table of pkg_company.typ_company index by number; 

    var_company_cache typ_company_cache; 

    procedure GetCompany1(
    pCompanyId in   number, 
    pCompany out nocopy pkg_company.typ_company 
) 
    is   
    begin 
    if(var_company_cache.exists(pCompanyId)) 
     pCompany := var_company_cache(pCompanyId); 
    else 
     pCompany := pkg_company.get(pCompanyId); 
     var_company_cache(pCompanyId) := pCompany; 
    end if; 
    end; 

    function GetCompany2(
    pCompanyId in number 
) return pkg_company.typ_company 
    is  
    begin 
    if(not var_company_cache.exists(pCompanyId)) 
     var_company_cache(pCompanyId) := pkg_company.get(pCompanyId); 
    end if;        
    return var_company_cache(pCompanyId); 
    end; 

    procedure ClearCompanyCache 
    is 
    begin    
    var_company_cache.Delete; 
    end; 

end; 

用法:

declare 
    c pkg_company.typ_company; 
    e pkg_employee.typ_employee; 
begin 
    EntityAccess.GetCompany2(12345,c); 
    for e in (select employeeid from employee where companyid=12345) 
    loop 

    e := pkg_employee.get(12345, c); -- c passed by reference in out nocopy 
            -- and c.companyid stored inside e. 

    pkg_employee.process(e); -- No need to pass company, but inside process() 
           -- method you need to call either 
           -- EntityAccess.GetCompany1(e.companyid, var_c) 
           -- or 
           -- var_c := EntityAccess.GetCompany2(e.companyid) 

    end loop; 
end; 

方法2 - 環境包

因爲包狀態屬於可以使用包變量來保存當前的處理狀態,並在需要的時候引用它一個會話。

create or replace package ProcessEnvironment as 

    var_current_company pkg_company.typ_company; 

    -- may be more "global" variables here 
end; 
/

create or replace package body ProcessEnvironment as 
end; 

用法:

declare 
    e pkg_employee.typ_employee; 
begin 
    ProcessEnvironmant.var_current_company := pkg_company.get(12345); 

    for e in (select employeeid from employee where companyid=12345) 
    loop 

    e := pkg_employee.get(12345, ProcessEnvironmant.var_current_company); 
          -- c passed by reference in out nocopy 
          -- and c.companyid stored inside e. 

    pkg_employee.process(e); -- No need to pass company, but inside process() 
           -- method you references 
           -- ProcessEnvironmant.var_current_company 
           -- with appropriate checks 

    end loop; 
end; 

混合的方法

似乎在Approach 1情況下,從集合複製,特別是如果與功能GetCompany2()訪問的情況。複製構造函數可能速度夠快,但會產生一些開銷。
對於Approach 2,在業務邏輯功能的代碼中必須存在一些檢查,所以從另一個角度來看它是維護開銷。
爲了解決這兩個問題,你可以使用緩存,但保持包裝內只有一個值:

create or replace package EntitytAccess as 

    procedure GetCompany(
    pCompanyId in   number, 
    pCompany out nocopy pkg_company.typ_company 
); 

end; 

包體:

create or replace package body EntitytAccess as 

    var_cached_company pkg_company.typ_company; 

    procedure GetCompany(
    pCompanyId in   number, 
    pCompany out nocopy pkg_company.typ_company 
) 
    is   
    begin 

    if((var_cached_company is null) 
     or 
     (var_cached_company.comanyid != pCompanyId) 
    )then 

     var_company_cache := pkg_company.get(pCompanyId); 

    end if; 

    pCompany := var_cached_company; 

    end; 

end; 

用法:

declare 
    c pkg_company.typ_company; 
    e pkg_employee.typ_employee; 
begin 
    EntityAccess.GetCompany(12345,c); 

    for e in (select employeeid from employee where companyid= c.companyid) 
    loop 

    e := pkg_employee.get(c.companyid , c); -- c passed by reference in out nocopy 
              -- and c.companyid stored inside e. 

    pkg_employee.process(e); -- No need to pass company, but inside process() 
           -- method you need to call 
           -- EntityAccess.GetCompany(e.companyid, var_c) 
           -- where var_c is company object variable. 

    end loop; 
end; 
0

如何在e內部存儲指向c的指針?我不想創建c的副本1000s 。只需要存儲指針並在需要時訪問值 。

「pkg_employee.get」是一個函數 - 刪除OUT參數。

  1. 定義函數的OUT參數是一種不好的做法。
  2. 如果只使用函數的結果,爲什麼它只是輸出參數?

如果您反覆調用相同的函數,並且該函數確定爲性能提升,則可以使用函數的「RESULT_CACHE」子句。請首先閱讀它,以確保這是您所需要的。

+0

a)NOCOPY [僅在OUT中允許](http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/parameter_declaration.htm#CJAJDGCC)b)OUT參數在函數中不合理實踐? c)聲明爲「out」的參數包含'nocopy'並避免複製實際參數對象實例。 – ThinkJet

+0

@ThinkJet a)我知道他不應該使用它 b)「不要使用OUT和IN OUT作爲功能參數。」 Oracle數據庫PL/SQL語言參考鏈接:http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/subprograms.htm#LNPLS659 c)他不需要它! NOCOPY增加了性能提升,在傳遞少量數據時不必使用它。 –

+0

@the_slk記錄類型非常複雜,並且有很多功能(我提供了一個簡單的例子,因此很容易理解)。如何將複雜記錄類型的函數定義爲參數而不進行復制(不使用nocopy)?關於result_cache,它僅適用於我們不使用的企業版。如果您還可以幫助解決主要問題:如何在另一個類型的另一個實例中包含指向內存中記錄類型的實例的指針? – nomongo