9

ここに画像の説明を入力してください

例として、5つのオブジェクトがあります。オブジェクトは、互いに結合または隣接する赤い点です。言い換えれば、X+1またはX-1またはY+1またはY-1です。

ここに画像の説明を入力してください

次のような各オブジェクトの最初のXY座標を含むMSSQLVIEWを作成する必要があります。

    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

最初のステップは、すべてのセルで開始し、ステップをたどることなく可能な限り移動する「ウォーカー」再帰テーブル式を設定することです。セルが再訪問されないようにするために、すべての開始点から訪問された各セルを格納する訪問済み列を使用します。特に、この条件は、AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'すでにアクセスしたセルを拒否します。

残りがどのように機能するかを理解するには、CTEの後に「StartX、StartYによるウォーカーオーダーから*を選択」を実行して「ウォーカー」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)から開始してアクセスできる各セルを対象とし、同じグループ内の他のセルと比較し、上位の行に他のセルがないセル、またはそれらが上にあるセルを見つけることを目的としています。同じ行は、このセルの左側にあります。ただし、これでも結果が多すぎます。(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行が残り******ます。必要な行は1つだけなので、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

座標が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......
............

このクエリを4段階で実行すると、TopMost行の左端のセルとして定義した場合に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 に答える
0

Ok。その少し難しい。しかし、いずれにせよ、この問題はもっと簡単な方法では解決できないと確信しています。したがって、テーブルがあります。

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

そしてここにselectクエリがあります

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 に答える