9

在此处输入图像描述

例如,我有 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

我不知道如何在VIEW中对它进行分组(不使用存储过程)。任何人有任何想法都会有很大帮助。谢谢

4

3 回答 3

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 之后运行“Select * from Walker order by StartX, StartY”来查看“Walker”CTE 生成的结果。具有 5 个单元格的“片段”至少出现在 5 个组中,每个组都有不同的(StartX,StartY),但每个组都有(X,Y)具有不同“访问”路径的所有 5 个片段。

子查询 (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) 开始访问的每个单元格,与同一组中的其他单元格进行比较,并找到没有其他单元格在较高行上的单元格,或者它们是否在同一行,位于该单元格的左侧。然而,这仍然给我们留下了太多的结果。仅考虑 (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)     ******

2 行保留 (3,4) 的“第一个 XY 坐标”,用 标记******。我们只需要一行,所以我们使用 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)
于 2012-09-23T07:57:48.143 回答
0

好的。它有点难。但无论如何,我确信以更简单的方式无法解决这个问题。所以我们有表:

CREATE Table Tbl1(Id int, X int, Y int)
    INSERT INTO Tbl1
    SELECT 1,1,1 UNION ALL
    SELECT 2,1,2 UNION ALL
    SELECT 3,1,8 UNION ALL
    SELECT 4,1,9 UNION ALL
    SELECT 5,1,10 UNION ALL
    SELECT 6,2,2 UNION ALL
    SELECT 7,2,3 UNION ALL
    SELECT 8,2,8 UNION ALL
    SELECT 9,2,9 UNION ALL
    SELECT 10,3,9 UNION ALL
    SELECT 11,4,3 UNION ALL
    SELECT 12,4,4 UNION ALL
    SELECT 13,5,7 UNION ALL
    SELECT 14,5,8 UNION ALL
    SELECT 15,5,9 UNION ALL
    SELECT 16,6,5

这是选择查询

with cte1 as 
/*at first we make recursion to define groups of filled adjacent cells*/
/*as output of cte we have a lot of strings like <X>cell(1)X</X><Y>cell(1)Y</Y>...<X>cell(n)X</X><Y>cell(n)Y</Y>*/
(
SELECT id,X,Y,CAST('<X>'+CAST(X as varchar(10))+'</X><Y>'+CAST(Y as varchar(10))+'</Y>' as varchar(MAX)) info
FROM Tbl1

UNION ALL

SELECT b.id,a.X,a.Y,CAST(b.info + '<X>'+CAST(a.X as varchar(10))+'</X><Y>'+CAST(a.Y as varchar(10))+'</Y>' as varchar(MAX))
FROM Tbl1 a JOIN cte1 b 
ON ((((a.X=b.X+1) OR (a.X=b.X-1)) AND a.Y=b.Y) OR (((a.Y=b.Y+1) OR (a.Y=b.Y-1)) AND a.X=b.X)) 
AND a.id<>b.id
AND
b.info NOT LIKE
('%'+('<X>'+CAST(a.X as varchar(10))+'</X><Y>'+CAST(a.Y as varchar(10))+'</Y>')+'%')
),

cte2 as
/*In this query, we select only the longest sequence of cell connections (first filter)*/
/*And we convert the string to a new standard (x,y | x,y | x,y |...| x,y) (for further separation)*/
(
SELECT *, ROW_NUMBER()OVER(ORDER BY info) cellGroupId
FROM(
    SELECT REPLACE(REPLACE(REPLACE(REPLACE(info,'</Y><X>','|'),'</X><Y>',','),'<X>',''),'</Y>','') info
    FROM(
        SELECT info, MAX(LEN(info))OVER(PARTITION BY id)maxlen FROM cte1
        ) AS tmpTbl
    WHERE maxlen=LEN(info)
)AS tmpTbl
),


cte3 as
/*In this query, we separated strings like (x,y | x,y | x,y |...| x,y) to many (x,y)*/
(
SELECT cellGroupId, CAST(LEFT(XYInfo,CHARINDEX(',',XYInfo)-1) as int) X, CAST(RIGHT(XYInfo,LEN(XYInfo)-CHARINDEX(',',XYInfo)) as int) Y
FROM(
    SELECT cellGroupId, tmpTbl2.n.value('.','varchar(MAX)') XYinfo
    FROM 
        (SELECT CAST('<r><c>' + REPLACE(info,'|','</c><c>')+'</c></r>' as XML) n, cellGroupId FROM cte2) AS tmpTbl1
        CROSS APPLY n.nodes('/r/c') tmpTbl2(n)
    ) AS tmpTbl
),

cte4 as
/*In this query, we finally determined group of individual objects*/
(
SELECT cellGroupId,X,Y
FROM(
    SELECT cellGroupId,X,Y,ROW_NUMBER()OVER(PARTITION BY X,Y ORDER BY cellGroupId ASC)rn
    FROM(
        SELECT *, 
        MAX(SumOfAdjacentCellsByGroup)OVER(PARTITION BY X,Y) Max_SumOfAdjacentCellsByGroup_ByXY /*calculated max value of <the sum of the cells in the group> by each cell*/
        FROM(
            SELECT *, SUM(1)OVER(PARTITION BY cellGroupId) SumOfAdjacentCellsByGroup /*calculated the sum of the cells in the group*/
            FROM cte3
            )AS TmpTbl  
        )AS TmpTbl
    /*We got rid of the subgroups (i.e. [(1,2)(2,2)(2,3)] its subgroup of [(1,2)(1,1)(2,2)(2,3)])*/
    /*it was second filter*/
    WHERE SumOfAdjacentCellsByGroup=Max_SumOfAdjacentCellsByGroup_ByXY 
)AS TmpTbl
/*We got rid of the same groups (i.e. [(1,1)(1,2)(2,2)(2,3)] its same as [(1,2)(1,1)(2,2)(2,3)])*/
/*it was third filter*/
WHERE rn=1
)


SELECT X,Y /*result*/
FROM(SELECT a.X,a.Y, ROW_NUMBER()OVER(PARTITION BY cellGroupId ORDER BY id)rn
FROM cte4 a JOIN Tbl1 b ON a.X=b.X AND a.Y=b.Y)a /*connect back*/
WHERE rn=1 /*first XY coordinate*/
于 2012-09-22T20:23:33.330 回答
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)

然后,此查询以板的形式显示您的拼图(在 SQL Management Studio 中以文本模式运行)

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)
于 2012-09-23T00:05:40.600 回答