sql - 循环匹配

标签 sql algorithm oracle circular-reference

我有一个包含三个表的数据库:userid_tbl、need_tbl、have_tbl

create table userid_tbl
(user_id varchar2(15) not null primary key);

create table need_tbl
(user_id varchar2(15) not null,
have_item varchar2(100) not null,
foreign key (user_id) references userid_tbl (user_id)
);

create table have_tbl
(user_id varchar2(15) not null,
have_item varchar2(100) not null,
foreign key (user_id) references userid_tbl (user_id)
);

每个用户都可以在数据库中创建无限数量的需求记录或他们可以提供给其他人的服务。这些项目来自预定列表。

在对 need 和 have 表进行连接后,我匹配了需求和需求,并在如下所示的 View 中丢弃了任何用户无法满足的请求:

Item:         Need:            Have:
1             Bob              George
2             George           Herbie
3             Herbie           Bob
4             Larry            Wally
5             Wally            Larry
6             John             George

我现在正在尝试编写一个查询,我可以在其中计算每个用户满足其请求需求的排列数。例如,在上面的示例中,Bob、George 和 Herbie 可以一起满足彼此的需求,Larry 和 Wally 也可以,但 John 不能,因此总数为 2。

我一开始是用一个 LOOP 来做这件事的,但是必须有一种更简单、资源占用更少的方法来用一个查询来做这件事。

最佳答案

详细解释见我博客中的文章:

如果在表格的needs 列中多次找到您的用户,这是一个NP 复杂任务。

如果没有,请在您的 View 上发出此查询:

SELECT  COUNT(*)
FROM    v_needs
CONNECT BY NOCYCLE
        need = PRIOR have
WHERE   CONNECT_BY_ISCYCLE = 1

CONNECT BY NOCYCLE 允许分层查询中的循环(NOCYCLE 只是在发现循环时停止分支,没有 NOCYCLE 返回错误) .

CONNECT_BY_ISCYCLE 在发现循环时返回 true(它返回 1 用于在下一次迭代中产生当前分支根的记录)。

请注意,此查询将返回循环中的所有用户,而不会对它们进行分组。

要对用户进行分组,请发出此查询:

SELECT  n.*, CONNECT_BY_ROOT(have), level
FROM    v_needs n
START WITH
        have IN
        (
        SELECT  MIN(have)
        FROM    (
                SELECT  have, CONNECT_BY_ROOT(have) AS root
                FROM    t_needs
                START WITH
                        have IN
                        (
                        SELECT  have
                        FROM    t_needs n
                        WHERE   CONNECT_BY_ISCYCLE = 1
                        CONNECT BY NOCYCLE
                                needs = PRIOR have
                        )
                CONNECT BY NOCYCLE
                        have = PRIOR needs
                )
        GROUP BY
                root
        )
CONNECT BY NOCYCLE
        have = PRIOR needs

更新:

从您的帖子中我可以看出您对最大循环长度有限制。

这使得这个问题更容易解决。

只需将循环限制条件添加到每个查询中:

SELECT  n.*, CONNECT_BY_ROOT(have), level
FROM    v_needs n
START WITH
        have IN
        (
        SELECT  MIN(have)
        FROM    (
                SELECT  have, CONNECT_BY_ROOT(have) AS root
                FROM    t_needs
                START WITH
                        have IN
                        (
                        SELECT  have
                        FROM    t_needs n
                        WHERE   CONNECT_BY_ISCYCLE = 1
                        CONNECT BY NOCYCLE
                                needs = PRIOR have
                                AND level <= 5
                        )
                CONNECT BY NOCYCLE
                        have = PRIOR needs
                        AND level <= 5
                )
        GROUP BY
                root
        )
CONNECT BY NOCYCLE
        have = PRIOR needs
        AND level <= 5

这将停止遍历 5th 级别的层次结构树。

如果您的数据库中有 1,000,000 个用户,平均每个用户 5 个匹配项,则查询需要检查 1,000,000 * 5^5 = 3,125,000,000 行,这是可观察的行数。

如果您MATERIALIZE View 并在“需要”和“有”上创建索引,查询将得到极大改进。

在这种情况下,查询将在几分钟内完成。

关于sql - 循环匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/876922/

相关文章:

c# - 连接可变数量的字符串

sql-server - Entity Framework : Schema specified is not valid

sql - Oracle - 分组依据为 "steps"

php - 如果有任何服务绑定(bind)到另一个表中的类别 ID,则从表中获取类别

sql - oracle中select语句中的子查询是如何工作的

java - 显示斐波那契数列的算术?

algorithm - 从有 n 个元素的数组中获取 k 大的元素,其中 n 远大于 k

java - 从排序数组创建 BST 的大 Oh

sql - 尽管语法不正确,Oracle脚本也不会产生错误。

sql - 在 VB.NET 中从 SQL Server 执行存储过程?