使用ADO操作SQL Server空间数据时出现字符集异常问题
Delphi 10.4读取SQL Server空间字段(geometry)乱码问题解决
问题场景
使用Delphi 10.4结合ADO操作SQL Server数据库,读取包含geometry类型的空间字段时出现异常:两次读取同一数据库的同一数据,普通varchar字段(如Name)始终正常,但Geom字段一次返回正确的WKT格式字符串,另一次返回乱码字符(类似二进制字节错误转成字符串的结果)。
相关代码片段
type TDatabaserecord = record ... NameStr : String; OutLineStr : String; ... end; type TTabRecordList = class(TList<TDatabaserecord>) private ... public procedure SavetoFile(Filename: String); end; procedure ReadDBConent ( ....., MYDatabaserecord : TDatabaserecord ); begin .... MyDatabaserecord.NameStr := Fieldbyname('Name').AsString; MyDatabaserecord.OutLineStr := Fieldbyname('Geom').AsString; ... end; procedure TTabRecordList.SaveDBConent ( ..... ); var i: Integer; ds: TDatabaserecord; Content: TStringlist; begin Content := TStringlist.create; try for i := 0 to self.Count - 1 do begin ds := self.Items[i]; ... Content.add('Name=' + ds.NameStr); Content.add('OutLine=' + ds.OutLineStr); end; Content.SavetoFile(Filename); finally Content.Free; end; end;
错误现象对比
正常读取结果:
Name=NameofObject OutLine=POLYGON ((330692534935335 323094130298054, 347882368377054 323094130298054, 347907281179144 319182820370010, 330717447737425 319108081963742, 330692534935335 323094130298054))
异常读取结果:
Name=NameofObject OutLine= €3þÕW¦ÅBp8犟,úB bM³ÁÈB ]M(Y-úB JHÿÏÈB0Ω1¿øB€È–K>©ÅB`*4Gë¿øB€3þÕW¦ÅBp8犟,úB ÿÿÿÿ
数据表结构
create table TableName ( Name varchar(50) NULL, ... Geom geometry )
问题原因
这不是字符集配置问题,而是ADO对SQL Servergeometry类型的处理机制导致的:geometry字段本质是二进制存储的空间数据,ADO驱动有时会自动调用SQL Server的STAsText()函数将其转为WKT字符串返回,但有时会直接返回原始二进制数据,当用.AsString读取时,二进制字节会被错误地按当前字符集转成字符串,从而出现乱码。
解决方案
方案1:修改SQL查询,强制返回WKT字符串(推荐)
在查询语句中直接调用geometry类型的STAsText()方法,将空间数据转为标准WKT字符串后再返回,这样.AsString读取时始终得到正确结果。
示例查询SQL:
SELECT Name, Geom.STAsText() AS Geom FROM TableName
原读取代码无需修改,Fieldbyname('Geom').AsString即可稳定获取正确的WKT格式字符串。
方案2:在Delphi中解析二进制空间数据(复杂,不推荐)
如果无法修改SQL语句,需要手动读取geometry字段的二进制数据,再解析为WKT字符串。可借助第三方空间组件(如SQLSpatial库)实现,示例代码如下:
procedure ReadDBConent ( ....., MYDatabaserecord : TDatabaserecord ); var BlobField: TBlobField; BlobStream: TStream; GeomBytes: TBytes; Geometry: TSQLGeometry; // 假设使用SQLSpatial库的TSQLGeometry类 begin .... MyDatabaserecord.NameStr := Fieldbyname('Name').AsString; BlobField := Fieldbyname('Geom') as TBlobField; BlobStream := TBlobStream.Create(BlobField, bmRead); try SetLength(GeomBytes, BlobStream.Size); BlobStream.ReadBuffer(GeomBytes[0], BlobStream.Size); Geometry := TSQLGeometry.FromWKB(GeomBytes); MyDatabaserecord.OutLineStr := Geometry.AsText; finally Geometry.Free; BlobStream.Free; end; ... end;
关键说明
两次读取结果不同是因为ADO驱动对geometry字段的处理没有统一逻辑,强制使用STAsText()是最可靠的解决方式,避免依赖驱动的不稳定行为。
内容的提问来源于stack exchange,提问作者Franz
相关产品推荐
相关产品推荐

