但是,如果您可以创建表,则可以成为关系引擎的一部分:既是关系表的生成者,也是其使用者。这就是 OLEDB 的概念,此概念允许任何数据源生成数据流。这也是 SQL Server 2000 表值函数蕴含的概念。
在 Transact-SQL 中实现表值函数很简单:
create function t_sql_tvfPoints() returns @points table (x float, y float) as begin insert @points values(1,2); insert @points values(3,4); return; end
select O.ObjID, dbo.fDistanceLatLon(O.lat,O.lon, 37.8, -122.4)from fHtmCoverCircleLatLon(37.8, -122.4, 40) as TrixelTable join Object O on O.HtmID between TrixelTable.HtmIdStart -- coarse testand TrixelTable.HtmIdEnd where dbo.fDistanceLatLon(lat,lon,37.8, -122.4) < 40 -- careful test
create table Place(PlaceName varchar(100) not null, -- City nameState char(2) not null, -- 2 char state codePopulation int not null, -- Number of residents (1990)Households int not null, -- Number of homes (1990)LandArea int not null, -- Area in sqare KMWaterArea int not null, -- Water area within land areaLat float not null, -- Latitude in decimal degreesLon float not null, -- Longitude decimal degreesHtmID bigint not null primary key --spatial index key)
为了加快名称查找,我们添加了一个名称索引,但数据是按空间键聚集在一起的。邻近的对象共同位于聚集 B 树中,并因此位于相同或相邻的磁盘页上。
create table Station (StationName varchar(100) not null, -- USGS Station NameState char(2) not null, -- State locationLat float not null, -- Latitude in DecimalLon float not null, -- Longitude in DecimalDrainageArea float not null, -- Drainage Area (km2)FirstYear int not null, -- First Year operation YearsRecorded int not null, -- Record years (at Y2k)IsActive bit not null, -- Was it active at Y2k?IsRealTime bit not null, -- On Internet at Y2K?StationNumber int not null, -- USGS Station NumberHtmID bigint not null, -- HTM spatial key-- (based on lat/lon)primary key(htmID, StationNumber) )
如上所述,HtmID 字段是根据 LatLon 按以下方法计算出的:
update Station set HtmID = dbo.fHtmLatLon(lat, lon)
由于一个位置有多达 18 个测量站,因此主键必须包括测量站编号以便区分它们。但是,在 B 树中,HTM 键将所有邻近的测量站聚集在一起。为了加快查找,我们添加了测量站编号和名称索引:
create index Station_Name on Station(StationName)create index Station_Number on Station(StationNumber)
create table SpatialIndex (HtmID bigint not null , -- HTM spatial key (based on lat/lon)Lat float not null , -- Latitude in DecimalLon float not null , -- Longitude in Decimalx float not null , -- Cartesian coordinates,y float not null , -- derived from lat-lonz float not null , --,Type char(1) not null , -- Place (P) or gauge (G)ObjID bigint not null , -- Object ID in tableprimary key (HtmID, ObjID) )
insert SpatialIndexselect P.HtmID, Lat, Lon, XYZ.x, XYZ.y, XYZ.z, 'P' as type, P. HtmID as ObjIDFrom Place P cross apply fHtmLatLonToXyz(P.lat, P.lon)XYZinsert SpatialIndexselect S.HtmID, Lat, Lon, XYZ.x, XYZ.y, XYZ.z, 'S' as type, S.StationNumber as ObjIDfrom Station S cross apply fHtmLatLonToXyz(S.lat, S.lon) XYZ
select * -- find a HTM cover 100 NM around Baltimorefrom fHtmCoverCircleLatLon(39.3, -76.6, 100)
它将返回表 2 中所示的 Trixel 表。即,fHtmCoverCircleLatLon() 函数将返回“覆盖”该圆圈(在本例中,是单个 trixel)的一组 HTM 三角形。该圆圈内所有对象的 HTM 键也位于这些三角形中之一内。现在,我们需要检查所有这些三角形并舍弃假正值(图 1 中的仔细检查)。我们将按照与巴尔的摩市的距离对答案集进行排序,因此,如果我们需要找出最近的地点,只需选择 TOP 1 WHERE distance > 0 即可(我们要从中排除巴尔的摩市本身)。
declare @lat float, @lon floatselect @lat = lat, @lon = lon from Place where Place.PlaceName = 'Baltimore' and State = 'MD' select ObjID, dbo.fDistanceLatLon(@lat,@lon, lat, lon) as distancefrom SpatialIndex join fHtmCoverCircleLatLon(@lat, @lon, 100) On HtmID between HtmIdStart and HtmIdEnd -- coarse testand type = 'P'and dbo.fDistanceLatLon(@lat,@lon, lat, lon) < 100 -- careful testorder by distance asc
declare @radius floatset @radius = dbo.fDistanceLatLon(41,-109.55,37,-102.05)/2select * from Station where StationNumber in (select ObjID from fHtmCoverCircleLatLon(39, -105.55, @radius) join SpatialIndex on HtmID between HtmIdStart and HtmIdEndand lat between 37 and 41and lon between -109.05 and -102.048and type = 'S')OPTION (FORCE ORDER)
circleSpec := 'CIRCLE LATLON ' lat lon radius | 'CIRCLE J2000 ' ra dec radius| 'CIRCLE [CARTESIAN ]' x y z radius rectSpec := 'RECT LATLON ' { lat lon }2| 'RECT J2000 ' { ra dec }2| 'RECT [CARTESIAN ]' { x y z }2hullSpec := 'CHULL LATLON ' { lon lat }3+| 'CHULL J2000 ' { ra dec }3+| 'CHULL [CARTESIAN ]' { x y z }3+convexSpec := 'CONVEX ' [ 'CARTESIAN '] { x y z D }*areaSpec := rectSpec | circleSpec | hullSpec | convexSpec regionSpec := 'REGION ' {areaSpec}* | areaSpec
select S.* from (select ObjID from fHtmCoverRegion('RECT LATLON 37 -109.55 41 -102.05') loop join SpatialIndexon HtmID between HtmIdStart and HtmIdEndand lat between 37 and 41and lon between -109.05 and -102.048and type = 'S') as G join Station S on G.objID = S.StationNumberOPTION (FORCE ORDER)
select S.* from (select ObjID from fHtmCoverRegion('CHULL LATLON 37 -109.55 41 -109.55 41 -102.05 37 -102.05') loop join SpatialIndexon HtmID between HtmIdStart and HtmIdEndand lat between 37 and 41and lon between -109.05 and -102.048and type = 'S') as G join Station S on G.objID = S.StationNumberOPTION (FORCE ORDER)
declare @utahRegion varchar(max)set @utahRegion = 'region ' + 'rect latlon 37 -114.0475 41 -109.0475 ' -- Main part+ 'rect latlon 41 -114.0475 42 -111.01 ' -- Ogden and Salt Lake.
现在,我们可以用以下查询来查找犹他州中的所有流量计:
select S.* from (select ObjID from fHtmCoverRegion(@utahRegion) loop join SpatialIndexon HtmID between HtmIdStart and HtmIdEndand ((( lat between 37 and 41) -- Careful testand (lon between -114.0475 and -109.04)) -- Are we inside or (( lat between 41 and 42) -- one of the twoand (lon between -114.0475 and -111.01)) -- boxes? )and type = 'S' ) as G join Station S on G.objID = S.StationNumber OPTION (FORCE ORDER)
select * from Place where HtmID in (select distinct SI.objIDfrom fHtmCoverRegion(@californiaRegion) loop join SpatialIndex SIon SI.HtmID between HtmIdStart and HtmIdEnd and SI.type = 'P'join place P on SI.objID = P.HtmIDcross join fHtmRegionToTable(@californiaRegion) Polygroup by SI.objID, Poly.convexID having min(SI.x*Poly.x + SI.y*Poly.y + SI.z*Poly.z - Poly.d) >= 0 ) OPTION (FORCE ORDER)
select * -- Get all the California River Stationsfrom Stationwhere stationNumber in -- that are inside the region(select ObjID from fHtmRegionObjects(@californiaRegion,'S')) select * -- Get all the California citiesfrom Placewhere HtmID in -- that are inside the region(select ObjID from fHtmRegionObjects(@californiaRegion,'P'))
[Gray]“There Goes the Neighborhood:Relational Algebra for Spatial Data Search”。Jim Gray、Alexander S. Szalay、Gyorgy Fekete、Wil O’Mullane、Maria A. Nieto-Santisteban、Aniruddha R. Thakar、Gerd Heber、Arnold H. Rots,MSR-TR-2004-32,2004 年 4 月
•
[Szalay]“Indexing the Sphere with the Hierarchical Triangular Mesh”。Alexander S. Szalay、Jim Gray、George Fekete、Peter Z. Kunszt、Peter Kukol、Aniruddha R. Thakar,Microsoft SQL Server 2005 Samples。
•
[Fekete]“SQL SERVER 2005 HTM Interface Release 4”。George Fekete、Jim Gray、Alexander S. Szalay,2005 年 5 月 15 日,Microsoft SQL Server 2005 Samples。
•
[Samet1]“Applications of Spatial Data Structures:Computer Graphics, Image Processing, and GIS”。Hanan Samet,Addison-Wesley, Reading, MA, 1990。ISBN0-201-50300-0。
•
[Samet2]“The Design and Analysis of Spatial Data Structures”。Hanan Samet,Addison-Wesley, Reading, MA, 1990。ISBN 0-201-50255-0。
declare @lat float, @lon floatselect @lat = lat, @lon = lon from Place where PlaceName = 'Baltimore' and State = 'MD' select PlaceName, dbo.fDistanceLatLon(@lat,@lon, lat, lon) as distancefrom Place
还有 fDistanceXyz() 和 fDistanceEq() 函数可供天文学家使用。
下面的例程可返回一个用作空间索引的表。所返回空间索引表的数据定义为:
SpatialIndexTable table (HtmID bigint not null , -- HTM spatial key (based on lat/lon)Lat float not null , -- Latitude in DecimalLon float not null , -- Longitude in Decimalx float not null , -- Cartesian coordinates,y float not null , -- derived from lat-lonz float not null , --,Type char(1) not null , -- place (P) or gauge (G)ObjID bigint not null , -- object ID in tabledistance float not null , -- distance in arc minutes to objectprimary key (HtmID, ObjID) )
查找邻近的对象:fHtmNearbyLatLon(type, lat, lon, radius) 将返回空间索引表
返回半径范围内特定类型的对象列表及它们到给定点的距离。该列表将按对象的位置由近到远排列。
使用示例:
select distance, Place.*from fHtmNearbyLatLon('P', 39.3, -76.6, 10) I join Place on I.objID = Place.HtmIDorder by distance
declare @answer nvarchar(max)declare @lat float, @lon floatselect @lat = lat, @lon = lon from Place where Place.PlaceName = 'Baltimore' and State = 'MD' set @answer = ' using fHtmCoverCircleLatLon() it finds: 'select @answer = @answer + cast(P.placeName as varchar(max)) + ', ' + str( dbo.fDistanceLatLon(@lat,@lon, I.lat, I.lon) ,4,2) + ' arc minutes distant.' from SpatialIndex I join fHtmCoverCircleLatLon(@lat, @lon, 5) On HtmID between HtmIdStart and HtmIdEnd -- coarse testand type = 'P' -- it is a placeand dbo.fDistanceLatLon(@lat,@lon, lat, lon) between 0.1 and 5 -- careful testjoin Place P on I.objID = P.HtmIDorder by dbo.fDistanceLatLon(@lat,@lon, I.lat, I.lon) ascprint 'The city within 5 arc minutes of Baltimore is: ' + 'Lansdowne-Baltimore Highlands, 4.37 arc minutes away'
select S.* from (select ObjID from fHtmCoverRegion('RECT LATLON 37 -109.55 41 -102.05') loop join SpatialIndexon HtmID between HtmIdStart and HtmIdEndand lat between 37 and 41and lon between -109.05 and -102.048and type = 'S') as G join Station S on G.objID = S.StationNumberOPTION (FORCE ORDER)
RegionTable (convexID bigint not null , -- ID of the convex, 0,1,...halfSpaceID bigint not null -- ID of the halfspace -- within convex, 0,1,2,x float not null -- Cartesian coordinates ofy float not null -- unit-normal-vector of z float not null -- halfspace planed float not null -- displacement of halfspace ) -- along unit vector [-1..1]