本文將向各位介紹如何使用MySql5.x中的空間數據庫,並展示一下它高效的性能(前提是正確使用)。
本文適合於對SQL和MYSQL熟悉的人員。
步驟1:創建支持空間查詢的表
首先來說一下如何創建一個包含空間數據的名為Points的表。
CREATE TABLE `points` (
`name` varchar(20) NOT NULL DEFAULT '',
`location` point NOT NULL,
`description` varchar(200) DEFAULT NULL,
PRIMARY KEY (`name`),
SPATIAL KEY `sp_index` (`location`)
) ENGINE=MyISAM DEFAULT CHARSET=gbk;
這條DDL命令創建了一個名為Points的表,包含一個name字段和一個類型為point的字段location(所處位置)及descrption(描述)字段。
正如你所看到的,空間類型字段的使用跟Mysql中其他類型一樣,創建時選擇相應的類型即可。
空間數據類型的基類是Geometry。
可以在下面的文檔中找到所有Mysql支持的空間數據類型:
http://dev.mysql.com/doc/refman/4.1/en/spatial-extensions.html
步驟2:向空間數據表中插入數據
我們來看一看想Points表中的插入數據是多么的簡單:
INSERT INTO Points (name, location) VALUES ( 'point1' , GeomFromText( ' POINT(31.5 42.2) ' ) )
這是一個普通的SQL插入操作,只有函數GeomFromText()是我們以前未見過的。這個函數接受一個字符串,並且返回一個幾何對象。有關該字符串的GIS標准格式詳見:
http://dev.mysql.com/doc/refman/4.1/en/gis-wkt-format.html
步驟3:從空間數據表中讀取數據
從Points表中讀取數據也是非常簡單的:
SELECT name, AsText(location) FROM Points;
以上語句的返回結果中location會被轉換成跟第二步中一樣的GIS標准字符串。實際上AsText函數僅僅是把數據庫內部存儲的幾何對象格式化成一個字符串而已。
下面一個函數也是非常有用的:
SELECT name, AsText(location) FROM Points WHERE X(location) < 10 and Y(location) > 12;
該Select語句返回一系列location的X()(經度)小於10並且Y()(經度)大於12的點集合。
步驟4:空間表的高級查詢
把指定的幾何對象轉變易讀的文本:
SELECT AsText(Envelope(GeomFromText('LineString(1 1,2 2)')));
返回指定幾何對象的大小:
SELECT GeometryType(GeomFromText('POINT(1 1)'));
返回指定幾何對象的類型:
SELECT GeometryType(GeomFromText('POINT(1 1)'));
查找指定矩形范圍內的點:
SET @bbox = 'POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))';
SELECT name, AsText(location) FROM Points WHERE Intersects( location, GeomFromText(@bbox) );
步驟5:查找圓形區域內的點
這一步介紹如何查詢圓形區域(通常用一個中心點和半徑來表示)內的幾何對象。
您首先想到的語句可能是:
SET @point = 'POINT(10 10)';
SET @radius = 20;
SELECT name, AsText(location) FROM Points WHERE Distance(location, GeomFromText(@point)) < @radius;
但是這條語句運行會出錯,因為Distance函數還沒有實現。MySql空間擴展文檔說明中已經說明他們只實現了OpenGis標准的一部分。
一個替代的方式是使用intersect函數。
MySql空間擴展文檔中已經指明各種幾何對象可以使用intersect函數來判斷幾何對象是否和一個矩形相交。
這樣在取得近似范圍后我們可以再使用距離估算來過濾出正確的結果。
SET @center = GeomFromText('POINT(10 10)');
SET @radius = 30;
SET @bbox = CONCAT('POLYGON((',
X(@center) - @radius, ' ', Y(@center) - @radius, ',',
X(@center) + @radius, ' ', Y(@center) - @radius, ',',
X(@center) + @radius, ' ', Y(@center) + @radius, ',',
X(@center) - @radius, ' ', Y(@center) + @radius, ',',
X(@center) - @radius, ' ', Y(@center) - @radius, '))'
);
[1]
SELECT name, AsText(location)
FROM Points
WHERE Intersects( location, GeomFromText(@bbox) )
AND SQRT(POW( ABS( X(location) - X(@center)), 2) + POW( ABS(Y(location) - Y(@center)), 2 )) < @radius; To Obtain a result ordered by distance from the center of the selection area:
[2]
SELECT name, AsText(location), SQRT(POW( ABS( X(location) - X(@center)), 2) + POW( ABS(Y(location) - Y(@center)), 2 )) AS distance
FROM Points
WHERE Intersects( location, GeomFromText(@bbox) )
AND SQRT(POW( ABS( X(location) - X(@center)), 2) + POW( ABS(Y(location) - Y(@center)), 2 )) < @radius
ORDER BY distance;
步驟6:測試性能
最后一步我們來試試在大數據量的情況下空間數據查詢的性能。
首先我們新建一個存儲過程,指定一個隨機數值隨機產生記錄插入到Points表中。
CREATE PROCEDURE fill_points(
IN size INT(10)
)
BEGIN
DECLARE i DOUBLE(10,1) DEFAULT size;
DECLARE lon FLOAT(7,4);
DECLARE lat FLOAT(6,4);
DECLARE position VARCHAR(100);
-- Deleting all.
DELETE FROM Points;
WHILE i > 0 DO
SET lon = RAND() * 360 - 180;
SET lat = RAND() * 180 - 90;
SET position = CONCAT( 'POINT(', lon, ' ', lat, ')' );
INSERT INTO Points(name, location) VALUES ( CONCAT('name_', i), GeomFromText(position) );
SET i = i - 1;
END WHILE;
END
然后調用該存儲過程,參數指定一個較大的數字,例如我們想產生一百萬條記錄:
CALL fill_points(1000000);
然后我們執行查詢[1]和[2]
在我機器上(Intel Core Duo 2.0 GHz Laptop)的測試結果是:
圓形區域選擇(即周邊搜索)結果不排序[1]
43862 rows in set ~1.10 sec with 1.000.000 records
圓形區域選擇(即周邊搜索)結果排序[2]
43862 rows in set ~1.72 sec with 1.000.000 records