2

私はすでに書いた概念実証のためにパフォーマンスを改善しようとしていますが、運がありません。このアプローチにはおそらく欠陥があると思いますが、別の解決策を見つけるのに苦労しています。私が見つけることができるすべてのAskTomの記事とフォーラムの投稿をカバーしました。

Oracle10gR2を実行しています。

アイテムは階層構造で配置されています。数量は関係で定義されます。階層内のオブジェクトには、論理グループであるアセンブリと、実際のアイテムを表すアイテムの2種類があります。したがって、完全なツールセットを表す場合、ツールセット全体を表すルートと、実際のツールを表すリーフがあります。それで:

ツールセット->ドライバー->マイナスドライバー->小型マイナスドライバー

アセンブリは、アイテムと同様に、階層で再利用できます。

アイテムの各インスタンスに行と数量が含まれるように、階層をフラット化する必要があります。どの関係も数量>=1にすることができます。アイテムの数量を取得するには、ルートからリーフまでのすべての関係から数量の積を取得する必要があります。

私のソリューションは機能しますが、拡張性が高くありません。実際のデータに対して実行すると、6000以上の行を生成するのに約8分かかり、5万以上の行を生成する階層があります。理想的にはこれは10秒以内に完了するでしょうが、それは…楽観的です;)

私のソリューションと簡略化されたデータセットは以下のとおりです。フィードバックをいただければ幸いです。

CREATE TABLE ITEMHIER
(
  PARENT          VARCHAR2(30 BYTE),
  CHILD           VARCHAR2(30 BYTE),
  QUANTITY        NUMBER(15,2),
  ISLEAF          NUMBER
);

INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY005','ITEM001',2,1);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY005','ITEM002',1,1);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY005','ITEM003',5,1);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY006','ITEM002',10,1);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY006','ITEM004',3,1);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY007','ITEM005',12,1);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY007','ITEM006',1,1);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY008','ITEM006',2,1);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY008','ITEM005',5,1);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY002','ASSY005',2,0);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY002','ASSY007',1,0);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY003','ASSY006',3,0);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY003','ASSY008',2,0);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY004','ASSY007',1,0);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY004','ASSY005',3,0);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY004','ASSY006',2,0);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY001','ASSY002',1,0);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY001','ASSY003',2,0);
INSERT INTO ITEMHIER (PARENT, CHILD, QUANTITY, ISLEAF) VALUES ('ASSY001','ASSY004',1,0);

COMMIT;
/

CREATE OR REPLACE FUNCTION GETQTY(P_NAVPATH   IN VARCHAR2,
                                  P_STARTWITH IN VARCHAR2) RETURN INTEGER AS

R_QTY  INTEGER;

BEGIN

    SELECT EXP(SUM(LN(QUANTITY)))
    INTO R_QTY
    FROM (
           SELECT QUANTITY, SYS_CONNECT_BY_PATH(CHILD,'/') NAV_PATH
           FROM ITEMHIER
           START WITH PARENT = P_STARTWITH
           CONNECT BY PRIOR  CHILD = PARENT
         )
    WHERE INSTR(P_NAVPATH, NAV_PATH) = 1; 

    RETURN R_QTY;
END;
/

SELECT 'ASSY001' || SYS_CONNECT_BY_PATH(CHILD,'/') NAV_PATH,
      GETQTY(SYS_CONNECT_BY_PATH(CHILD,'/'), 'ASSY001') QTY,
      CHILD
FROM ITEMHIER
WHERE ISLEAF = 1
START WITH PARENT = 'ASSY001'
CONNECT BY PRIOR CHILD = PARENT;

- - 編集

このWITH句を使用すると、処理時間を約1/2に短縮できました。これは、大きなメリットです。他のアイデアはありますか?

with
h as (
    select sys_connect_by_path(child,'/') navpath,
          child,
          quantity qty,
          isleaf
    from itemhier
    start with parent = 'ASSY001'
    connect by prior child = parent
)
select h1.navpath,
       h1.child,
       (SELECT exp(sum(ln(h2.qty)))
        FROM h h2
        WHERE instr(h1.navpath, h2.navpath) = 1) qty
from h h1
where isleaf = 1

編集2

sys_connect_by_pathを使用して算術式を作成し、PL / SQLを使用してそれを評価するというjonearlesの提案は、進むべき道のようです。最大のデータセットに対して実行すると、55秒で77k行の出力を生成できました。

私も並列処理を使用しようとしましたが、彼が指摘したように、パフォーマンスの向上はほとんどまたはまったくありませんでした。

4

3 に答える 3

3

Podiluskaの提案は良いです。Oracle 11g R2を使用している場合は、一般的なテーブル式が最適です。sys_connect_by_path新しい構文の再帰的な性質により、との組み合わせを捨てることができinstr、パフォーマンスに深刻な悪影響を及ぼします。

これを試して:

select
  child,
  sum(total_quantity) total_quantity
from (
  with h (parent, child, isleaf, quantity, total_quantity) as (
    select 
      parent,
      child,
      isleaf,
      quantity,
      quantity total_quantity
    from
      itemhier
    where
      parent = 'ASSY001' 
    union all
    select
      ih.parent,
      ih.child,
      ih.isleaf,
      ih.quantity,
      ih.quantity * h.total_quantity total_quantity
    from
      itemhier ih
    join 
      h on h.child = ih.parent
  )
  select * from h
  where isleaf = 1
)
group by child;

これがsqlfiddleです: http ://sqlfiddle.com/#!4 / 9840f / 6

于 2012-09-01T07:06:07.410 に答える
2

ステートメント、および一般的なテーブル式/サブクエリファクタリングを確認する必要がありますWITH。これにより、単一のSQLステートメントで階層をトラバースできます。おそらくそれも速くなるでしょう。

例えば:

'assy002'のすべてのリーフを検索するには

with cte as
(
    select * from #ITEMHIER
    union all
    select i.PARENT, cte.CHILD, cte.QUANTITY, cte.ISLEAF
    from #ITEMHIER i
        inner join cte on i.CHILD = cte.PARENT
)
    select CHILD,QUANTITY, isleaf from cte
    where PARENT='assy002'
    and isleaf=1;
于 2012-08-31T19:01:29.643 に答える
2

SYS_CONNECT_BY_PATHブランチ内のすべての数量の積である式を生成するために使用できます。次に、関数を使用してその式を動的に実行し、最終的な数量を取得します。

それは理想的な解決策ではありません。SQLとPL/SQLのコンテキストスイッチには時間がかかります。そして、SQLインジェクションについて心配する必要があります。ただし、少なくとも同じテーブルを2回クエリすることは避けられます。

(Dan A.とpodiluskaが示唆したように、再帰CTEが最善の解決策である可能性が非常に高いです。私の経験では、2つの構文が同じことを行い、同様のアクセスパスを使用している場合でも、再帰CTEは。よりも大幅に高速ですconnect by。ただし、11gR2にアップグレードするまで待つ必要があります。)

CREATE OR REPLACE FUNCTION EVALUATE_EXPRESSION(P_EXPRESSION IN VARCHAR2) RETURN NUMBER AS
    R_QTY  INTEGER;
BEGIN
    EXECUTE IMMEDIATE 'SELECT '||P_EXPRESSION||' FROM DUAL' INTO R_QTY;
    RETURN R_QTY;
END;
/


SELECT 'ASSY001' || SYS_CONNECT_BY_PATH(CHILD,'/') NAV_PATH,
      GETQTY(SYS_CONNECT_BY_PATH(CHILD,'/'), 'ASSY001') QTY,
      SUBSTR(SYS_CONNECT_BY_PATH(QUANTITY,'*'), 2) QTY_EXPRESSION,
      EVALUATE_EXPRESSION(SUBSTR(SYS_CONNECT_BY_PATH(QUANTITY,'*'), 2)) QTY2,
      CHILD
FROM ITEMHIER
WHERE ISLEAF = 1
START WITH PARENT = 'ASSY001'
CONNECT BY PRIOR CHILD = PARENT;

また、テーブルにインデックスがあるとおっしゃいました。しかし、クエリはインデックスを使用していますか?説明プランを投稿してもらえますか?

最後に、これほど遅いクエリでは、並列処理を調べる必要があるかもしれません。残念ながら、並列処理とを使用して運が良かったことは一度もありませんconnect by

于 2012-09-04T05:20:34.223 に答える