2016-08-07 40 views
2

(どのようにSQLを使用して親カテゴリを見つけるために)私は、次の表を持っている:Firebirdの

enter image description here

私はこのように、 カテゴリの両親をカテゴリに取り、戻って私を与える手順を作成したいです:

enter image description here

私はすでにプロシージャを作成するsuccededているが、それは私が望んでいた方法でデータ は表示されません。 さらに、私はタスクを達成するための良い方法(再帰かもしれません)があると信じていますが、実装方法はわかりません。何かヒントはありますか?

--database Query--

-- Creating Domain Boolean (didn't exist in FireBird) -- 

CREATE DOMAIN DBOOLEAN 
AS Smallint 
DEFAULT 0 
NOT NULL 
check (Value in (0,1)); 

-- Creating table -- 
CREATE TABLE TCATEGORIE 
(CODE_CAT Char(5) NOT NULL, 
NOM_CAT DNOM, 
CUISINE_CAT DBOOLEAN DEFAULT 0, 
FKCATPRINC_CAT Char(5), 
PRIMARY KEY (CODE_CAT) 
); 

-- Inserted data -- 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('B', 'Boissons', 0,null); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('BS', 'Boissons froides', 0, 'B'); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('BV', 'Vins', 0, 'B'); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('BVR', 'Vin rouge', 0, 'BV'); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('BVB', 'Vin Blanc', 0, 'BV'); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('BVROS', 'Vin Rose', 0, 'BV'); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('BC', 'Boissons chaudes', 0, 'B'); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('V', 'Viande', 1, null); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('VR', 'Viande Rouge', 1, 'V'); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('VB', 'Viande Blanche', 1, 'V'); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('P', 'Poisson', 1, null); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('F', 'Fromage', 1, null); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('S', 'Sauce', 1, null); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('D', 'Dessert', 1, null); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('DC', 'Dessert chaud', 1, 'D'); 
Insert Into TCATEGORIE 
(CODE_CAT, NOM_CAT, CUISINE_CAT, FKCATPRINC_CAT) 
values ('DG', 'Dessert glace', 1, 'D'); 

--My Procedure--

set term^; 
create procedure CatParent (choix 
type of column TCATEGORIE.CODE_CAT) 
returns (CODE_CAT1 type of column TCATEGORIE.CODE_CAT, 
CODE_CAT2 type of column TCATEGORIE.CODE_CAT, 
CODE_CAT3 type of column TCATEGORIE.CODE_CAT, 
NOM_CAT1 type of column TCATEGORIE.NOM_CAT, 
NOM_CAT2 type of column TCATEGORIE.NOM_CAT, 
NOM_CAT3 type of column TCATEGORIE.NOM_CAT) 
as 
begin 

if(char_length(trim(choix))>=3) 
then 
select c.CODE_CAT, c2.CODE_CAT, c3.CODE_CAT, 
c.NOM_CAT, c2.NOM_CAT, c3.NOM_CAT 
from TCATEGORIE c 
left join TCATEGORIE c2 
on c.CODE_CAT=c2.FKCATPRINC_CAT 
left join TCATEGORIE c3 
on c2.CODE_CAT=c3.FKCATPRINC_CAT 
where c.FKCATPRINC_CAT is null and 
c3.code_cat=:choix into :CODE_CAT1, :CODE_CAT2, :CODE_CAT3, 
:NOM_CAT1, :NOM_CAT2, :NOM_CAT3; 

else if(char_length(trim(choix))=2) 
then 
select first 1 c.CODE_CAT, c2.CODE_CAT, c.NOM_CAT, c2.NOM_CAT 
from TCATEGORIE c 
left join TCATEGORIE c2 
on c.CODE_CAT=c2.FKCATPRINC_CAT 
left join TCATEGORIE c3 
on c2.CODE_CAT=c3.FKCATPRINC_CAT 
where c.FKCATPRINC_CAT is null and 
c2.code_cat=:choix 
into :CODE_CAT1, :CODE_CAT2, 
:NOM_CAT1, :NOM_CAT2; 

else 
select first 1 c.CODE_CAT, c.NOM_CAT 
from TCATEGORIE c 
left join TCATEGORIE c2 
on c.CODE_CAT=c2.FKCATPRINC_CAT 
left join TCATEGORIE c3 
on c2.CODE_CAT=c3.FKCATPRINC_CAT 
where c.FKCATPRINC_CAT is null and 
c.code_cat=:choix into :CODE_CAT1, :NOM_CAT1; 


end^ 
set term;^ 

答えて

2
SET TERM^; 

create or alter procedure CAT_PARENT (
ICODE_CAT varchar(5)) 
returns (
CODE_CAT char(20), 
NOM_CAT varchar(256), 
CUISINE_CAT smallint, 
FKCATPRINC_CAT char(20)) 
as 
BEGIN 
    FOR 
    select 
     tcategorie.code_cat, 
     tcategorie.nom_cat, 
     tcategorie.cuisine_cat, 
     tcategorie.fkcatprinc_cat 
    from tcategorie 
    where 
     (
      (tcategorie.code_cat = :icode_cat) 
     ) 
    INTO :CODE_CAT, 
     :NOM_CAT, 
     :CUISINE_CAT, 
     :FKCATPRINC_CAT 
    DO 
    BEGIN 
    suspend; 
    while (:FKCATPRINC_CAT is not null) do 
     begin 
     execute procedure cat_parent(:FKCATPRINC_CAT) 
     returning_values(:CODE_CAT, 
      :NOM_CAT, 
      :CUISINE_CAT, 
      :FKCATPRINC_CAT); 
      SUSPEND; 
     end 
    END 
END^ 

SET TERM ;^

編集:

ます。また、このようCTE(Common Table Expressionを使用することができます。

SET TERM^; 

create or alter procedure CAT_PARENT_CTE (
ICODE_CAT varchar(5)) 
returns (
    CODE_CAT char(20), 
    NOM_CAT varchar(256)) 
as 
BEGIN 
for with recursive dept_code 
    as (
     select tcategorie.code_cat, tcategorie.nom_cat, tcategorie.fkcatprinc_cat 
    from tcategorie 
    where (tcategorie.code_cat = :icode_cat) 
    union all 
    select tcategorie.code_cat, tcategorie.nom_cat, tcategorie.fkcatprinc_cat from dept_code 
    inner join tcategorie on tcategorie.code_cat = dept_code.fkcatprinc_cat 
) 

    select dept_code.code_cat, dept_code.nom_cat from dept_code 
    into :CODE_CAT,:NOM_CAT 

    DO 
    BEGIN 
    suspend; 
    END 
END^ 
+0

クリーン&簡潔な。信じられないほど、ありがとう:) – SebastianJ

+0

@セバスチャンJあなたは大歓迎です。私もanoter技術を追加しました。編集を参照してください。 –

0

AS。適切なオプションは、ストアドプロシージャの使用と再帰的クエリの使用を避けることです。 再帰の深さは1024にハードコードされていますが、十分なはずのツリーの場合は。

あなたのツリーが深く成長することができます場合は、戻ってSPに落下する必要があります。


そして、あなたはSPを使用したい場合は、SELECTABLEプロシージャを記述する必要があります。

代わりの(定義により、1行のアクションです)Execute Procedure
あなたは以下のような手順でSelect * from CatParent('BVROS')を試してみて下さい。

create procedure CatParent (choix 
type of column TCATEGORIE.CODE_CAT) 
returns (CODE type of column TCATEGORIE.CODE_CAT, 
NOM type of column TCATEGORIE.NOM_CAT 
) 
as 
DECLARE VARIABLE ParentCode type of column TCATEGORIE.CODE_CAT; 
begin 
    CODE = :choix; 

    While (0 = 0) DO BEGIN 
    if (CODE is not null) then LEAVE; -- already traversed to root 

    NOM = NULL; 
    SELECT NOM_CAT, FKCATPRINC_CAT FROM TCATEGORIE 
     WHERE :CODE = CODE_CAT 
     INTO :NOM, :ParentCode; 

    if (:NOM is NULL) then LEAVE; 
    -- This code does not exists, tree is broken, Error. 
    -- Or empty name, Error too. 

    SUSPEND; -- yield results into the "table" 

    CODE = :ParentCode; -- prepare for searching next parent 
    END; 
end;