6.1 KiB
Set Operations
文档类型:语法主线 是否可直接用于生成代码:是 是否含已验证可执行示例:是 是否含已验证反例:是 遇到不确定时跳转到:14_resultset_and_filters.md、13_matrix_and_collections.md、12_pitfalls.md
手册位置:第 25 篇,共 32 篇。上一篇:24_builtin_runtime_objects.md。下一篇:26_matrix_deep_dive.md。
这一篇只讲集合运算本身:in、sqlin、union2、intersect、minus、outersect。它和 14_resultset_and_filters.md 的分工很明确: 这一篇讲“去重后的集合关系”,过滤页讲“按原结果集逐行保留或排除”。
这一篇解决什么问题
回答“某个元素是否在数组里、某组元素是否构成子集、某一行是否存在于结果集中,以及两个结果集如何做并集、交集、差集和对称差集”。
必须记住的规则
in/not in处理的是元素存在关系,以及左侧为数组时的子集关系。sqlin/not sqlin处理的是行存在关系;左侧要当成一整行去匹配右侧结果集。union2、intersect、minus、outersect都按“行”运算,而不是按单元格逐个运算。- 当前解释器支持
not in和not sqlin这种否定写法。 - 集合运算结果会折叠重复行;如果需求是保留重复记录,不要用这一篇的算符,改看 14_resultset_and_filters.md。
- 当数据本身就是一维数组时,按行集合运算和按元素集合运算是一致的。
已验证语法
in 与 not in
in 既可以判断单个元素是否存在,也可以判断左侧数组是否是右侧结果集的子集:
代码块身份:已验证可执行示例
program test;
begin
WriteLn(1 in array(1, 2, 2));
WriteLn(1 in array(0, 2));
WriteLn(1 in array((1), (2)));
WriteLn(array(1, 2) in array(1, 2, 3, 4));
WriteLn(array(1, 3) in array((1, 2), (3, 4)));
WriteLn(array(1, 2) in array(1));
WriteLn(1 not in array(0, 2));
end.
已验证运行结果:
1 in array(1, 2, 2)输出11 in array(0, 2)输出01 in array((1), (2))输出1array(1, 2) in array(1, 2, 3, 4)输出1array(1, 3) in array((1, 2), (3, 4))输出1array(1, 2) in array(1)输出01 not in array(0, 2)输出1
sqlin 与 not sqlin
sqlin 改成按整行判断左侧是否存在于右侧结果集中:
代码块身份:已验证可执行示例
program test;
begin
WriteLn(1 sqlin array(1, 2));
WriteLn(array(1, 2) sqlin array(1, 2, 3));
WriteLn(array(1, 2) sqlin array((1, 2), (3, 4)));
WriteLn(array(5, 6) not sqlin array((1, 2), (3, 4)));
end.
已验证运行结果:
1 sqlin array(1, 2)输出1array(1, 2) sqlin array(1, 2, 3)输出0array(1, 2) sqlin array((1, 2), (3, 4))输出1array(5, 6) not sqlin array((1, 2), (3, 4))输出1
可以把两者的差异直接记成一句话:
in看元素或子集sqlin看整行
行集合的并、交、差、对称差
下面这组最小例子同时验证了“按行运算”和“结果会折叠重复行”:
代码块身份:已验证可执行示例
program test;
begin
A := array((1, 2), (1, 2), (2, 3));
B := array((1, 2), (3, 4));
U := A union2 B;
I := A intersect B;
M := A minus B;
O := A outersect B;
end.
已验证运行结果:
A union2 B共有三行:(1,2)、(2,3)、(3,4)A intersect B只有一行:(1,2)A minus B只有一行:(2,3)A outersect B有两行:(2,3)、(3,4)A原本有两行相同的(1,2),但union2/intersect的结果都只保留一份,说明集合运算会折叠重复行
如果你需要看更大的四列结果集例子,当前解释器下也已经实测过:
array((1,2,3,4),(2,3,4,5),(1,1,1,1)) union2 array((1,2,3,4),(3,4,5,6),(2,2,2,2))返回array((1,2,3,4),(2,3,4,5),(1,1,1,1),(3,4,5,6),(2,2,2,2))- 同一组输入下:
intersect返回array((1,2,3,4)) - 同一组输入下:
minus返回array((2,3,4,5),(1,1,1,1)) - 同一组输入下:
outersect返回array((2,3,4,5),(1,1,1,1),(3,4,5,6),(2,2,2,2))
和过滤运算的区别
- 集合运算先把数据当成“行集合”来看,再做包含、并交差。
- 过滤运算先保留“原结果集里的每一条命中记录”;因此重复行会保留下来。
- 你要的是“集合关系”,看这一篇。
- 你要的是“从原表里筛出哪些行”,看 14_resultset_and_filters.md。
最小可编译示例
如果你只想先记住最短写法,从这里开始:
代码块身份:已验证可执行示例
Matched := 1 in array(1, 2, 3);
常见误写
- 把
in和sqlin当成同一个概念。 - 期待
union2保留重复行。 - 用集合运算去做“保留原始重复记录”的过滤任务。
- 把二维结果集默认当成“按元素逐个比较”的集合运算。
代码块身份:反例 / 不可照写
Matched := array(1, 2) in array((1, 2), (3, 4));
上面这类写法不要直接理解成“左边这一整行是否存在于右边”。当前手册里,整行存在判断统一写成 sqlin,避免把 in 的子集语义和行匹配语义混在一起。
代码块身份:反例 / 不可照写
OnlyLeft := A minus B;
如果你的任务要求保留 A 里重复出现的命中次数,上面这种集合差集就不是合适工具,因为它会把结果当成集合而不是原始记录流。
跳转指引
- 回看表达式和逻辑运算:见 07_expressions_and_operators.md
- 看数组、嵌套数组和子矩阵:见 13_matrix_and_collections.md
- 看结果集过滤:见 14_resultset_and_filters.md
- 进入矩阵深水专题:见 26_matrix_deep_dive.md