SQL 中的地理位置查询结果不一致

Posted

技术标签:

【中文标题】SQL 中的地理位置查询结果不一致【英文标题】:Inconsistent geolocation query results in SQL 【发布时间】:2012-04-14 09:23:35 【问题描述】:

我编写了一个简单的网络应用程序,让您可以在谷歌地图上标记跳蚤市场的摊位。 每个展位及其地理位置和其他信息都存储在 sqlite3 数据库中。 这是 stand 表的 CREATE 语句:

CREATE TABLE stands (
id INTEGER PRIMARY_KEY,
name TEXT,
address TEXT,
u REAL,
v REAL,
);

u 和 v 分别是纬度和经度。 此外,我有一个城市表,其中存储了每个设有展台的城市的名称和地理范围。这用于让用户在城市之间快速导航。

CREATE TABLE cities
(name TEXT PRIMARY_KEY,
u_min REAL,
u_max REAL,
v_min REAL,
v_max REAL);

添加新站时,会在城市表中添加新行,或者如果站位于已知城市,则仅在需要时更新城市的边界。

这里有一些样品架:

592077673|Kierrätystori Rovaniemellä|Urheilukatu 1, 96100 Rovaniemi, Suomi|66.4978306921681|25.7220569153442
1321495145|Kruununhaka|Liisankatu, 00170 Helsinki, Suomi|60.1742596|24.9555782    
571688977|Viikki asukastalo LAVAn edusta|Biologinkatu 5, 00790 Helsinki, Suomi|60.2342312|25.04058
563089951|Hämeentie 156|Hämeentie 156, 00560 Helsinki, Suomi|60.2130467082539|24.9785856067459    
518892420|Joensuu - Ilosaari|Siltakatu 1, 80100 Joensuu, Finland|62.5990455742272|29.7706540507875

和城市:

    Rovaniemi|66.4978306921681|66.4978306921681|25.7220569153442|25.7220569153442
Helsinki|60.1577049447137|60.2556221042622|24.9216988767212|25.0662129772156
Järvenpää|60.4513724|60.4513724|25.0819323000001|25.0819323000001
Joensuu|62.5990455742272|62.5990653244875|29.7706540507874|29.7706540507875
Vantaa|60.2731724937748|60.2731724937748|24.9571491285278|24.9571491285278

我遇到的问题是检索每个城市的展位数量。 到目前为止,我一直在使用以下查询:

SELECT cities.name AS city,
  cities.u_min,
  cities.u_max,
  cities.v_min,
  cities.v_max,
  count(stands.id) AS count
FROM cities
LEFT OUTER JOIN stands
  ON ((stands.u BETWEEN cities.u_min AND cities.u_max)
    AND(stands.v BETWEEN cities.v_min AND cities.v_max))
GROUP BY cities.name;

这会返回:

Helsinki|60.1577049447137|60.2556221042622|24.9216988767212|25.0662129772156|9
**Joensuu|62.5990455742272|62.5990653244875|29.7706540507874|29.7706540507875|0**
Järvenpää|60.4513724|60.4513724|25.0819323000001|25.0819323000001|1
Rovaniemi|66.4978306921681|66.4978306921681|25.7220569153442|25.7220569153442|1
Vantaa|60.2731724937748|60.2731724937748|24.9571491285278|24.9571491285278|1

这是不正确的,因为名为 Joensuu 的城市在其边界内确实有 1 个展台:

518892420|Joensuu - Ilosaari|Siltakatu 1, 80100 Joensuu, Finland|62.5990455742272|29.7706540507875

但以下查询返回预期的立场:

SELECT * FROM stands where u between 62.5990455742272 and 62.5990653244875 and v between 29.7706540507874 and 29.7706540507875;

我真的不明白这里出了什么问题。 任何帮助将不胜感激。

顺便说一句,我将这个数据库导入到 mysql 并发生了同样的事情,所以我怀疑这是一个 sqlite3 错误。

【问题讨论】:

【参考方案1】:

我认为这与浮点精度错误有关。处理此类问题的方法之一是引入一个小数并将其添加到您的边界以使它们更宽一些 - 这消除了精度错误。

一种方法是直接扩大每个查询的边界:

SET @e = 0.0000000000001;

SELECT cities.name AS city,
  cities.u_min,
  cities.u_max,
  cities.v_min,
  cities.v_max,
  count(stands.id) AS count
FROM cities
LEFT OUTER JOIN stands
  ON ((stands.u BETWEEN cities.u_min - @e AND cities.u_max + @e)
    AND(stands.v BETWEEN cities.v_min - @e AND cities.v_max + @e))
GROUP BY cities.name;

另一种方法是将加宽的边界存储在城市表中:

SET @e = 0.0000000000001;

UPDATE cities
SET cities.u_min = cities.u_min - @e,
  cities.u_max = cities.u_max + @e,
  cities.v_min = cities.v_min - @e,
  cities.v_max = cities.v_max + @e;

附:我不确定变量语法在 SQLite 中是否有效,但如果无效,只需将所有 @e 替换为 0.0000000000001

【讨论】:

不,sqlite 不支持 SET 语法。我手动修补了数据库并修改了处理数据库事务的 php 脚本,以拓宽创建和更新的界限。【参考方案2】:

原因是你使用LEFT JOIN,所以它包括所有城市,不管stands表中是否有匹配。请注意 COUNT 如何为该行返回 0

只需使用INNER JOIN 而不是LEFT JOIN。这将告诉 MySQL 仅返回满足 ON 中的条件的行。

UPD:我误解了这个问题,所以这个答案是错误的。我会发布另一个答案。

【讨论】:

谢谢,但是 INNER JOIN 并不能解决问题。 Joensuu排的计数应该返回1不是0,因为城市边界有一个立场(拉特:62.5990455742272 - 62.599065324-62.5990653244875,长:29.7706540​​507874 - 29.7706540​​507875):** 518892420707875):**芬兰|62.5990455742272|29.7706540​​507875**\n 问题出在开启状态的某处,由于某种原因,支架没有被计算在内。

以上是关于SQL 中的地理位置查询结果不一致的主要内容,如果未能解决你的问题,请参考以下文章

SQL 查询指定字符串的位置

记一次伪*sql查询结果不一致的

SQL注入常见的姿势一:联合查询注入

Excel 执行Sql 将查询结果输出到EXCEL指定位置

SQL Server 查询。用户存在于另一个表中但不存在用户的位置

使用地理跟踪和 SQL 查询