基于集合的高效实现:为左列每个值分配右列唯一未使用的最小值
基于集合的高效实现:为左列每个值分配右列唯一未使用的最小值
嗨,我太懂你不想用游标的心情了——逐行处理的游标在数据量上去之后,速度简直慢到让人抓狂。咱们先把需求再理清楚一遍:给每个唯一的Col1值,匹配Col2里最小的、还没被其他Col1占用的选项,最终结果里Col2不能有重复,而且要用上纯集合操作来实现高效运行。
你之前试的select Col1, min(Col2)之所以不行,核心问题是它只盯着单个Col1的最小Col2,完全没考虑这个值是不是已经被其他Col1用掉了,所以才会出现多个Col1都拿到X的错误结果。
下面给你两种高效的纯集合解法,都比游标快得多:
方法一:递归CTE模拟贪心算法
这个思路和你用游标手动分配的逻辑一样,但用集合操作实现,性能提升非常明显:
WITH SortedCol1 AS ( -- 先给所有唯一的Col1排个顺序,这里按Col1本身的字母/数值序排序 SELECT DISTINCT Col1, ROW_NUMBER() OVER (ORDER BY Col1) AS RowNum FROM YourTable ), RecursiveMatch AS ( -- 初始步骤:处理第一个Col1,给它分配自己可选的最小Col2 SELECT sc1.Col1, (SELECT TOP 1 t.Col2 FROM YourTable t WHERE t.Col1 = sc1.Col1 ORDER BY t.Col2) AS Col2, sc1.RowNum FROM SortedCol1 sc1 WHERE sc1.RowNum = 1 UNION ALL -- 递归步骤:处理下一个Col1,选它可选的、没被前面Col1用过的最小Col2 SELECT sc1.Col1, (SELECT TOP 1 t.Col2 FROM YourTable t WHERE t.Col1 = sc1.Col1 AND t.Col2 NOT IN (SELECT Col2 FROM RecursiveMatch WHERE RowNum < sc1.RowNum) ORDER BY t.Col2) AS Col2, sc1.RowNum FROM SortedCol1 sc1 JOIN RecursiveMatch rm ON sc1.RowNum = rm.RowNum + 1 ) -- 最终结果按Col1的顺序输出 SELECT Col1, Col2 FROM RecursiveMatch ORDER BY RowNum;
逻辑解释
SortedCol1:给每个唯一的Col1生成一个连续的序号,确定处理顺序;- 初始递归:先给第一个
Col1分配它能选的最小Col2; - 递归迭代:每处理一个
Col1,都从它的可选Col2里挑出没被前面的Col1占用的最小值; - 最终输出所有匹配结果。
在你的示例数据里,这个方法会先给A分配X,然后给B分配剩下的最小Y,最后给C分配唯一可选的Z,完全符合预期。
方法二:窗口函数序号匹配法
如果你的数据库支持窗口函数(现在主流数据库都支持),也可以用序号匹配的方式,逻辑更简洁:
WITH RankedCol1 AS ( -- 给唯一Col1按顺序分配序号 SELECT DISTINCT Col1, ROW_NUMBER() OVER (ORDER BY Col1) AS Col1Rank FROM YourTable ), RankedCol2 AS ( -- 给唯一Col2按升序分配序号 SELECT DISTINCT Col2, ROW_NUMBER() OVER (ORDER BY Col2) AS Col2Rank FROM YourTable ), Col1Options AS ( -- 给每个Col1的可选Col2按全局Col2的序号排序,生成该Col1内的选项排名 SELECT t.Col1, t.Col2, ROW_NUMBER() OVER (PARTITION BY t.Col1 ORDER BY rc2.Col2Rank) AS OptionRank FROM YourTable t JOIN RankedCol2 rc2 ON t.Col2 = rc2.Col2 ) -- 匹配Col1的全局序号和它的选项排名,确保每个Col2只被用一次 SELECT rc1.Col1, co.Col2 FROM RankedCol1 rc1 JOIN Col1Options co ON rc1.Col1 = co.Col1 AND rc1.Col1Rank = co.OptionRank ORDER BY rc1.Col1Rank;
逻辑解释
RankedCol1和RankedCol2分别给唯一的Col1和Col2生成全局序号;Col1Options给每个Col1的可选Col2按全局大小排序,生成该Col1内的选项排名;- 最后通过
Col1的全局序号和它的选项排名匹配,保证每个Col2只会被一个Col1选中(因为序号是一一对应的)。
这个方法完全没有递归,纯窗口函数操作,性能非常出色,适合数据量较大的场景。
两种方法都不需要游标,都是纯集合操作,速度能达到游标的上千倍,完美符合你的需求。
内容来源于stack exchange
相关产品推荐
相关产品推荐

