用于标识分组值或对象的视图

Zia*_*iad 9 sql sql-server sql-server-2008

在此输入图像描述

作为一个例子,我有5个对象.物体是彼此结合或相邻的红点.换句话说,X + 1或X-1或Y + 1或Y-1.

在此输入图像描述

我需要创建一个MS SQL VIEW,其中包含每个对象的第一个XY坐标,如:

    X,Y
=======
1.  1,1
2.  1,8
3.  4,3
4.  5,7
5.  6,5
Run Code Online (Sandbox Code Playgroud)

我无法弄清楚如何在VIEW中对它进行分组(不使用存储过程).任何人都有任何想法会有很大的帮助.谢谢

Ric*_*iwi 11

另一个答案已经很长了,所以我将其保留原样.这个答案更好,更简单,也更正确,而另一个有一些边缘情况会产生错误答案 - 我将把这个练习留给读者.

注意:为清晰起见,添加了换行符.整个块是单个查询

;with Walker(StartX,StartY,X,Y,Visited) as (
    select X,Y,X,Y,CAST('('+right(X,3)+','+right(Y,3)+')' as Varchar(Max))
    from puzzle
    union all
    select W.StartX,W.StartY,P.X,P.Y,W.Visited+'('+right(P.X,3)+','+right(P.Y,3)+')'
    from Walker W
    join Puzzle P on
      (W.X=P.X   and W.Y=P.Y+1 OR   -- these four lines "collect" a cell next to
       W.X=P.X   and W.Y=P.Y-1 OR   -- the current one in any direction
       W.X=P.X+1 and W.Y=P.Y   OR
       W.X=P.X-1 and W.Y=P.Y)
      AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'
)
select X, Y, Visited
from
(
    select W.X, W.Y, W.Visited, rn=row_number() over (
                                   partition by W.X,W.Y
                                   order by len(W.Visited) desc)
    from Walker W
    left join Walker Other
        on Other.StartX=W.StartX and Other.StartY=W.StartY
            and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
    where Other.X is null
) Z
where rn=1
Run Code Online (Sandbox Code Playgroud)

第一步是设置一个"walker"递归表表达式,该表达式将从每个单元格开始并尽可能地移动而无需回溯任何步骤.确保不重新访问单元格是通过使用被访问列来完成的,该列存储从每个起始点访问过的每个单元格.特别是,这种情况AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'拒绝它已经访问过的单元格.

要了解其余的工作原理,您需要在CTE之后运行"通过StartX,StartY从Walker订单选择*"来查看"Walker"CTE生成的结果.具有5个细胞的"片段"出现在至少5组中,每组具有不同的(StartX,StartY)组,但每组具有所有5个(X,Y)具有不同"访问"路径的片段.

子查询(Z)使用LEFT JOIN + IS NULL将组清除到包含"第一个XY坐标"的每个组中的单个行,由条件定义

     Other.StartX=W.StartX and Other.StartY=W.StartY
        and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
Run Code Online (Sandbox Code Playgroud)

意图是从(StartX,StartY)开始可以访问的每个单元格,以与同一组中的每个其他单元格进行比较,并找到NO OTHER单元格位于更高行的单元格,或者它们是否在同一行,就在这个单元格的左边.然而,这仍然给我们带来了太多结果.在(3,4)和(4,4)处只考虑一个2格的部分:

StartX  StartY  X   Y   Visited
3       4       3   4   (3,4)          ******
3       4       4   4   (3,4)(4,4)
4       4       4   4   (4,4)
4       4       3   4   (4,4)(3,4)     ******
Run Code Online (Sandbox Code Playgroud)

用(3,4)的"第一XY坐标"保留2行,标记为******.我们只需要一行,所以我们使用Row_Number,因为我们编号,我们也可以选择最长的Visited路径,这将为我们提供尽可能多的单元格.

最终外部查询仅从每个相似(X,Y)组中获取第一行(RN = 1).


要显示每个部分的所有单元格,请更改该行

select X, Y, Visited
Run Code Online (Sandbox Code Playgroud)

在中间到

select X, Y, (
    select distinct '('+right(StartX,3)+','+right(StartY,3)+')'
    from Walker
    where X=Z.X and Y=Z.Y
    for xml path('')
    ) PieceCells
Run Code Online (Sandbox Code Playgroud)

哪个给出了这个输出

X           Y           PieceCells
1           1           (1,1)(2,1)(2,2)(3,2)
3           4           (3,4)(4,4)
5           6           (5,6)
7           5           (7,5)(8,5)(9,5)
8           1           (10,1)(8,1)(8,2)(9,1)(9,2)(9,3)
Run Code Online (Sandbox Code Playgroud)