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

时间:2012-09-22 18:03:41

标签: sql sql-server sql-server-2008

  

enter image description here

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

  

enter image description here

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

    X,Y
=======
1.  1,1
2.  1,8
3.  4,3
4.  5,7
5.  6,5

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

2 个答案:

答案 0 :(得分: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

第一步是设置一个“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))

意图是从(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)     ******

使用{3,4}的“第一个XY坐标”保留2行,标有******。我们只需要一行,所以我们使用Row_Number,因为我们编号,我们也可以选择最长的Visited路径,这会给我们多个中的单元格。我们可以得到一块。

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

<小时/> 要显示每个部分的所有单元格,请更改

select X, Y, Visited

在中间

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

给出了这个输出

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)

答案 1 :(得分:0)

假设您的坐标以X,Y形式存储,如下所示:

CREATE Table Puzzle(
  id int identity, Y int, X int)
INSERT INTO Puzzle VALUES
  (1,1),(1,2),(1,8),(1,9),(1,10),
  (2,2),(2,3),(2,8),(2,9),
  (3,9),
  (4,3),(4,4),
  (5,7),(5,8),(5,9),
  (6,5)

然后,此查询将以棋盘形式显示您的Puzzle(在SQL Management Studio中以TEXT模式运行)

SELECT (
  SELECT (
    SELECT CASE WHEN EXISTS (SELECT *
                             FROM Puzzle T
                             WHERE T.X=X.X and T.Y=Y.Y)
                THEN 'X' ELSE '.' END
    FROM (values(0),(1),(2),(3),(4),(5),
                (6),(7),(8),(9),(10),(11)) X(X)
    ORDER BY X.X
    FOR XML PATH('')) + Char(13) + Char(10)
  FROM (values(0),(1),(2),(3),(4),(5),(6),(7)) Y(Y)
  ORDER BY Y.Y
  FOR XML PATH(''), ROOT('a'), TYPE
).value('(/a)[1]','varchar(max)')

它给你这个

............
.XX.....XXX.
..XX....XX..
.........X..
...XX.......
.......XXX..
.....X......
............

如果您将TopLeft单元格定义为TopMost行的最左侧单元格,则分4个阶段完成的查询将为您提供TopLeft单元格的结果。

-- the first table expression joins cells together on the Y-axis
;WITH FlattenOnY(Y,XLeft,XRight) AS (
-- start with all pieces
select Y,X,X
from puzzle
UNION ALL
-- keep connecting rightwards from each cell as far as possible
select B.Y,A.XLeft,B.X
from FlattenOnY A
join puzzle B on A.Y=B.Y and A.XRight+1=B.X
)

-- the second table expression flattens the results from the first, so that
-- it represents ALL the start-end blocks on each row of the Y-axis
,YPieces(Y,XLeft,XRight) as (
-- 
select Y,XLeft,Max(XRight)
from(
select Y,Min(XLeft)XLeft,XRight
from FlattenOnY
group by XRight,Y)Z
group by XLeft,Y
)
-- here, select * from YPieces will return the "blocks" such as 
-- Row 1: 1-2 & 8-10
-- Row 2: 2-3  (equals Y,XLeft,XRight of 2,2,3)
-- etc

-- the third expression repeats the first, except it now combines on the X-axis
,FlattenOnX(Y,XLeft,CurPieceXLeft,CurPieceXRight,CurPieceY) AS (
-- start with all pieces
select Y,XLeft,XLeft,XRight,Y
from YPieces
UNION ALL
-- keep connecting rightwards from each cell as far as possible
select A.Y,A.XLeft,B.XLeft,B.XRight,B.Y
from FlattenOnX A
join YPieces B on A.CurPieceY+1=B.Y and A.CurPieceXRight>=B.XLeft and B.XRight>=A.CurPieceXLeft
)

-- and again we repeat the 2nd expression as the 4th, for the final pieces
select Y,XLeft X
from (
select *, rn2=row_number() over (
    partition by Y,XLeft
    order by CurPieceY desc)
from (
  select *, rn=row_number() over (
      partition by CurPieceXLeft, CurPieceXRight, CurPieceY
      order by Y)
  from flattenOnX
) Z1
where rn=1) Z2
where rn2=1

结果是

Y           X
----------- -----------
1           1
1           8
4           3
5           7
6           5

或者你的平面形式是这样的吗?如果是的话,给我们一个喊叫,我会重做解决方案

create table Puzzle (
  row int,
  [0] bit, [1] bit, [2] bit, [3] bit, [4] bit, [5] bit,
  [6] bit, [7] bit, [8] bit, [9] bit, [10] bit, [11] bit
)
insert Puzzle values
(0,0,0,0,0,0,0,0,0,0,0,0,0),
(1,0,1,1,0,0,0,0,0,1,1,1,0),
(2,0,0,1,1,0,0,0,0,1,1,0,0),
(3,0,0,0,0,0,0,0,0,0,1,0,0),
(4,0,0,0,1,1,0,0,0,0,0,0,0),
(5,0,0,0,0,0,0,0,1,1,1,0,0),
(6,0,0,0,0,0,1,0,0,0,0,0,0),
(7,0,0,0,0,0,0,0,0,0,0,0,0)