考虑一些简单的过程 P,它从一组 (super_id, user_id) 中创建可能路径的矩形。
长度为 N 的路径有 N 层深,并链接 (N-1) 个关系。
每个级别的值是否与该级别不同?
- 没有?与实际路径相比,P 将找到循环、交叉路径和环绕路径。环绕是当实际路径级别 > 1 的节点被“发现”为级别 = 1 节点时。
- 是吗? P 将找到路径、交叉路径和环绕路径。额外的数据限制或规则可以帮助消除
考虑 4 个级别值不明确的简单路径:
data path(keep=L1-L4) rels(keep=super_id user_id);
array L(4);
input L(*);
output path;
super_id = L(1);
do i = 2 to dim(L);
user_id = L(i);
output rels;
super_id = user_id;
end;
datalines;
1 3 1 4
1 5 1 4
2 3 2 3
1 2 3 4
run;
只有12条关系数据。这些对存在的路径和它们存在的级别都不是未知的:
1 : 1 3
2 : 3 1
3 : 1 4
4 : 1 5
5 : 5 1
6 : 1 4
7 : 2 3
8 : 3 2
9 : 2 3
10 : 1 2
11 : 2 3
12 : 3 4
一个显式的 2 阶段查询,用于在关系中组装 4 级路径。如果代码有效,则可以将其抽象为宏编码。
proc sql;
* RELS cross RELS, extensive i/o;
* get on the induction ladder;
create table ITER_1 as
select distinct
S.super_id as L3 /* parent^2 */
, S.user_id as L2 /* parent */
, U.user_id as L1 /* leaf */
from RELS U
cross join RELS S
where S.user_id = U.super_id
order by L3, L2, L1
;
* ITER_1 cross RELS, little less extensive i/o;
* if you see the inductive variation you can macroize it;
create table ITER_2 as
select distinct
S.super_id as L4 /* parent^3 */
, U.L3 /* parent^2 */
, U.L2 /* parent */
, U.L1 /* leaf */
from ITER_1 U
cross join RELS S
where S.user_id = U.L3
order by L4, L3, L2, L1
;
quit;
上述汇编器没有对身份知识,不能限制离散对的路径。所以会有循环、交叉和换行。
找到的路径(一些解释)
1 : 1 2 3 1 path 4 L3 xover to path 1 L2
2 : 1 2 3 2 path 4 L3 xover to path 3 L2
3 : 1 2 3 4 actual
4 : 1 3 1 2 path 1 L3 xover to path 4 L1
5 : 1 3 1 3
6 : 1 3 1 4 actual
7 : 1 3 1 5
8 : 1 3 2 3
9 : 1 5 1 2
10 : 1 5 1 3
11 : 1 5 1 4 actual
12 : 1 5 1 5
13 : 2 3 1 2
14 : 2 3 1 3
15 : 2 3 1 4
16 : 2 3 1 5
17 : 2 3 2 3 actual is actually a cycler too
18 : 3 1 2 3
19 : 3 1 3 1
20 : 3 1 3 2
21 : 3 1 3 4
22 : 3 1 5 1
23 : 3 2 3 1
24 : 3 2 3 2
25 : 3 2 3 4
26 : 5 1 2 3
27 : 5 1 3 1
28 : 5 1 3 2
29 : 5 1 3 4
30 : 5 1 5 1 path 2 L3 cycled to path 2 L1
如果每个关系级别的 id 在任何其他级别都找不到,则隐式消除循环。因为没有路径标识信息,所以不能消除交叉。环绕也一样。
更复杂的 SQL 可以确保找到的“路径”中的每个关系只出现一次,并且路径的内容不同。根据实际数据,您可能仍有大量错误路径。
高度规则的代码适合宏化,但实际的 SQL 运行时间高度依赖于实际数据和 REL 数据集索引。
proc sql;
create table ITER_1 as
select
L3 /* parent^2 */
, L2 /* parent */
, L1 /* leaf */
, R1
, R2
from
(
select distinct
S.super_id as L3 /* parent^2 */
, S.user_id as L2 /* parent */
, U.user_id as L1 /* leaf */
, U.row_id as R1
, S.row_id as R2
, monotonic() as seq
from RELS U
cross join RELS S
where S.user_id = U.super_id
and S.row_id < U.row_id /* triangular constraint allowed due to symmetry */
)
group by L3, L2, L1
having seq = min(seq)
order by L3, L2, L1
;
create table ITER_2 as
select
L4 /* parent^3 */ format=6.
, L3 /* parent^2 */ format=6.
, L2 /* parent */ format=6.
, L1 /* leaf */ format=6.
, R1 format=6.
, R2 format=6.
, R3 format=6.
from
(
select distinct
S.super_id as L4 /* parent^3 */ format=6.
, U.L3 /* parent^2 */ format=6.
, U.L2 /* parent */ format=6.
, U.L1 /* leaf */ format=6.
, U.R1 format=6.
, U.R2 format=6.
, S.row_id as R3 format=6.
, monotonic() as seq
from ITER_1 U
cross join RELS S
where S.user_id = U.L3
and S.row_id ne R1
and S.row_id ne R2
)
group by L4, L3, L2, L1
having seq = min(seq)
order by L4, L3, L2, L1
;
退出;
对 NULL 项的最后调整将需要更多 SQL。
是否可以在不需要 NULL 的情况下处理发现的层次结构?带有 BY 处理的 DATA Step SET 可以使用 LAST 检测关卡的结束。