SQL查询从二维数据表中获取邻居

时间:2010-12-01 22:24:27

标签: sql database algorithm postgresql

我有一张表,其中包含世界地图的分区。通过将所有区域分成矩形来完成分区,称为“tile”。像往常一样,矩形有左下角和右上角,两者都是浮点坐标。

任务是:

对于每个磁贴,将其邻居放在右边,将其邻居放到顶部,就像一组{tile_id, id_of_top_neighbour, id_of_right_n}一样。

通过图块A的右邻居意味着这样的图块B,其具有与A的max_x坐标最接近的min_x坐标,而y是相同的。

表格说明:

integer tile_id; --Tile id. PK.
real    min_x;  --X coordinate of bottom-left point
real    min_y;  --Y coordinate of bottom-left point
real    max_x;  --X coordinate of upper-right point
real    max_y;  --Y coordinate of upper-right point

解决方案失败:

首先,我尝试按一个坐标排序,在java端迭代此结果集,然后对每一行执行额外的选择。表现不佳。

现在我想知道在运行时是否可以使用纯SQL解决方案......

事先赞赏任何帮助或想法。

修改 两个瓦片之间可能存在间隙,因此(例如,对于右邻居)B.min_x-A.max_x可以是> 0.Hovewer,两个瓷砖不能相交,而不是边界。

我们正在使用Postgres 8.3

2 个答案:

答案 0 :(得分:1)

窗口函数和CTE会使这很容易。我认为两者都可以在8.4及以上版本中使用。我强烈建议你升级。我在9.0上测试了这个解决方案:

with tile_data as
(
    select
        tile_id,
        min_x,
        min_x + 0.9 as max_x,
        min_y,
        min_y + 0.9 as max_y
    from
    (
     select 1 as tile_id, 0.0 as min_x, 0.0 as min_y union all
     select 2, 1.0, 0.0 union all
     select 3, 2.0, 0.0 union all
     select 4, 0.0, 1.0 union all
     select 5, 1.0, 1.0 union all
     select 6, 2.0, 1.0 union all
     select 7, 0.0, 2.0 union all
     select 8, 1.0, 2.0 union all
     select 9, 2.0, 2.0 
    ) a
),
right_neighbor_tiles as
(
    select
        tile_id,
        other_tile_id as right_neighbor_tile_id
    from
    (
        select
             a.tile_id,
             b.tile_id as other_tile_id,
             row_number() over(partition by a.tile_id order by b.min_x - a.min_x) as distance_rank
        from 
            tile_data a
        inner join
            tile_data b
        on
            a.min_x < b.min_x 
            and a.min_y = b.min_y
    ) ranked_tiles_right
    where
        distance_rank = 1
),
up_neighbor_tiles as
(
    select
        tile_id,
        other_tile_id as up_neighbor_tile_id
    from
    (
        select
            a.tile_id,
            b.tile_id as other_tile_id,
            row_number() over(partition by a.tile_id order by a.min_y - b.min_y) as distance_rank
        from 
            tile_data a
        inner join
            tile_data b
        on
            a.min_y > b.min_y 
            and a.min_x = b.min_x
    ) ranked_tiles_up
    where
        distance_rank = 1
)
select 
    a.*,
    b.right_neighbor_tile_id,
    c.up_neighbor_tile_id
from
    tile_data a
left join
    right_neighbor_tiles b
on
    a.tile_id = b.tile_id
left join
    up_neighbor_tiles c
on
    a.tile_id = c.tile_id

结果:

tile_id  min_x  max_x  min_y  max_y  right_neighbor_tile_id   up_neighbor_tile_id
1        0      0.9    0      0.9    2
2        1      1.9    0      0.9    3
3        2      2.9    0      0.9
4        0      0.9    1      1.9    5                        1
5        1      1.9    1      1.9    6                        2
6        2      2.9    1      1.9                             3
7        0      0.9    2      2.9    8                        4
8        1      1.9    2      2.9    9                        5
9        2      2.9    2      2.9                             6

答案 1 :(得分:0)

更改您的数据以使用Box类型。

create temp table test (
    nick    char,
    tile    box
);


/*
.........
aa..bbcc.
aa..bbcc.
*/

insert into test values ('a',  box(point(0,1),point(1,0)));
insert into test values ('b',  box(point(4,0),point(5,1)));
insert into test values ('c',  box(point(7,0),point(8,1)));

select
    distinct on (test.nick)

    test.nick,
    r.nick as right,
    least((r.tile[0])[0], (r.tile[1])[0]) as least_x -- gets the lowest x cord

from test
    join test as r
        on  test.tile << r.tile -- strictly left of operator

    group by test.nick, right, least_x

输出:

 nick | right   | least_x
 ------+---------+---------
 a    | b       |       4
 b    | c       |       7
 (2 rows)