简介
我有以下的SQLite表198305层地理编码葡萄牙语邮政编码:的SQLite - WHERE子句&的UDF
CREATE TABLE "pt_postal" (
"code" text NOT NULL,
"geo_latitude" real(9,6) NULL,
"geo_longitude" real(9,6) NULL
);
CREATE UNIQUE INDEX "pt_postal_code" ON "pt_postal" ("code");
CREATE INDEX "coordinates" ON "pt_postal" ("geo_latitude", "geo_longitude");
我也有在PHP以下用户定义的函数,返回两者之间的距离坐标:
$db->sqliteCreateFunction('geo', function()
{
if (count($data = func_get_args()) < 4)
{
$data = explode(',', implode(',', $data));
}
if (count($data = array_map('deg2rad', array_filter($data, 'is_numeric'))) == 4)
{
return round(6378.14 * acos(sin($data[0]) * sin($data[2]) + cos($data[0]) * cos($data[2]) * cos($data[1] - $data[3])), 3);
}
return null;
});
只有记录具有到1 k从38.73311, -9.138707
小于或等于一个距离米
的问题
的UDF是在SQL查询工作完美无瑕,但由于某些原因,我不能用它在WHERE
条款返回值 - 例如,如果我执行查询:
SELECT
"code",
geo(38.73311, -9.138707, "geo_latitude", "geo_longitude") AS "distance"
FROM "pt_postal" WHERE 1 = 1
AND "geo_latitude" BETWEEN 38.7241268076 AND 38.7420931924
AND "geo_longitude" BETWEEN -9.15022289523 AND -9.12719110477
AND "distance" <= 1
ORDER BY "distance" ASC
LIMIT 2048;
它返回1035条记录通过distance
在〜0.05秒有序,然而最后一条记录的“距离”为1.353
公里(大于我在最后WHERE
中定义的最大值1公里)。
如果我把以下条款:
AND "geo_latitude" BETWEEN 38.7241268076 AND 38.7420931924
AND "geo_longitude" BETWEEN -9.15022289523 AND -9.12719110477
现在查询需要近6秒,并返回2048条记录(我LIMIT
)由distance
排序。这应该需要这么长时间,但它应该只返回874条记录,其中有"distance" <= 1
。
SEARCH TABLE pt_postal USING INDEX coordinates (geo_latitude>? AND geo_latitude<?)
#(~7500 rows)
USE TEMP B-TREE FOR ORDER BY
而且没有坐标界限:
的原始查询返回EXPLAIN QUERY PLAN
SCAN TABLE pt_postal
#(~500000 rows)
USE TEMP B-TREE FOR ORDER BY
我想这样做
我想我知道为什么这发生了,SQLite正在这样做:
- 使用指数
coordinates
过滤掉的记录的边界之外的WHERE
条款 - 过滤这些记录由
"distance" <= 1
WHERE
条款,但distance
仍然NULL => 0
! - 填入“代码”和“距离”(通过调用UDF的第一次)
- 为了用“距离”(这是由现在已填充)
- 极限记录
什么我想的SQLite做:
- 使用指数
coordinates
过滤掉的记录的边界之外的WHERE
条款 - 这些记录,通过调用UDF
- 通过的“距离”过滤器由
"distance" <= 1
WHERE
子句 - 顺序记录(不再次呼叫UDF)
- 限制记录
code
和
distance
任何人都可以解释我如何使SQLite的行为(如果它甚至可能)我想要的方式?
后记
只是出于好奇,我试图基准慢多少调用UDF两次是:
SELECT
"code",
geo(38.73311, -9.138707, "geo_latitude", "geo_longitude") AS "distance"
FROM "pt_postal" WHERE 1 = 1
AND "geo_latitude" BETWEEN 38.7241268076 AND 38.7420931924
AND "geo_longitude" BETWEEN -9.15022289523 AND -9.12719110477
AND geo(38.73311, -9.138707, "geo_latitude", "geo_longitude") <= 1
ORDER BY "distance" ASC
LIMIT 2048;
令我惊讶的是,它仍然运行在相同的〜 0.06秒 - 它仍然(错误!)返回1035条记录。
看起来像第二个geo()
调用甚至没有被评估......但是it should,对吧?
请选择一个错误的记录,并检查您是否仍得到相同的结果当你直接使用它的值时:'SELECT geo(1.2,3.4,5.6,7.8);' – 2013-05-13 07:46:19
@CL。 '[地理(1.2,3.4,5.6,7.8)] => 691.995'。当我改变代码时,我注意到我通过'sprintf()'输出了一个带参数的查询,并且我正在执行另一个准备好的PDO查询。问题是,我没有将绑定参数传递给准备好的参数! :我现在很尴尬,我几个小时都在搞这个,以前我都看不清楚了。对于你浪费的时间感到抱歉,至少你会把我引向问题的根源。 – 2013-05-13 08:31:53