假设我们有一个关联 TopicID 和 StoryID 的 SQL 表 TopicStory,
这对列形成一个复合主键。
目标是找到一组特定的 TopicID。最多五个
在一组中发布的问题需要。深度优先搜索
这些概述如下。
搜索深度为 5 时,上述问题不能
比多项式复杂度差。然而一个普遍的问题
这要求可以找到的最大主题集 like
约束(选择的每个主题至少有两个与
任何其他选择的主题)可能是 NP 完全的。
“搜索”一词的使用暗示了一种回溯算法。以下
我们通过嵌套循环实现了回溯,其中每个循环都是
由其外部的循环定义和参数化。
在我们给出具体细节之前,描述一个
“蛮力”方法,其次是更复杂的方法
更容易被欣赏。
BRUTE_FORCE:
Generate all possible subsets of five topics.
Test each of these sets for feasibility (each topic has
at least two stories unrelated to any of the other topics).
我们的深度优先搜索草图假设主题具有总排序,
例如按 TopicID 的整数值排序。这允许主题集
不重复生成(由于主题的排列)。
NESTED_LOOPS:
(Loop_1) Select into List_1 all topics with at least two stories.
Iterate through List_1, choosing the first topic %T1%.
PASS control into Loop_2.
CONTINUE in Loop_1.
If the end of List_1 is reached, EXIT with failure.
(Loop_2) Select into List_2 all topics > %T1% with at least two
stories unrelated to %T1%.
Iterate through List_2, choosing the second topic %T2%.
If topic %T1% still has at least two stories unrelated
to %T2%, PASS control into Loop_3.
CONTINUE in Loop_2.
If the end of List_2 is reached, go BACK to Loop_1.
(Loop 3) Select into List_3 all topics > %T2% with at least two
stories unrelated to %T1% or %T2%.
Iterate through List_3, choosing the third topic %T3%.
If topic %T1% still has at least two stories unrelated
to %T2% or %T3%,
and topic %T2% still has at least two stories unrelated
to %T1% or %T3%, PASS control into Loop_4.
CONTINUE in Loop_3.
If the end of List_3 is reached, go BACK to Loop_2.
(Loop 4) Select into List_4 all topics > %T3% with at least two
stories unrelated to %T1%, %T2%, or %T3%.
Iterate through List_4, choosing the fourth topic %T4%.
If topic %T1% still has at least two stories unrelated
to %T2%, %T3%, or %T4%,
and topic %T2% still has at least two stories unrelated
to %T1%, %T3%, or %T4%,
and topic %T3% still has at least two stories unrelated
to %T1%, %T2%, or %T4%, PASS control into Loop_5.
CONTINUE in Loop_4.
If the end of List_4 is reached, go BACK to Loop_3.
(Loop 5) Select into List_5 all topics > %T4% with at least two
stories unrelated to %T1%, %T2%, %T3%, or %T4%.
Iterate through List_5, choosing the fifh topic %T5%.
If topic %T1% still has at least two stories unrelated
to %T2%, %T3%, %T4%, or %T5%,
and topic %T2% still has at least two stories unrelated
to %T1%, %T3%, %T4%, or %T5%,
and topic %T3% still has at least two stories unrelated
to %T1%, %T2%, %T4%, or %T5%,
and topic %T4% still has at least two stories unrelated
to %T1%, %T2%, %T3%, or %T5%, EXIT with success
returning five topics %T1%, %T2%, %T3%, %T4%, and %T5%.
CONTINUE in Loop_5.
If the end of List_5 is reached, go BACK to Loop_4.
在每个嵌套循环的开头使用“选择”是为了唤起
SQL 查询实现大部分逻辑的可能性。为了
例如,最外面的循环基本上只是获取结果集
这个查询:
SELECT TS1.TopicID, Count(*)
From TopicStory TS1
Group By TS1.TopicID
Having Count(*) > 1
内部循环的对应列表可以类似地构造
通过 SQL 查询,具体取决于在
外循环。为了说明没有不必要的重复让我们跳
直接到最里面的循环并为 List_5 提供适当的查询:
SELECT TS5.TopicID, Count(*)
From TopicStory TS5
Where TS5.TopicID > %T4%
and NOT EXISTS ( SELECT *
From TopicStory TSX
Where TSX.TopicID in (%T1%,%T2%,%T3%,%T4%)
and TSX.StoryID = TS5.StoryID
)
Group By TS5.TopicID
Having Count(*) > 1
然后检查 List_5 中的 %T5% 是否生成
主题 %T1% 的至少两个故事的计数:
SELECT Count(*)
From TopicStory TZ1
Where TZ1.TopicID = %T1%
and NOT EXISTS ( SELECT *
From TopicStory TX1
Where TX1.StoryID = TZ1.StoryID
and TX1.TopicID in (%T2%,%T3%,%T4%,TS5.TopicID)
)
并且比照其他先前的主题选择。
虽然它可能会不可接受地降低性能,但附加的逻辑
用于限制与 %T5% 相关的主题(以便较早的主题选择
仍然保留至少两个故事选择)可以推送到一个查询中。
它看起来像这样:
/*
Given %T1%, %T2%, %T3$, and %T4% from queries above, find all topics %T5% > %T4%
with at least 2 stories not related to %T1%, %T2%, %T3%, or %T4% and such that
%T1% still has at least 2 stories not related to %T2%, %T3%, %T4%, or %T5% and
%T2% still has at least 2 stories not related to %T1%, %T3%, %T4%, or %T5% and
%T3% still has at least 2 stories not related to %T1%, %T2%, %T4%, or %T5% and
%T4% still has at least 2 stories not related to %T1%, %T2%, %T3%, or %T5%
*/
SELECT TS5.TopicID, Count(*)
From TopicStory TS5
Where TS5.TopicID > %T4%
and NOT EXISTS ( SELECT *
From TopicStory TSX
Where TSX.TopicID in (%T1%,%T2%,%T3%,%T4%)
and TSX.StoryID = TS5.StoryID
)
and ( SELECT Count(*)
From TopicStory TZ1
Where TZ1.TopicID = %T1%
and NOT EXISTS ( SELECT *
From TopicStory TX1
Where TX1.StoryID = TZ1.StoryID
and TX1.TopicID in (%T2%,%T3%,%T4%,TS5.TopicID)
)
) > 1
and ( SELECT Count(*)
From TopicStory TZ2
Where TZ2.TopicID = %T2%
and NOT EXISTS ( SELECT *
From TopicStory TX2
Where TX2.StoryID = TZ2.StoryID
and TX2.TopicID in (%T1%,%T3%,%T4%,TS5.TopicID)
)
) > 1
and ( SELECT Count(*)
From TopicStory TZ3
Where TZ3.TopicID = %T3%
and NOT EXISTS ( SELECT *
From TopicStory TX3
Where TX3.StoryID = TZ3.StoryID
and TX3.TopicID in (%T1%,%T2%,%T4%,TS5.TopicID)
)
) > 1
and ( SELECT Count(*)
From TopicStory TZ4
Where TZ4.TopicID = %T4%
and NOT EXISTS ( SELECT *
From TopicStory TX3
Where TX3.StoryID = TZ3.StoryID
and TX3.TopicID in (%T1%,%T2%,%T3%,TS5.TopicID)
)
) > 1
Group By TS5.TopicID
Having Count(*) > 1
MySQL 的功能集是一项正在进行的工作,因此可以想象是一个高效的
在存储过程中实现是可能的,其中游标可以采用
主题列表的作用。但是,如果
“游标”是外部管理的列表(例如在 PHP 中)和数据库查询
尽可能保持简单。