使用过关系数据库的人一般都非常熟悉SQL语句,它使用Select语句对数据库进行查询,Select语句可以发分为两大类:非聚合类查询语句和聚合类查询语句。
SELECT 学号, 姓名, 期中成绩*0.4 + 期末成绩*0.6 as 成绩 FROM 成绩表 WHERE 学号>= 100 AND 学号 =< 200 order by学号 ASC
SELECT AVG(期中成绩) as期中平均成绩, AVG(期末成绩) as期末平均成绩 GROUP BY 班级 |
上面例子中的第一条SELECT语句是一条非聚合类查询语句,用于选择“成绩表”中“学号>= 100 and 学号 =< 200”的数据,返回的每条记录中包含属性“学号”、“姓名”和计算属性“成绩”,查询结果按照“学号”升序排序,可以看到非聚合类查询语句包含了如下几部分:
l 属性列表,例如:属性“学号”、“姓名”和计算属性“成绩”。
l From子句,用于指定表的名称,例如:“成绩表”。
l WHERE子句,用于指定过滤器,例如:“学号>= 100 and 学号 =< 200”
l ORDER BY字句,用于排序规则:例如:“学号 ASC”。
上面例子中的第二条SELECT语句是一条聚合类查询语句,用于计算每个班级的期中平均成绩和期末平均成绩,与非聚合类查询语句比较,它包含如下的额外成分:
l 聚合函数,例如:“AVG(期中成绩)”。
l GROUP BY子句,用于指定分组规则,例如:“GROUP BY 班级”。
与SELECT语句类似,要素服务也提供了非聚合类查询和聚合类查询方法,调用方法MgFeatureService::SelectFeatures(…)或MgFeatureService::SelectAggregate(…)可以从要素源中选择符合查询条件的要素,它们的方法签名如下所示,其中参数resource用于指定一个要素源,参数className用于指定一个要素类的名称或一个Join的名称,它类似于SELECT语句的FROM子句;参数options用于指定查询条件,可以指定返回的属性、过滤器、排序规则,添加计算属性;参数coordinateSystem用于指定返回要素的空间参考系,这意味着如果要素源的空间参考系和coordinateSystem指定的空间参考系不一致,那么会对要素进行坐标转换。
MgFeatureReader SelectFeatures( MgResourceIdentifier resource, string className, MgFeatureQueryOptions options); MgFeatureReader SelectFeatures( MgResourceIdentifier resource, string className, MgFeatureQueryOptions options, string coordinateSystem); MgDataReader SelectAggregate( MgResourceIdentifier resource, string className, MgFeatureAggregateOptions options); |
方法SelectFeatures(…)和SelectAggregate(…)主要的区别是:
l SelectAggregate(…)用于执行聚集类查询,它支持分组(GroupBy)和唯一(Distinct)操作,而SelectFeatures(…)用于执行非聚合类查询。
l SelectAggregate(…)的返回值是类MgDataReader的对象实例一个的实例,而SelectFeatures(…) 的返回值是一个MgFeatureReader的实例,关于MgFeatureReader和MgDataReader的不同之处请参考下一小节。
1.1.1.1 属性列表
在执行查询的时候,不仅可以通过MgQueryOptions::AddFeatureProperty(…)选择一个或多个已经存在的属性,而且还可以新建一些计算属性,计算属性由已经存在的属性计算得到,例如一个要素类中包含了两个Int32类型的属性“PropA”和“PropB”,我们可以创建一个计算属性“PropA+PropB”,它的值等于这两个属性的和,使用方法MgQueryOptions::AddComputedProperty(…)可以创建这样一个计算属性,其中参数aliasName用于为计算属性指定一个名称,expression用于指定计算属性的计算公式,即一个表达式。
int AddFeatureProperty(string propertyName); int AddComputedProperty(string aliasName, string expression); |
如下的代码展示了如何创建一个计算属性。
$queryOptions = new MgFeatureQueryOptions(); $queryOptions->AddFeatureProperty("FeatId"); $queryOptions->AddFeatureProperty("aDouble"); $queryOptions->AddComputedProperty("ceilADouble", "Ceil(aDouble)"); $featureReader = featureService.SelectFeatures($resourceId, $featClassName, $queryOptions); |
上面代码的功能类似于如下的SQL语句。
select FeatId, aDouble, Ceil(aDouble) as ceilADouble from featclass |
1.1.1.2 过滤器
过滤器用于选择要素源中的部分要素,例如定义一个过滤器选择要素类Road中与某一条道路相交的其它道路,方法SelectFeatures(…)和SelectAggregate(…)都支持使用过滤器选择符合查询条件的要素,在创建类MgDeleteFeatures、MgLockFeatures、MgUnlockFeatures和MgUpdateFeatures的实例时,也需要传入一个过滤器。当执行相应的FDO命令时,会针对每个要素评估过滤器,只有评估结果为真的要素才会被FDO命令所影响。
MapGuide使用字符串来表示一个过滤器,它的语法有点类似于SQL语句的WHERE子句,但是SQL语句针对的是表和列,而MapGuide过滤器针对的是要素类和属性,表8-3列出了一些过滤器以及它们的含义。
过滤器 | 描述 |
Lanes >= 4 | 选择小路(Lanes)数目大于等于4的道路(Road) |
Pines.state = "proposed" | 选择管道网络(PipeNetworks)中管道状态为“proposed”的所有管道(Pipe) |
state = "existing" and owner like "%Smith%" | 选择状态为“existing”,并且所有人包含文字“Smith”的所有地块(Parcels) |
state in ("affected", "encroached") | 选择状态为“affected”或“encroached”的地块(Parcels) |
表8-3 MapGuide过滤器示例
过滤器可以分为两种:基本过滤器和空间过滤器,这两种过滤器可以独立使用,也可以组合使用。
1) 基本过滤器
基本过滤条件使用非几何属性、数据值和操作符来创建一个过滤字符串,这个字符串通常作为方法MgFeatureQueryOption::SetFilter(…)或MgFeatureAggregateOptions::SetFilter(…)的参数。假设“FEATID”是一个属性名称,那么“FEATID>20”定义了一个过滤条件选择所有“FEATID”属性大于20的要素。
|
2) 空间过滤器
空间过滤器使用一个关联与两个几何图形的空间操作符定义一个过滤条件,它通常作为MgFeatureQueryOption::SetSpatialFilter(…) 或MgFeatureAggregateOptions::SetSpatialFilter(…)方法的参数。该方法的原型如下所示,参数geometryProperty用于指定要素类中几何属性的名称,geometry用于指定一个几何对象,参数spatialOperation用于指定一个空间操作符。假设操作符是MgFeatureSpatialOperations::Within,几何对象的WKT表示为POLYGON((0 0, 2 0, 2 2, 0 2, 0 0)),那么此过滤条件的效果是选择在多边形内部的所有要素。
|
类MgFeatureSpatialOperations定义了MapGudie支持的空间操作符,它们的含义请参考空间关系。
l MgFeatureSpatialOperations::Contains
l MgFeatureSpatialOperations::CoveredBy
l MgFeatureSpatialOperations::Crosses
l MgFeatureSpatialOperations::Disjoint
l MgFeatureSpatialOperations::EnvelopeIntersects
l MgFeatureSpatialOperations::Equals
l MgFeatureSpatialOperations::Inside
l MgFeatureSpatialOperations::Intersects
l MgFeatureSpatialOperations::Overlaps
l MgFeatureSpatialOperations::Touches
l MgFeatureSpatialOperations::Within
3) 组合过滤器
通过调用方法MgFeatureQueryOption::SetBinaryOperator(…)可以使用逻辑操作符AND或OR将基本过滤器和空间过滤器组合在一起,参数andOr的值为TRUE表示使用AND逻辑操作符,FALSE表示使用OR逻辑操作符,该方法只有在基本过滤器和空间过滤器设置的情况下才会发挥作用。如果设置了基本过滤器和空间过滤器,却没有调用此方法,那么使用AND操作符组合基本过滤器和空间过滤器。
|
过滤器的BNF语法如下所示,从中可以看到表达式是过滤器的重要组成部分,表达式支持的数据类型如表8-4所示,可以使用部分函数如表8-5所示。
<Filter> ::= '(' Filter ')' | <LogicalOperator> | <SearchCondition> <LogicalOperator> ::= <BinaryLogicalOperator> | <UnaryLogicalOperator> <BinaryLogicalOperator> ::= <Filter> <BinaryLogicalOperations> <Filter> <SearchCondition> ::= <InCondition> | <ComparisonCondition> | <GeometricCondition> | <NullCondition> <InCondition> ::= <Identifier> IN '(' ValueExpressionCollection')' <ValueExpressionCollection> ::= <ValueExpression> | <ValueExpressionCollection> ',' <ValueExpression> <ComparisonCondition> ::= <Expression> <ComparisonOperations> <Expression> <GeometricCondition> ::= <DistanceCondition> | <SpatialCondition> <DistanceCondition> ::= <Identifier> <DistanceOperations> <Expression> <distance> <NullCondition> ::= <Identifier> NULL <SpatialCondition> ::= <Identifier> <SpatialOperations> <Expression> <UnaryLogicalOperator> ::= NOT <Filter> <BinaryLogicalOperations> ::= AND | OR <ComparisionOperations> ::= = // EqualTo (EQ) <> // NotEqualTo (NE) > // GreaterThan (GT) >= // GreaterThanOrEqualTo (GE) < // LessThan (LT) <= // LessThanOrEqualTo (LE) LIKE // Like <DistanceOperations> ::= BEYOND | WITHINDISTANCE <distance> ::= DOUBLE | INTEGER <SpatialOperations> ::= CONTAINS | CROSSES | DISJOINT | EQUALS | INTERSECTS | OVERLAPS | TOUCHES | WITHIN | COVEREDBY | INSIDE
<Expression> ::= '(' Expression ')' | <UnaryExpression> | <BinaryExpression> | <Function> | <Identifier> | <ValueExpression> <BinaryExpression> ::= <Expression> '+' <Expression> | <Expression> '-' <Expression> | <Expression> '*' <Expression> | <Expression> '/' <Expression> <DataValue> ::= TRUE | FALSE | DATETIME | DOUBLE | INTEGER | STRING | BLOB | CLOB | NULL <Function> ::= <Identifier> '(' <ExpressionCollection> ')' <ExpressionCollection> ::= | <Expression> | <ExpressionCollection> ',' <Expression> <GeometryValue> ::= GEOMFROMTEXT '(' STRING ')' <Identifier> ::= IDENTIFIER <ValueExpression> ::= <LiteralValue> | <Parameter>; <LiteralValue> ::= <GeometryValue> | <DataValue> <Parameter> ::= PARAMETER | ':'STRING <UnaryExpression> ::= '-' <Expression> |
数据类型 | 描述 |
String | 字符串由单引号括起来,例如’test’,如果需要在字符串中包括单引号,那么使用两个连续的单引号表示一个单引号,例如’aaa’’bbb’。 |
Identifier | 标识符是使用双引号括起来的、不等于关键词的字符和数字序列,例如”abc”, 如果需要在标识符中包括双引号,那么使用两个连续的双引号表示一个双引号,例如”aaa””bbb”。 |
Integer | 整数是一个十进制数字,它可以使用负号,但不支持正号,如果数字超过了32-bit的精度范围,那么这个数字会被转换为浮点数。 |
Double | 浮点数是一个包含小数点的数字,它可以使用负号,还可以包含指数(e{{[0-9]})。 |
DateTime | 日期和时间由如下格式的字符串表示: DATE ‘YYYY-MM-DD’ TIME ‘HH:MM:SS[.sss]’ TIMESTAMP ‘YYYY-MM-DD HH:MM:SS[.sss]’ |
CLOB/BLOB | 目前不支持此数据类型。 |
表8-4 表达式支持的数据类型
函数 | 描述 |
Double Avg(n) | 计算属性n的平均值,忽略为null的属性值。 |
Int64 Ceil(Double) | 返回大于等于Double的最小整数。 |
String Concat(Str1, Str2) | 连接字符串Str1和Str2。 |
Int64 Count(expression) | 返回expression不等于null的要素总数。 |
Int64 Floor(Double) | 返回小于等于Double的最大整数。 |
String Lower(Str) | 将Str转换为小写字母。 |
Double Min(expression) | 返回expression的最小值。 |
Double Max(expression) | 返回expression的最大值。 |
Double Stddev(n) | 计算n的标准方差,忽略null。 |
Double Sum(n) | 计算n的总和。 |
String Upper(Str) | 将Str转换为大写字母。 |
表8-5 表达式中可以使用的部分函数
表达式中的一些函数可以使用单个要素的属性值作为参数,返回一个和此属性值相关的值,例如ceil、floor、concat和upper等,这些函数可以使用于非聚合类和聚合类查询;一些函数可以将多个要素的属性值作为参数,返回一个和一组属性值相关的值,例如avg、count、max、min、stddev和sum等,这些函数只能使用于聚合类查询。
下面是一些表达式的例子,以及如何使用这些表达式进行查询。
1) <Identifier> NULL
给定一个字符串类型的属性Name,如果它的属性允许为NULL,并且在插入一个要素时没有指定此属性的值,那么这个要素Name属性的值为NULL。通过应用过滤条件“Name NULL”,可以选择所有Name属性值为NULL的所有要素。
|
通过应用过滤条件“NOT Name NULL”,可以选择所有Name属性值不为NULL的所有要素。
|
2) <Identifier> LIKE <String>
Like可以用于实现模糊查询,给定一个字符串模式(Patern),可以使用Like查找所有符合指定模式的要素。其中,百分号“%”用于匹配0个或多个字符,下划线“_”用于匹配单个字符。假设Description是一个字符串类型的属性,数据源中有两个包含了此属性的要素,这两个属性的值为“Point Data”和“Polygon Data”,过滤条件“Description LIKE 'Polygon'”返回“Polygon Data”,
|
过滤条件“NOT Description LIKE 'Polygon'”返回“Point Data”,
|
过滤条件“Description LIKE 'Pol_gon'”返回“Polygon Data”。
|
3) <Identifier> IN (<ValueExpressionCollection>)
IN操作符用于选择与列表中的任意一个值匹配的要素,给定一个Int16类型的属性Id,过滤条件“Id IN (-5995, -7033)”选择Id属性值等于-5995或-7033的要素。
1.1.1.3 排序
调用MgQueryOptions::SetOrderingFilter(…)可以将查询结果进行排序,该方法的签名如下所示,参数orderByProperties是一个属性名称的集合,用于指定排序的属性,如果指定了多个属性,那么优先使用orderByProperties中序号较小的属性,只有在序号较小的属性具有相同的值时,才使用序号较大的属性;参数orderOption用于指定是以升序的方式还是降序的方式进行排序,它的值可以为MgOrderingOption::Ascending或MgOrderingOption::Descending,分别用于表示升序和降序。
void SetOrderingFilter(MgStringCollection orderByProperties, int orderOption); |
1.1.1.4 分组
调用MgFeatureAggregateOptions::SetGroupingFilter(…)可以将查询结果进行分组,该方法的签名如下所示,参数groupByProperties是一个属性名称的集合,用于指定分组的属性,如果指定了多个属性,那么优先使用orderByProperties中序号较小的属性,只有在序号较小的属性具有相同的值时,才使用序号较大的属性;参数groupFilter用于指定分组的过滤器。
void SetGroupingFilter(MgStringCollection groupByProperties, string groupFilter); |
如下的代码展示了如何使用分组创建一个聚合类查询。
$stringCollection->Add("GroupName"); $groupingFilter = "GroupName = 'Group1' or GroupName = 'Group2' or GroupName = 'Group3'"; $aggregateOptions->SetGroupingFilter($stringCollection, $groupingFilter); $aggregateOptions->SetComputedProperty("minD", "min(aDouble)"); $dataReader = $featureService->SelectAggregate($featureSrcResId, $featClassName, $aggregateOptions); |
上面代码的功能类似于如下的SQL语句。
SELECT min(aDouble) AS minD FROM featclass GROUP BY groupname HAVING groupname = 'Group1' OR groupname = 'Group2' OR groupname = 'Group3' |
1.1.1.5 MgDataReader、MgFeatureReader和MgSqlDataReader
MapGuide支持三种类型的读取器,它们分别为MgDataReader、MgSqlDataReader和MgFeatureReader,读取器的类型由类MgReaderType定义。
l MgReaderType::DataReader
l MgReaderType::FeatureReader
l MgReaderType::SqlDataReader
类MgDataReader、MgSqlDataReader和MgFeatureReader都继承自MgReader,都是一个用于读取查询结果的前向只读迭代器(Forward-Only, Read-Only Iterator),读取器的初始位置总是位于第一个数据之前,在读取数据之前必须调用方法ReadNext(…),在调用ReadNext(…)之后就无法读取读取器当前位置之前的数据。它们之间的主要不同点是,MgFeatureReader包含了一个方法MgFeatureReader::GetClassDefinition(),通过该方法的返回值可以得到查询结果的类定义(Class Definition),通过类定义得到每个属性的定义、主键等信息,而MgDataReader和MgSqlDataReader仅仅可以通过方法MgReader::GetPropertyType(…)得到属性的类型。
如下的代码展示了将一个MgFeatureReader实例中的所有要素输出到一个文件中。
function printFeatureReader($featureReader) { global $logFileHandle; $i = 0; while ($featureReader->ReadNext()) { fwrite($logFileHandle, "Printing a feature.n"); $i++; $propCount = $featureReader->GetPropertyCount(); for($j=0; $j<$propCount; $j++) { $propertyName = $featureReader->GetPropertyName($j); $boolVal = $featureReader->IsNull($propertyName); if ($boolVal) continue; $propertyType = $featureReader->GetPropertyType($propertyName); printPropertyValueFromFeatReader($featureReader, $propertyType, $propertyName); } } return $i; }
function printPropertyValueFromFeatReader($featureReader, $propertyType, $propertyName) { global $logFileHandle; global $agfReaderWriter; global $wktReaderWriter; switch ($propertyType) { case MgPropertyType::Null : fwrite($logFileHandle, "$propertyName is a null propertyn"); break; case MgPropertyType::Boolean : $val = $featureReader->GetBoolean($propertyName); $valStr = printBoolean($val); fwrite($logFileHandle, "$propertyName is boolean "$valStr"n"); break; case MgPropertyType::Byte : $val = $featureReader->GetByte($propertyName); fwrite($logFileHandle, "$propertyName has a byte value of "$val"n"); break; case MgPropertyType::DateTime : $val = $featureReader->GetDateTime($propertyName); $valStr = printDateTime($val); fwrite($logFileHandle, "DateTime is "$valStr"n"); break; case MgPropertyType::Single : $val = $featureReader->GetSingle($propertyName); fwrite($logFileHandle, "$propertyName is single "$val"n"); break; case MgPropertyType::Double : $val = $featureReader->GetDouble($propertyName); fwrite($logFileHandle, "$propertyName is double "$val"n"); break; case MgPropertyType::Int16 : $val = $featureReader->GetInt16($propertyName); fwrite($logFileHandle, "$propertyName is int16 "$val"n"); break; case MgPropertyType::Int32 : $val = $featureReader->GetInt32($propertyName); fwrite($logFileHandle, "$propertyName is int32 "$val"n"); break; case MgPropertyType::Int64 : $val = $featureReader->GetInt64($propertyName); fwrite($logFileHandle, "$propertyName is int64 "$val"n"); break; case MgPropertyType::String : $val = $featureReader->GetString($propertyName); fwrite($logFileHandle, "$propertyName is string "$val"n"); break; case MgPropertyType::Blob : fwrite($logFileHandle, "$propertyName is blobn"); break; case MgPropertyType::Clob : fwrite($logFileHandle, "$propertyName is clobn"); break; case MgPropertyType::Feature : $val = $featureReader->GetFeatureObject($propertyName); if ($val != NULL) { fwrite($logFileHandle, "$propertyName is a featuren"); printFeatureReader($val); } break; case MgPropertyType::Geometry : fwrite($logFileHandle, "$propertyName is a geometryn"); $val = $featureReader->GetGeometry($propertyName); if ($val != NULL) { $aGeometry = $agfReaderWriter->Read($val); $wktRepresentation = $wktReaderWriter->Write($aGeometry); fwrite($logFileHandle, "WKT Representation: "$wktRepresentation"n"); } else { fwrite($logFileHandle, "This geometry property is nulln"); } break; case MgPropertyType::Raster : $val = $featureReader->GetRaster($propertyName); fwrite($logFileHandle, "$propertyName is a rastern"); break; default : fwrite($logFileHandle, "Unknown property typen"); } } |
需要注意的是,在使用读取器读取完数据后要尽量马上调用方法MgReader::Close()关闭读取器。为什么呢?为了提高MapGuide服务器的响应速度,MapGuide服务器使用了连接池(Connection Pool)的方式缓存了要素源FDO连接,即FdoIConnection实例,当接收到一个要素服务请求之后,它会首先在FDO连接池中查找是否有可用的FDO连接,如果有就使用此FDO连接执行服务请求,否则创建一个新的FDO连接,但是如果已经超过服务器配置文件中设置的最大FDO连接数,那么此要素服务请求会失败。当要素服务返回一个读取器之后,为了保证查询结果不会在读取数据之前消失,MapGuide服务器会将读取器所关联的FDO连接标记为正在使用,只有在调用方法MgReader::Close()关闭读取器后才会关闭和释放此FDO连接,如果不及时调用方法MgReader::Close()可能会导致超过最大FDO的连接数,最终无法处理要素服务请求。