Firebird(如何使用 SQL 查找父类别)

Posted

技术标签:

【中文标题】Firebird(如何使用 SQL 查找父类别)【英文标题】:Firebird (how to find a parent category using SQL) 【发布时间】:2016-08-07 16:23:41 【问题描述】:

我有下表:

我想创建一个接收类别并返回给我的过程 类别父母,像这样:

我已经成功创建了一个过程,但它不显示数据 以我想要的方式。 此外,我相信有更好的方法(也许是递归?)来完成任务,但我不知道如何实现它。你有什么提示吗?

--数据库查询--

-- 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');

--我的过程--

    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;^

【问题讨论】:

【参考方案1】:
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^

【讨论】:

@SebastianJ 欢迎您。我还添加了另一种技术。查看编辑。【参考方案2】:

作为。正确的选择是避免使用存储过程和使用递归查询。 递归深度被硬编码为 1024。但是对于你的树来说这应该足够了。

How can I create "recursive sql" http://firebirdsql.su/doku.php?id=recursive

如果您的树可以长得更深,那么您将不得不退回到 SP。


如果你想使用SP,那么你必须编写一个SELECTABLE 过程。

而不是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;

【讨论】:

以上是关于Firebird(如何使用 SQL 查找父类别)的主要内容,如果未能解决你的问题,请参考以下文章

Firebird 2.5 SQL 查询执行计划不使用带有 OR 语句的索引

如何使用 SQL 在数据库中检测具有嵌套关系的父级?

如果子组件发生更改,如何重新渲染父组件

如何计算分层表结构中子类别支出的父类别支出总和?

sql 父类下的子类查询的方法,包含父类的信息

如何通过SQL语句来修改FireBird用户sysdba的密码