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 查找父类别)的主要内容,如果未能解决你的问题,请参考以下文章