挑战Firebird递归CTE问题

时间:2015-04-08 04:31:55

标签: sql grouping firebird common-table-expression recursive-query

这可能不是一个直截了当的Firebird问题,但我希望有一个我不知道的功能可以帮助我超越普通的SQL。

我有两张桌子。第一个是“关键参数”的名称列表,第二个是关联某些对象ID,关键参数名称和关键参数值:

CREATE TABLE CRITICALPARAMS
(
PARAM Varchar(32) NOT NULL,
INDX INTEGER NOT NULL,
CONSTRAINT PK_CRITICALPARAMS_1 PRIMARY KEY (PARAM),
CONSTRAINT UNQ_CRITICALPARAMS_1 UNIQUE (INDX)
);

CREATE TABLE CRITICALPARAMVALS
(
ID INTEGER NOT NULL,
PARAM Varchar(32) NOT NULL,
VAL Float NOT NULL,
CONSTRAINT PK_CRITICALPARAMVALS_1 PRIMARY KEY (ID,PARAM)
);

让我们假设我们有一个四维空间:

insert into CRITICALPARAMS values ('a', 1);
insert into CRITICALPARAMS values ('b', 2);
insert into CRITICALPARAMS values ('c', 3);
insert into CRITICALPARAMS values ('foo', 4);

......以及那个空间中的一些物品:

insert into CRITICALPARAMVALS values (1, 'a', 0.0);
insert into CRITICALPARAMVALS values (1, 'b', 0.0);
insert into CRITICALPARAMVALS values (1, 'c', 2.0);
insert into CRITICALPARAMVALS values (1, 'foo', 99.0);
insert into CRITICALPARAMVALS values (2, 'a', 0.0);
insert into CRITICALPARAMVALS values (2, 'b', 0.0);
insert into CRITICALPARAMVALS values (2, 'c', 2.0);
insert into CRITICALPARAMVALS values (2, 'foo', 99.0);
insert into CRITICALPARAMVALS values (3, 'a', 0.0);
insert into CRITICALPARAMVALS values (3, 'b', 0.0);
insert into CRITICALPARAMVALS values (3, 'c', 1.0);
insert into CRITICALPARAMVALS values (3, 'foo', 98.0);
insert into CRITICALPARAMVALS values (4, 'a', 0.0);
insert into CRITICALPARAMVALS values (4, 'b', 0.0);
insert into CRITICALPARAMVALS values (4, 'c', 1.0);
insert into CRITICALPARAMVALS values (4, 'foo', 98.0);
insert into CRITICALPARAMVALS values (5, 'a', 0.0);
insert into CRITICALPARAMVALS values (5, 'b', 0.0);
insert into CRITICALPARAMVALS values (5, 'c', 2.0);
insert into CRITICALPARAMVALS values (5, 'foo', 98.0);

问题是对关键参数空间进行分区,将具有相同参数值的所有对象ID分组在一起。我们可以考虑使用“种子”对象ID,并询问其他ID与种子对象属于同一分区。

在我们的例子中,对象1和2形成一个分区,3和4形成另一个,5形成第三个。关键参数a和b中的所有五个对象相等,但参数c和foo不同。

有没有办法用普通的SQL来解决这个问题?如何递归CTE?

我已经粗略地解决了这个问题,在存储过程中使用EXECUTE STATEMENT,循环遍历种子的关键参数值,并手动构建一个包含尽可能多WHERE子句作为关键参数的大型SQL语句,但该解决方案不能扩展为我上升到大约500-1000个关键参数(或更多!)。

我目前的尝试已经逐渐消失了 - 我首先定义一个视图,它可以给我一个关键参数的分区(TEST_FLOAT_EQ是一个可选择的存储过程,比较两个浮点数'足够好!'相等) :

CREATE VIEW VGROUPIDBYPARAM (SEEDID, GROUPMEMBERID, CRITPARAMINDX)
AS 
select a.id as seedid, b.id as groupmemberid, c.INDX as critparamindx
from CRITICALPARAMVALS a
join CRITICALPARAMVALS b on a.PARAM=b.param and (exists (select isequal from TEST_FLOAT_EQ(a.val, b.val, 1e-5) where ISEQUAL=1))
join CRITICALPARAMS c on b.param=c.PARAM;

...然后我想归纳地使用VGROUPIDBYPARAM视图,类似于以下部分完整的选择:

SELECT a1.SEEDID, a6.GROUPMEMBERID
FROM VGROUPIDBYPARAM a1
join VGROUPIDBYPARAM a2 on a1.SEEDID=a2.SEEDID and a1.GROUPMEMBERID=a2.GROUPMEMBERID
join VGROUPIDBYPARAM a3 on a1.SEEDID=a3.SEEDID and a2.GROUPMEMBERID=a3.GROUPMEMBERID
join VGROUPIDBYPARAM a4 on a1.SEEDID=a4.SEEDID and a3.GROUPMEMBERID=a4.GROUPMEMBERID
join VGROUPIDBYPARAM a5 on a1.SEEDID=a5.SEEDID and a4.GROUPMEMBERID=a5.GROUPMEMBERID
join VGROUPIDBYPARAM a6 on a1.SEEDID=a6.SEEDID and a5.GROUPMEMBERID=a6.GROUPMEMBERID
...
where a1.CRITPARAMINDX=1
and a2.CRITPARAMINDX=2
and a3.CRITPARAMINDX=3
and a4.CRITPARAMINDX=4
and a5.CRITPARAMINDX=5
and a6.CRITPARAMINDX=6
...

在这个归纳过程结束时(我希望递归CTE可以模仿),唯一幸存的记录通过一堆JOINS使组成员ID属于与种子ID相同的分区。 / p>

非常感谢能够帮助我有效解决问题的任何人!

1 个答案:

答案 0 :(得分:2)

要解决这个问题,我会从这个简单的查询开始(计算其他对象中的匹配维度):

SELECT
    CPV1.ID AS ID1,
    CPV2.ID AS ID2,
    COUNT(*)
FROM
    CRITICALPARAMVALS CPV1
    INNER JOIN CRITICALPARAMVALS CPV2 ON CPV1.ID <> CPV2.ID
          AND CPV1.PARAM = CPV2.PARAM
          AND CPV1.VAL = CPV2.VAL
GROUP BY
    CPV1.ID, CPV2.ID

使用以下输出:

enter image description here

如您所见,有趣的行标有黄色背景。

要仅过滤那些行,我们应该添加以下条件:

HAVING COUNT(*) = (SELECT COUNT(*) FROM CRITICALPARAMS)
  

我们可以考虑使用&#34;种子&#34;对象ID,并询问其他ID   属于与种子对象相同的分区。

使用:SEED参数回答上述问题的最终查询如下所示:

SELECT
    CPV2.ID
FROM
    CRITICALPARAMVALS CPV1
    INNER JOIN CRITICALPARAMVALS CPV2 ON CPV1.ID <> CPV2.ID
          AND CPV1.PARAM = CPV2.PARAM
          AND CPV1.VAL = CPV2.VAL
WHERE CPV1.ID = :SEED
GROUP BY
    CPV1.ID, CPV2.ID  
HAVING COUNT(*) = (SELECT COUNT(*) FROM CRITICALPARAMS)

即使对于大数据集也应该表现良好。